45 lines
1.7 KiB
Vue
45 lines
1.7 KiB
Vue
<script setup lang="ts">
|
|
import { computed, h, ref } from 'vue'
|
|
import { NButton, NDropdown } from 'naive-ui'
|
|
import { Monitor, Moon, Sun } from '@lucide/vue'
|
|
import type { ThemePreference } from '../../composables/useTheme'
|
|
|
|
/** 图标表示用户偏好;保留系统跟随选项,不以简单二态切换覆盖偏好。 */
|
|
const preference = defineModel<ThemePreference>({ required: true })
|
|
const open = ref(false)
|
|
const modes = [
|
|
{ key: 'light', label: '浅色模式', icon: Sun },
|
|
{ key: 'dark', label: '暗黑模式', icon: Moon },
|
|
{ key: 'system', label: '跟随系统', icon: Monitor }
|
|
] as const
|
|
const current = computed(() => modes.find(mode => mode.key === preference.value) ?? modes[2])
|
|
const options = modes.map(mode => ({ ...mode, icon: () => h(mode.icon, { size: 16 }) }))
|
|
|
|
/** Dropdown 的键类型较宽,只接收受支持的主题值。 */
|
|
function selectTheme(key: string | number) {
|
|
if (key === 'light' || key === 'dark' || key === 'system') preference.value = key
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<NDropdown v-model:show="open" trigger="click" :value="preference" :options="options" @select="selectTheme">
|
|
<NButton
|
|
quaternary
|
|
class="theme-toggle icon-button"
|
|
:aria-label="`主题模式:${current.label}`"
|
|
aria-haspopup="menu"
|
|
:aria-expanded="open"
|
|
>
|
|
<template #icon><component :is="current.icon" :size="18" aria-hidden="true" /></template>
|
|
</NButton>
|
|
</NDropdown>
|
|
</template>
|
|
|
|
<style>
|
|
/* 本组件专属布局与 Naive 内部结构覆盖。 */
|
|
@reference "../../styles/styles.css";
|
|
.n-button.theme-toggle {
|
|
@apply shrink-0 w-[34px] h-[34px];
|
|
}
|
|
</style>
|