38 lines
1.6 KiB
TypeScript
38 lines
1.6 KiB
TypeScript
export type Theme = 'light' | 'dark'
|
|
|
|
// ต้องตรงกับ inline script กัน FOUC ใน nuxt.config.ts
|
|
const STORAGE_KEY = 'theme-preference'
|
|
|
|
export function useTheme() {
|
|
const theme = useState<Theme>('theme', () => 'light')
|
|
const isDark = computed(() => theme.value === 'dark')
|
|
|
|
function apply(next: Theme) {
|
|
theme.value = next
|
|
// ใช้ typeof window แทน import.meta.client — เทียบเท่ากันใน Nuxt (SSR ไม่มี window)
|
|
// และรันได้ใน vitest ตรงๆ โดยไม่ต้องพึ่งการ transform เฉพาะของ Nuxt
|
|
if (typeof window !== 'undefined') {
|
|
document.documentElement.classList.toggle('dark', next === 'dark')
|
|
// ข้อยกเว้นเดียวที่ localStorage อนุญาต: theme preference ไม่ใช่ credential (กติกา #4)
|
|
localStorage.setItem(STORAGE_KEY, next)
|
|
}
|
|
}
|
|
|
|
/** Sync state กับที่ inline script ตั้งไว้ตอนโหลดหน้า (client เท่านั้น) */
|
|
function initTheme() {
|
|
if (typeof window === 'undefined') return
|
|
const stored = localStorage.getItem(STORAGE_KEY)
|
|
if (stored === 'light' || stored === 'dark') {
|
|
theme.value = stored
|
|
} else {
|
|
theme.value = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
|
}
|
|
document.documentElement.classList.toggle('dark', theme.value === 'dark')
|
|
}
|
|
|
|
function toggle() {
|
|
apply(theme.value === 'dark' ? 'light' : 'dark')
|
|
}
|
|
|
|
return { theme, isDark, initTheme, toggle }
|
|
}
|