forked from patternfly/patternfly-org
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathuseTheme.js
More file actions
137 lines (118 loc) · 3.95 KB
/
Copy pathuseTheme.js
File metadata and controls
137 lines (118 loc) · 3.95 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
135
136
137
import { useState, useEffect, useCallback } from 'react';
const THEME_MODES = {
SYSTEM: 'system',
LIGHT: 'light',
DARK: 'dark'
};
const THEME_STORAGE_KEY = 'theme-preference';
const DARK_MODE_CLASS = 'pf-v6-theme-dark';
/**
* Custom hook for managing theme state with system preference detection
* @returns {Object} Theme state and controls
* @returns {string} themeMode - Current theme mode ('system'|'light'|'dark')
* @returns {Function} setThemeMode - Function to change theme mode
* @returns {string} resolvedTheme - The actual applied theme ('light'|'dark')
* @returns {Object} THEME_MODES - Available theme mode constants
*/
export const useTheme = () => {
const getStoredThemeMode = () => {
if (typeof window === 'undefined' || !window.localStorage) {
return null;
}
return localStorage.getItem(THEME_STORAGE_KEY);
};
const setStoredThemeMode = (mode) => {
if (typeof window === 'undefined' || !window.localStorage) {
return;
}
localStorage.setItem(THEME_STORAGE_KEY, mode);
};
const getResolvedTheme = (mode) => {
// SSR-safe check for window and matchMedia
if (typeof window === 'undefined' || !window.matchMedia) {
return 'light';
}
if (mode === THEME_MODES.SYSTEM) {
try {
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
} catch (error) {
// Fallback if matchMedia fails
console.warn('matchMedia not supported, defaulting to light theme');
return 'light';
}
}
return mode;
};
const updateThemeClass = (resolvedTheme) => {
if (typeof window === 'undefined' || !document) {
return;
}
const htmlElement = document.querySelector('html');
if (!htmlElement) {
return;
}
if (resolvedTheme === 'dark') {
htmlElement.classList.add(DARK_MODE_CLASS);
} else {
htmlElement.classList.remove(DARK_MODE_CLASS);
}
};
const [themeMode, setThemeModeState] = useState(() => {
const stored = getStoredThemeMode();
return stored && Object.values(THEME_MODES).includes(stored) ? stored : THEME_MODES.SYSTEM;
});
const [resolvedTheme, setResolvedTheme] = useState(() => getResolvedTheme(themeMode));
const setThemeMode = useCallback((newMode) => {
setThemeModeState(newMode);
setStoredThemeMode(newMode);
const newResolvedTheme = getResolvedTheme(newMode);
setResolvedTheme(newResolvedTheme);
updateThemeClass(newResolvedTheme);
}, []);
// Listen for system preference changes
useEffect(() => {
// Enhanced SSR-safe check
if (typeof window === 'undefined' || !window.matchMedia) {
return;
}
let mediaQuery;
try {
mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
} catch (error) {
console.warn('matchMedia not supported, skipping system theme detection');
return;
}
const handleSystemThemeChange = (e) => {
if (themeMode === THEME_MODES.SYSTEM) {
const newSystemTheme = e.matches ? 'dark' : 'light';
setResolvedTheme(newSystemTheme);
updateThemeClass(newSystemTheme);
}
};
// Check if addEventListener is available (some older browsers might not support it)
if (mediaQuery.addEventListener) {
mediaQuery.addEventListener('change', handleSystemThemeChange);
return () => {
mediaQuery.removeEventListener('change', handleSystemThemeChange);
};
} else if (mediaQuery.addListener) {
// Fallback for older browsers
mediaQuery.addListener(handleSystemThemeChange);
return () => {
mediaQuery.removeListener(handleSystemThemeChange);
};
}
}, [themeMode]);
// Initial theme application
useEffect(() => {
const initialResolvedTheme = getResolvedTheme(themeMode);
setResolvedTheme(initialResolvedTheme);
updateThemeClass(initialResolvedTheme);
}, [themeMode]);
return {
themeMode,
setThemeMode,
resolvedTheme,
THEME_MODES
};
};