-
Notifications
You must be signed in to change notification settings - Fork 61
Expand file tree
/
Copy paththeme.js
More file actions
134 lines (107 loc) · 3.77 KB
/
Copy paththeme.js
File metadata and controls
134 lines (107 loc) · 3.77 KB
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
const THEME_STORAGE_KEY = 'appSettings'
const THEME_MEDIA_QUERY = '(prefers-color-scheme: dark)'
export const THEME_MODE = {
LIGHT: 'light',
DARK: 'dark',
SYSTEM: 'system'
}
export const themeOptions = [
{ value: THEME_MODE.LIGHT, label: '浅色' },
{ value: THEME_MODE.DARK, label: '深色' },
{ value: THEME_MODE.SYSTEM, label: '跟随系统' }
]
const validThemeModes = new Set(themeOptions.map((option) => option.value))
let mediaQueryList = null
let currentMode = THEME_MODE.LIGHT
const isBrowser = () => typeof window !== 'undefined' && typeof document !== 'undefined'
export const normalizeThemeMode = (mode) => (
validThemeModes.has(mode) ? mode : THEME_MODE.LIGHT
)
export const getStoredThemeMode = () => {
if (!isBrowser()) return THEME_MODE.LIGHT
try {
const appSettings = JSON.parse(localStorage.getItem(THEME_STORAGE_KEY) || '{}')
return normalizeThemeMode(appSettings.themeMode)
} catch (error) {
console.error('读取主题设置失败:', error)
return THEME_MODE.LIGHT
}
}
export const resolveThemeMode = (mode = getStoredThemeMode()) => {
const normalizedMode = normalizeThemeMode(mode)
if (normalizedMode !== THEME_MODE.SYSTEM || !isBrowser()) {
return normalizedMode
}
return window.matchMedia?.(THEME_MEDIA_QUERY).matches ? THEME_MODE.DARK : THEME_MODE.LIGHT
}
export const applyTheme = (mode = getStoredThemeMode()) => {
if (!isBrowser()) return THEME_MODE.LIGHT
currentMode = normalizeThemeMode(mode)
const resolvedTheme = resolveThemeMode(currentMode)
const isDark = resolvedTheme === THEME_MODE.DARK
if (isDark) {
document.body.setAttribute('arco-theme', 'dark')
} else {
document.body.removeAttribute('arco-theme')
}
document.documentElement.dataset.theme = resolvedTheme
document.body.dataset.theme = resolvedTheme
document.documentElement.dataset.themeMode = currentMode
document.body.dataset.themeMode = currentMode
document.documentElement.style.colorScheme = resolvedTheme
window.dispatchEvent(new CustomEvent('themeChanged', {
detail: {
mode: currentMode,
resolvedTheme
}
}))
return resolvedTheme
}
export const setStoredThemeMode = (mode) => {
if (!isBrowser()) return THEME_MODE.LIGHT
const themeMode = normalizeThemeMode(mode)
let appSettings = {}
try {
appSettings = JSON.parse(localStorage.getItem(THEME_STORAGE_KEY) || '{}')
} catch (error) {
console.error('读取主题设置失败:', error)
}
appSettings.themeMode = themeMode
localStorage.setItem(THEME_STORAGE_KEY, JSON.stringify(appSettings))
applyTheme(themeMode)
return themeMode
}
const handleSystemThemeChange = () => {
if (currentMode === THEME_MODE.SYSTEM) {
applyTheme(currentMode)
}
}
const handleStorageChange = (event) => {
if (event.key !== THEME_STORAGE_KEY) return
applyTheme(getStoredThemeMode())
}
const refreshSystemTheme = () => {
if (currentMode === THEME_MODE.SYSTEM) {
applyTheme(currentMode)
}
}
export const initTheme = () => {
if (!isBrowser()) return THEME_MODE.LIGHT
currentMode = getStoredThemeMode()
applyTheme(currentMode)
if (!mediaQueryList && window.matchMedia) {
mediaQueryList = window.matchMedia(THEME_MEDIA_QUERY)
if (mediaQueryList.addEventListener) {
mediaQueryList.addEventListener('change', handleSystemThemeChange)
} else if (mediaQueryList.addListener) {
mediaQueryList.addListener(handleSystemThemeChange)
}
}
window.removeEventListener('storage', handleStorageChange)
window.addEventListener('storage', handleStorageChange)
window.removeEventListener('focus', refreshSystemTheme)
window.addEventListener('focus', refreshSystemTheme)
document.removeEventListener('visibilitychange', refreshSystemTheme)
document.addEventListener('visibilitychange', refreshSystemTheme)
return currentMode
}