Files
veridian/app/components/ThemeSwitcher.vue
T
2026-01-11 05:04:29 -06:00

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>