52 lines
2.1 KiB
Vue
52 lines
2.1 KiB
Vue
<script setup lang="ts">
|
|
type Theme = 'light' | 'dark' | 'system'
|
|
|
|
const colorMode = useColorMode()
|
|
const isOpen = ref(false)
|
|
const buttonRef = ref<HTMLElement | null>(null)
|
|
|
|
const themeOptions: { value: Theme; label: string; icon: string }[] = [
|
|
{ value: 'light', label: 'Light', icon: 'mynaui:sun' },
|
|
{ value: 'dark', label: 'Dark', icon: 'mynaui:moon' },
|
|
{ value: 'system', label: 'System', icon: 'mynaui:desktop' }
|
|
]
|
|
|
|
const currentOption = computed(() =>
|
|
themeOptions.find(option => option.value === colorMode.preference) || themeOptions[2]
|
|
)
|
|
|
|
const selectTheme = (newTheme: Theme) => {
|
|
colorMode.preference = newTheme
|
|
isOpen.value = false
|
|
}
|
|
|
|
const toggleDropdown = () => {
|
|
isOpen.value = !isOpen.value
|
|
}
|
|
|
|
useClickOutside(buttonRef, () => {
|
|
isOpen.value = false
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div ref="buttonRef" class="relative">
|
|
<button aria-label="Open theme switcher" @click="toggleDropdown"
|
|
class="p-2 flex items-center justify-center rounded-lg hover:bg-[var(--color-highlight)] transition-colors group"
|
|
:class="isOpen ? 'bg-[var(--color-highlight)]' : 'bg-transparent'">
|
|
<Icon :name="currentOption!.icon"
|
|
class="text-5 text-[var(--color-subtle)] group-hover:text-[var(--color-text)] transition-colors" />
|
|
</button>
|
|
|
|
<div v-if="isOpen"
|
|
class="flex flex-col gap-1 absolute bottom-full mb-2 right-0 bg-[var(--color-neutral)] border border-[var(--color-highlight)] rounded-lg p-1 min-w-[120px] shadow-lg">
|
|
<button v-for="option in themeOptions" :key="option.value" @click="selectTheme(option.value)"
|
|
class="w-full flex items-center justify-left gap-2 px-3 py-2 rounded-md hover:bg-[var(--color-highlight)] transition-colors"
|
|
:class="colorMode.preference === option.value ? 'bg-[var(--color-highlight)] text-[var(--color-text)]' :
|
|
'bg-transparent text-[var(--color-subtle)]'">
|
|
<Icon :name="option.icon" class="w-4 h-4" />
|
|
<span class="text-sm">{{ option.label }}</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</template> |