mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-04 00:17:45 +08:00
refactor(frontend): extract pool header and theme toggle
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<button
|
||||
type="button"
|
||||
:class="buttonClass"
|
||||
:title="title"
|
||||
:aria-label="title"
|
||||
@click="toggleDarkMode"
|
||||
>
|
||||
<SunMoon
|
||||
v-if="themeMode === 'system'"
|
||||
:class="iconClass"
|
||||
/>
|
||||
<SunMedium
|
||||
v-else-if="themeMode === 'light'"
|
||||
:class="iconClass"
|
||||
/>
|
||||
<Moon
|
||||
v-else
|
||||
:class="iconClass"
|
||||
/>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { Moon, SunMedium, SunMoon } from 'lucide-vue-next'
|
||||
import { useDarkMode } from '@/composables/useDarkMode'
|
||||
import { useI18n } from '@/i18n'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
size?: 'sm' | 'md'
|
||||
class?: string
|
||||
}>(), {
|
||||
size: 'md',
|
||||
class: '',
|
||||
})
|
||||
|
||||
const { themeMode, toggleDarkMode } = useDarkMode()
|
||||
const { t } = useI18n()
|
||||
|
||||
const title = computed(() => {
|
||||
if (themeMode.value === 'system') return t('theme.system')
|
||||
if (themeMode.value === 'dark') return t('theme.dark')
|
||||
return t('theme.light')
|
||||
})
|
||||
|
||||
const buttonClass = computed(() => [
|
||||
'flex items-center justify-center rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted/50 transition',
|
||||
props.size === 'sm' ? 'h-8 w-8' : 'h-9 w-9',
|
||||
props.class,
|
||||
].filter(Boolean).join(' '))
|
||||
|
||||
const iconClass = computed(() => props.size === 'sm' ? 'h-3.5 w-3.5' : 'h-4 w-4')
|
||||
</script>
|
||||
@@ -0,0 +1,39 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { createApp, nextTick } from 'vue'
|
||||
|
||||
import ThemeModeButton from '@/components/common/ThemeModeButton.vue'
|
||||
import { useDarkMode } from '@/composables/useDarkMode'
|
||||
import { createI18n } from '@/i18n'
|
||||
|
||||
describe('ThemeModeButton', () => {
|
||||
it('cycles the shared theme mode from the single button entry point', async () => {
|
||||
const { setThemeMode } = useDarkMode()
|
||||
setThemeMode('system')
|
||||
await nextTick()
|
||||
|
||||
const root = document.createElement('div')
|
||||
document.body.appendChild(root)
|
||||
const app = createApp(ThemeModeButton)
|
||||
app.use(createI18n())
|
||||
app.mount(root)
|
||||
|
||||
const button = root.querySelector('button')
|
||||
expect(button).toBeTruthy()
|
||||
expect(button?.getAttribute('title')).toBe('跟随系统')
|
||||
|
||||
button?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
||||
await nextTick()
|
||||
|
||||
expect(button?.getAttribute('title')).toBe('浅色模式')
|
||||
expect(localStorage.getItem('theme')).toBe('light')
|
||||
|
||||
button?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
||||
await nextTick()
|
||||
|
||||
expect(button?.getAttribute('title')).toBe('深色模式')
|
||||
expect(localStorage.getItem('theme')).toBe('dark')
|
||||
|
||||
app.unmount()
|
||||
root.remove()
|
||||
})
|
||||
})
|
||||
@@ -8,6 +8,7 @@ export { default as EmptyState } from './EmptyState.vue'
|
||||
export { default as AlertDialog } from './AlertDialog.vue'
|
||||
export { default as LoadingState } from './LoadingState.vue'
|
||||
export { default as StripePaymentDialog } from './StripePaymentDialog.vue'
|
||||
export { default as ThemeModeButton } from './ThemeModeButton.vue'
|
||||
|
||||
// 表单组件
|
||||
export { default as MultiSelect } from './MultiSelect.vue'
|
||||
|
||||
Reference in New Issue
Block a user