-
Notifications
You must be signed in to change notification settings - Fork 74
Expand file tree
/
Copy pathuseColorMode.ts
More file actions
119 lines (99 loc) · 3.72 KB
/
useColorMode.ts
File metadata and controls
119 lines (99 loc) · 3.72 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
import { useEffect, useState } from 'react';
const CSS_DISABLE_TRANS = `
*, *::before, *::after {
-webkit-transition: none !important;
-moz-transition: none !important;
-o-transition: none !important;
-ms-transition: none !important;
transition: none !important;
}
`;
export type BasicColorMode = 'auto' | 'dark' | 'light';
/** The use color mode options */
export interface UseColorModeOptions<MODE extends string = BasicColorMode> {
/** HTML attribute applying the target element */
attribute?: string;
/** Disable transition on switch */
disableTransition?: boolean;
/** The initial color mode */
initialValue?: BasicColorMode | MODE;
/** Prefix when adding value to the attribute */
modes?: Record<BasicColorMode | MODE, string>;
/** CSS Selector for the target element applying to */
selector?: string;
/** Storage object, can be localStorage or sessionStorage */
storage?: 'localStorage' | 'sessionStorage';
/** Key to persist the data into localStorage/sessionStorage. Pass `null` to disable persistence */
storageKey?: string | null;
/**A custom handler for handle the updates. When specified, the default behavior will be overridden */
onChanged?: (
mode: BasicColorMode | MODE,
defaultHandler: (mode: BasicColorMode | MODE) => void
) => void;
}
/** The use color mode return type */
export interface UseColorModeReturn<MODE extends string = BasicColorMode> {
/** The value of the auto mode */
auto: BasicColorMode;
/** The current color mode value */
value: BasicColorMode | MODE;
/** Function to set the color mode */
set: (mode: BasicColorMode | MODE) => void;
}
/**
* @name useColorMode
* @description - Hook for get and set color mode (dark / light / customs) with auto data persistence.
* @category Browser
*
* @param {UseColorModeOptions} options The options for configuring color mode hook.
* @returns {UseColorModeReturn} The object containing the current color mode and a function to set the color mode.
*/
export const useColorMode = <MODE extends string = BasicColorMode>(
options?: UseColorModeOptions<MODE>
) => {
const {
selector = 'html',
attribute = 'class',
disableTransition = true,
initialValue = 'auto',
storageKey = 'reactuse-color-scheme',
modes = {},
storage: _storage = 'localStorage',
onChanged
} = options ?? {};
const storage = _storage === 'sessionStorage' ? sessionStorage : localStorage;
const [value, setValue] = useState(
storageKey
? (storage.getItem(storageKey) as BasicColorMode | MODE | null) || initialValue
: initialValue
);
const updateHTMLAttrs = (mode: string) => {
const element = document.querySelector(selector);
if (!element) return;
const modeClasses = [...Object.values(modes), 'auto', 'dark', 'light'] as (
| BasicColorMode
| MODE
)[];
if (attribute === 'class') {
element.classList.remove(...modeClasses);
element.classList.add(mode);
} else {
element.setAttribute(attribute, mode);
}
if (disableTransition) {
const style = document.createElement('style');
style.textContent = CSS_DISABLE_TRANS;
document.head.appendChild(style);
(() => getComputedStyle(style).opacity)();
document.head.removeChild(style);
}
};
const auto = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
useEffect(() => {
const mode = value !== 'auto' ? value : auto;
if (storageKey) storage.setItem(storageKey, value);
const defaultOnChanged = (mode: BasicColorMode | MODE) => updateHTMLAttrs(mode);
onChanged ? onChanged(mode, defaultOnChanged) : defaultOnChanged(mode);
}, [value, storage, storageKey, onChanged]);
return { value, auto, set: setValue };
};