Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | 5x 5x 5x 5x 5x 5x 1x 2x 1x 1x 7x 2x 5x 7x 7x 7x 7x 4x 4x 1x 3x 2x 2x 1x 1x 1x 1x 1x 1x 4x 4x 4x 101x | import { readonly, ref } from 'vue'
export type ThemePreference = 'light' | 'dark' | 'system'
type ResolvedTheme = 'light' | 'dark'
const THEME_STORAGE_KEY = 'uiThemePreference'
const themePreference = ref<ThemePreference>('system')
const resolvedTheme = ref<ResolvedTheme>('light')
let initialized = false
let mediaQueryList: MediaQueryList | null = null
let mediaQueryListenerBound = false
function isValidThemePreference(value: string | null): value is ThemePreference {
return value === 'light' || value === 'dark' || value === 'system'
}
function getSystemTheme(): ResolvedTheme {
if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {
return 'light'
}
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}
function computeResolvedTheme(preference: ThemePreference): ResolvedTheme {
if (preference === 'system') {
return getSystemTheme()
}
return preference
}
function applyTheme(preference: ThemePreference = themePreference.value): void {
Iif (typeof document === 'undefined') return
const nextResolvedTheme = computeResolvedTheme(preference)
resolvedTheme.value = nextResolvedTheme
document.documentElement.setAttribute('data-bs-theme', nextResolvedTheme)
}
function persistPreference(preference: ThemePreference): void {
Iif (typeof window === 'undefined') return
window.localStorage.setItem(THEME_STORAGE_KEY, preference)
}
function handleSystemThemeChange(): void {
if (themePreference.value === 'system') {
applyTheme('system')
}
}
function bindSystemThemeListener(): void {
Eif (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return
if (mediaQueryListenerBound) return
mediaQueryList = window.matchMedia('(prefers-color-scheme: dark)')
if (typeof mediaQueryList.addEventListener === 'function') {
mediaQueryList.addEventListener('change', handleSystemThemeChange)
} else if (typeof mediaQueryList.addListener === 'function') {
mediaQueryList.addListener(handleSystemThemeChange)
}
mediaQueryListenerBound = true
}
export function initTheme(): void {
if (initialized) {
applyTheme(themePreference.value)
return
}
Eif (typeof window !== 'undefined') {
const storedPreference = window.localStorage.getItem(THEME_STORAGE_KEY)
Iif (isValidThemePreference(storedPreference)) {
themePreference.value = storedPreference
}
}
bindSystemThemeListener()
applyTheme(themePreference.value)
initialized = true
}
export function useTheme() {
function setThemePreference(preference: ThemePreference): void {
themePreference.value = preference
persistPreference(preference)
applyTheme(preference)
}
return {
resolvedTheme: readonly(resolvedTheme),
setThemePreference,
themePreference: readonly(themePreference)
}
}
|