@pshah-lab/themeswitcher 0.1.0 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +93 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +21 -40
- package/dist/index.mjs +81 -200
- package/dist/index.mjs.map +1 -1
- package/dist/react.cjs +57 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.ts +18 -0
- package/dist/react.mjs +36 -0
- package/dist/react.mjs.map +1 -0
- package/package.json +53 -44
- package/README.md +0 -63
- package/dist/index.d.mts +0 -44
- package/dist/index.js +0 -234
- package/dist/index.js.map +0 -1
- package/scripts/no-flash-theme.js +0 -22
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const isBrowser = typeof window !== "undefined" &&
|
|
4
|
+
typeof document !== "undefined";
|
|
5
|
+
class ThemeToggle {
|
|
6
|
+
constructor(config = {}) {
|
|
7
|
+
var _a, _b, _c;
|
|
8
|
+
this.handleSystemChange = () => {
|
|
9
|
+
if (this.currentTheme === "auto") {
|
|
10
|
+
this.applyTheme("auto");
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
this.storageKey = (_a = config.storageKey) !== null && _a !== void 0 ? _a : "app-theme";
|
|
14
|
+
this.useSystemPreference = (_b = config.useSystemPreference) !== null && _b !== void 0 ? _b : true;
|
|
15
|
+
this.onChange = config.onChange;
|
|
16
|
+
const stored = this.getStoredTheme();
|
|
17
|
+
this.currentTheme = (_c = stored !== null && stored !== void 0 ? stored : config.defaultTheme) !== null && _c !== void 0 ? _c : "auto";
|
|
18
|
+
this.init();
|
|
19
|
+
}
|
|
20
|
+
init() {
|
|
21
|
+
if (!isBrowser)
|
|
22
|
+
return;
|
|
23
|
+
if (this.useSystemPreference) {
|
|
24
|
+
this.mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
25
|
+
this.mediaQuery.addEventListener("change", this.handleSystemChange);
|
|
26
|
+
}
|
|
27
|
+
this.applyTheme(this.currentTheme);
|
|
28
|
+
}
|
|
29
|
+
getStoredTheme() {
|
|
30
|
+
if (!isBrowser)
|
|
31
|
+
return null;
|
|
32
|
+
try {
|
|
33
|
+
const stored = localStorage.getItem(this.storageKey);
|
|
34
|
+
return stored === "light" || stored === "dark" || stored === "auto"
|
|
35
|
+
? stored
|
|
36
|
+
: null;
|
|
37
|
+
}
|
|
38
|
+
catch (_a) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
setStoredTheme(theme) {
|
|
43
|
+
if (!isBrowser)
|
|
44
|
+
return;
|
|
45
|
+
try {
|
|
46
|
+
localStorage.setItem(this.storageKey, theme);
|
|
47
|
+
}
|
|
48
|
+
catch (_a) {
|
|
49
|
+
/* noop */
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
getEffectiveTheme() {
|
|
53
|
+
var _a;
|
|
54
|
+
if (this.currentTheme === "auto") {
|
|
55
|
+
return ((_a = this.mediaQuery) === null || _a === void 0 ? void 0 : _a.matches) ? "dark" : "light";
|
|
56
|
+
}
|
|
57
|
+
return this.currentTheme;
|
|
58
|
+
}
|
|
59
|
+
applyTheme(theme) {
|
|
60
|
+
var _a;
|
|
61
|
+
if (!isBrowser)
|
|
62
|
+
return;
|
|
63
|
+
const effective = this.getEffectiveTheme();
|
|
64
|
+
document.documentElement.setAttribute("data-theme", effective);
|
|
65
|
+
document.documentElement.classList.remove("light", "dark");
|
|
66
|
+
document.documentElement.classList.add(effective);
|
|
67
|
+
(_a = this.onChange) === null || _a === void 0 ? void 0 : _a.call(this, theme, effective);
|
|
68
|
+
}
|
|
69
|
+
setTheme(theme) {
|
|
70
|
+
this.currentTheme = theme;
|
|
71
|
+
this.setStoredTheme(theme);
|
|
72
|
+
this.applyTheme(theme);
|
|
73
|
+
}
|
|
74
|
+
toggle() {
|
|
75
|
+
const next = this.getEffectiveTheme() === "dark" ? "light" : "dark";
|
|
76
|
+
this.setTheme(next);
|
|
77
|
+
}
|
|
78
|
+
getTheme() {
|
|
79
|
+
return this.currentTheme;
|
|
80
|
+
}
|
|
81
|
+
destroy() {
|
|
82
|
+
if (this.mediaQuery) {
|
|
83
|
+
this.mediaQuery.removeEventListener("change", this.handleSystemChange);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function createThemeToggle(config) {
|
|
88
|
+
return new ThemeToggle(config);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
exports.ThemeToggle = ThemeToggle;
|
|
92
|
+
exports.createThemeToggle = createThemeToggle;
|
|
93
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/index.ts"],"sourcesContent":["export type Theme = \"light\" | \"dark\" | \"auto\";\n\nexport interface ThemeConfig {\n storageKey?: string;\n defaultTheme?: Theme;\n useSystemPreference?: boolean;\n onChange?: (theme: Theme, effective: \"light\" | \"dark\") => void;\n}\n\nconst isBrowser =\n typeof window !== \"undefined\" &&\n typeof document !== \"undefined\";\n\nexport class ThemeToggle {\n private storageKey: string;\n private currentTheme: Theme;\n private useSystemPreference: boolean;\n private onChange?: ThemeConfig[\"onChange\"];\n private mediaQuery?: MediaQueryList;\n\n private handleSystemChange = () => {\n if (this.currentTheme === \"auto\") {\n this.applyTheme(\"auto\");\n }\n };\n\n constructor(config: ThemeConfig = {}) {\n this.storageKey = config.storageKey ?? \"app-theme\";\n this.useSystemPreference = config.useSystemPreference ?? true;\n this.onChange = config.onChange;\n\n const stored = this.getStoredTheme();\n this.currentTheme = stored ?? config.defaultTheme ?? \"auto\";\n\n this.init();\n }\n\n private init(): void {\n if (!isBrowser) return;\n\n if (this.useSystemPreference) {\n this.mediaQuery = window.matchMedia(\"(prefers-color-scheme: dark)\");\n this.mediaQuery.addEventListener(\"change\", this.handleSystemChange);\n }\n\n this.applyTheme(this.currentTheme);\n }\n\n private getStoredTheme(): Theme | null {\n if (!isBrowser) return null;\n\n try {\n const stored = localStorage.getItem(this.storageKey);\n return stored === \"light\" || stored === \"dark\" || stored === \"auto\"\n ? stored\n : null;\n } catch {\n return null;\n }\n }\n\n private setStoredTheme(theme: Theme): void {\n if (!isBrowser) return;\n\n try {\n localStorage.setItem(this.storageKey, theme);\n } catch {\n /* noop */\n }\n }\n\n public getEffectiveTheme(): \"light\" | \"dark\" {\n if (this.currentTheme === \"auto\") {\n return this.mediaQuery?.matches ? \"dark\" : \"light\";\n }\n return this.currentTheme;\n }\n\n private applyTheme(theme: Theme): void {\n if (!isBrowser) return;\n\n const effective = this.getEffectiveTheme();\n\n document.documentElement.setAttribute(\"data-theme\", effective);\n document.documentElement.classList.remove(\"light\", \"dark\");\n document.documentElement.classList.add(effective);\n\n this.onChange?.(theme, effective);\n }\n\n public setTheme(theme: Theme): void {\n this.currentTheme = theme;\n this.setStoredTheme(theme);\n this.applyTheme(theme);\n }\n\n public toggle(): void {\n const next =\n this.getEffectiveTheme() === \"dark\" ? \"light\" : \"dark\";\n this.setTheme(next);\n }\n\n public getTheme(): Theme {\n return this.currentTheme;\n }\n\n public destroy(): void {\n if (this.mediaQuery) {\n this.mediaQuery.removeEventListener(\n \"change\",\n this.handleSystemChange\n );\n }\n }\n}\n\nexport function createThemeToggle(\n config?: ThemeConfig\n): ThemeToggle {\n return new ThemeToggle(config);\n}"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,44 +1,25 @@
|
|
|
1
|
-
type Theme = "light" | "dark";
|
|
2
|
-
|
|
3
|
-
interface
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
attribute?: string;
|
|
9
|
-
/**
|
|
10
|
-
* Storage key for persistence
|
|
11
|
-
*/
|
|
12
|
-
storageKey?: string;
|
|
13
|
-
/**
|
|
14
|
-
* Default mode when nothing is stored
|
|
15
|
-
*/
|
|
16
|
-
defaultMode?: ThemeMode;
|
|
17
|
-
}
|
|
18
|
-
type ThemeSubscriber = (theme: Theme, mode: ThemeMode) => void;
|
|
19
|
-
interface ThemeManager {
|
|
20
|
-
get(): Theme;
|
|
21
|
-
getMode(): ThemeMode;
|
|
22
|
-
set(mode: ThemeMode): void;
|
|
23
|
-
toggle(): void;
|
|
24
|
-
subscribe(fn: ThemeSubscriber): () => void;
|
|
1
|
+
type Theme = "light" | "dark" | "auto";
|
|
2
|
+
|
|
3
|
+
interface ThemeConfig {
|
|
4
|
+
storageKey?: string;
|
|
5
|
+
defaultTheme?: Theme;
|
|
6
|
+
useSystemPreference?: boolean;
|
|
7
|
+
onChange?: (theme: Theme, effective: "light" | "dark") => void;
|
|
25
8
|
}
|
|
26
9
|
|
|
27
|
-
declare
|
|
10
|
+
declare class ThemeToggle {
|
|
11
|
+
constructor(config?: ThemeConfig);
|
|
12
|
+
|
|
13
|
+
setTheme(theme: Theme): void;
|
|
14
|
+
toggle(): void;
|
|
15
|
+
getTheme(): Theme;
|
|
16
|
+
getEffectiveTheme(): "light" | "dark";
|
|
17
|
+
destroy(): void;
|
|
18
|
+
}
|
|
28
19
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
setTheme: (mode: ThemeMode) => void;
|
|
33
|
-
toggleTheme: () => void;
|
|
34
|
-
};
|
|
35
|
-
/**
|
|
36
|
-
* Create a React hook bound to a ThemeManager instance.
|
|
37
|
-
*
|
|
38
|
-
* IMPORTANT:
|
|
39
|
-
* - Call this once (e.g. in a module or provider)
|
|
40
|
-
* - Do NOT call inside components repeatedly
|
|
41
|
-
*/
|
|
42
|
-
declare function createUseTheme(options?: ThemeManagerOptions): () => UseThemeResult;
|
|
20
|
+
declare function createThemeToggle(
|
|
21
|
+
config?: ThemeConfig
|
|
22
|
+
): ThemeToggle;
|
|
43
23
|
|
|
44
|
-
export {
|
|
24
|
+
export { ThemeToggle, createThemeToggle };
|
|
25
|
+
export type { Theme, ThemeConfig };
|
package/dist/index.mjs
CHANGED
|
@@ -1,209 +1,90 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
const isBrowser = typeof window !== "undefined" &&
|
|
2
|
+
typeof document !== "undefined";
|
|
3
|
+
class ThemeToggle {
|
|
4
|
+
constructor(config = {}) {
|
|
5
|
+
var _a, _b, _c;
|
|
6
|
+
this.handleSystemChange = () => {
|
|
7
|
+
if (this.currentTheme === "auto") {
|
|
8
|
+
this.applyTheme("auto");
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
this.storageKey = (_a = config.storageKey) !== null && _a !== void 0 ? _a : "app-theme";
|
|
12
|
+
this.useSystemPreference = (_b = config.useSystemPreference) !== null && _b !== void 0 ? _b : true;
|
|
13
|
+
this.onChange = config.onChange;
|
|
14
|
+
const stored = this.getStoredTheme();
|
|
15
|
+
this.currentTheme = (_c = stored !== null && stored !== void 0 ? stored : config.defaultTheme) !== null && _c !== void 0 ? _c : "auto";
|
|
16
|
+
this.init();
|
|
14
17
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
try {
|
|
24
|
-
const testKey = "__theme_test__";
|
|
25
|
-
window.localStorage.setItem(testKey, testKey);
|
|
26
|
-
window.localStorage.removeItem(testKey);
|
|
27
|
-
return localStorageAdapter(key);
|
|
28
|
-
} catch (e) {
|
|
29
|
-
return memoryStorage();
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
function localStorageAdapter(key) {
|
|
33
|
-
return {
|
|
34
|
-
get() {
|
|
35
|
-
const value = window.localStorage.getItem(key);
|
|
36
|
-
if (value === "light" || value === "dark" || value === "system") {
|
|
37
|
-
return value;
|
|
38
|
-
}
|
|
39
|
-
if (value !== null) {
|
|
40
|
-
window.localStorage.removeItem(key);
|
|
41
|
-
}
|
|
42
|
-
return null;
|
|
43
|
-
},
|
|
44
|
-
set(value) {
|
|
45
|
-
window.localStorage.setItem(key, value);
|
|
46
|
-
},
|
|
47
|
-
clear() {
|
|
48
|
-
window.localStorage.removeItem(key);
|
|
18
|
+
init() {
|
|
19
|
+
if (!isBrowser)
|
|
20
|
+
return;
|
|
21
|
+
if (this.useSystemPreference) {
|
|
22
|
+
this.mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
23
|
+
this.mediaQuery.addEventListener("change", this.handleSystemChange);
|
|
24
|
+
}
|
|
25
|
+
this.applyTheme(this.currentTheme);
|
|
49
26
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
value = null;
|
|
27
|
+
getStoredTheme() {
|
|
28
|
+
if (!isBrowser)
|
|
29
|
+
return null;
|
|
30
|
+
try {
|
|
31
|
+
const stored = localStorage.getItem(this.storageKey);
|
|
32
|
+
return stored === "light" || stored === "dark" || stored === "auto"
|
|
33
|
+
? stored
|
|
34
|
+
: null;
|
|
35
|
+
}
|
|
36
|
+
catch (_a) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
63
39
|
}
|
|
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
|
-
media.addListener(handler);
|
|
92
|
-
return () => media.removeListener(handler);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// src/dom.ts
|
|
96
|
-
function getRoot() {
|
|
97
|
-
if (typeof document === "undefined") {
|
|
98
|
-
return null;
|
|
99
|
-
}
|
|
100
|
-
return document.documentElement;
|
|
101
|
-
}
|
|
102
|
-
function applyTheme(theme, attribute) {
|
|
103
|
-
const root = getRoot();
|
|
104
|
-
if (!root) return;
|
|
105
|
-
const current = root.getAttribute(attribute);
|
|
106
|
-
if (current === theme) return;
|
|
107
|
-
root.setAttribute(attribute, theme);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// src/manager.ts
|
|
111
|
-
var DEFAULT_OPTIONS = {
|
|
112
|
-
attribute: "data-theme",
|
|
113
|
-
storageKey: "theme-mode",
|
|
114
|
-
defaultMode: "system"
|
|
115
|
-
};
|
|
116
|
-
function createThemeManager(options = {}) {
|
|
117
|
-
var _a;
|
|
118
|
-
const config = __spreadValues(__spreadValues({}, DEFAULT_OPTIONS), options);
|
|
119
|
-
const storage = createPersistence(config.storageKey);
|
|
120
|
-
const subscribers = /* @__PURE__ */ new Set();
|
|
121
|
-
let mode = (_a = storage.get()) != null ? _a : config.defaultMode;
|
|
122
|
-
let theme = resolveTheme(mode);
|
|
123
|
-
applyTheme(theme, config.attribute);
|
|
124
|
-
let unsubscribeSystem = null;
|
|
125
|
-
if (mode === "system") {
|
|
126
|
-
unsubscribeSystem = subscribeSystemTheme(handleSystemChange);
|
|
127
|
-
}
|
|
128
|
-
function resolveTheme(m) {
|
|
129
|
-
return m === "system" ? getSystemTheme() : m;
|
|
130
|
-
}
|
|
131
|
-
let notifying = false;
|
|
132
|
-
function notify() {
|
|
133
|
-
if (notifying) return;
|
|
134
|
-
notifying = true;
|
|
135
|
-
subscribers.forEach((fn) => fn(theme, mode));
|
|
136
|
-
notifying = false;
|
|
137
|
-
}
|
|
138
|
-
function setMode(nextMode) {
|
|
139
|
-
if (nextMode === mode) return;
|
|
140
|
-
mode = nextMode;
|
|
141
|
-
storage.set(mode);
|
|
142
|
-
if (unsubscribeSystem) {
|
|
143
|
-
unsubscribeSystem();
|
|
144
|
-
unsubscribeSystem = null;
|
|
40
|
+
setStoredTheme(theme) {
|
|
41
|
+
if (!isBrowser)
|
|
42
|
+
return;
|
|
43
|
+
try {
|
|
44
|
+
localStorage.setItem(this.storageKey, theme);
|
|
45
|
+
}
|
|
46
|
+
catch (_a) {
|
|
47
|
+
/* noop */
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
getEffectiveTheme() {
|
|
51
|
+
var _a;
|
|
52
|
+
if (this.currentTheme === "auto") {
|
|
53
|
+
return ((_a = this.mediaQuery) === null || _a === void 0 ? void 0 : _a.matches) ? "dark" : "light";
|
|
54
|
+
}
|
|
55
|
+
return this.currentTheme;
|
|
56
|
+
}
|
|
57
|
+
applyTheme(theme) {
|
|
58
|
+
var _a;
|
|
59
|
+
if (!isBrowser)
|
|
60
|
+
return;
|
|
61
|
+
const effective = this.getEffectiveTheme();
|
|
62
|
+
document.documentElement.setAttribute("data-theme", effective);
|
|
63
|
+
document.documentElement.classList.remove("light", "dark");
|
|
64
|
+
document.documentElement.classList.add(effective);
|
|
65
|
+
(_a = this.onChange) === null || _a === void 0 ? void 0 : _a.call(this, theme, effective);
|
|
145
66
|
}
|
|
146
|
-
theme
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
67
|
+
setTheme(theme) {
|
|
68
|
+
this.currentTheme = theme;
|
|
69
|
+
this.setStoredTheme(theme);
|
|
70
|
+
this.applyTheme(theme);
|
|
150
71
|
}
|
|
151
|
-
notify();
|
|
152
|
-
}
|
|
153
|
-
function handleSystemChange(nextTheme) {
|
|
154
|
-
if (mode !== "system") return;
|
|
155
|
-
if (theme === nextTheme) return;
|
|
156
|
-
theme = nextTheme;
|
|
157
|
-
applyTheme(theme, config.attribute);
|
|
158
|
-
notify();
|
|
159
|
-
}
|
|
160
|
-
return {
|
|
161
|
-
get() {
|
|
162
|
-
return theme;
|
|
163
|
-
},
|
|
164
|
-
getMode() {
|
|
165
|
-
return mode;
|
|
166
|
-
},
|
|
167
|
-
set(nextMode) {
|
|
168
|
-
setMode(nextMode);
|
|
169
|
-
},
|
|
170
72
|
toggle() {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
73
|
+
const next = this.getEffectiveTheme() === "dark" ? "light" : "dark";
|
|
74
|
+
this.setTheme(next);
|
|
75
|
+
}
|
|
76
|
+
getTheme() {
|
|
77
|
+
return this.currentTheme;
|
|
78
|
+
}
|
|
79
|
+
destroy() {
|
|
80
|
+
if (this.mediaQuery) {
|
|
81
|
+
this.mediaQuery.removeEventListener("change", this.handleSystemChange);
|
|
82
|
+
}
|
|
179
83
|
}
|
|
180
|
-
};
|
|
181
84
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
import { useEffect, useState } from "react";
|
|
185
|
-
function createUseTheme(options) {
|
|
186
|
-
const manager = createThemeManager(options);
|
|
187
|
-
return function useTheme() {
|
|
188
|
-
const [theme, setThemeState] = useState(manager.get());
|
|
189
|
-
const [mode, setModeState] = useState(manager.getMode());
|
|
190
|
-
useEffect(() => {
|
|
191
|
-
const unsubscribe = manager.subscribe((t, m) => {
|
|
192
|
-
setThemeState(t);
|
|
193
|
-
setModeState(m);
|
|
194
|
-
});
|
|
195
|
-
return unsubscribe;
|
|
196
|
-
}, []);
|
|
197
|
-
return {
|
|
198
|
-
theme,
|
|
199
|
-
mode,
|
|
200
|
-
setTheme: (nextMode) => manager.set(nextMode),
|
|
201
|
-
toggleTheme: () => manager.toggle()
|
|
202
|
-
};
|
|
203
|
-
};
|
|
85
|
+
function createThemeToggle(config) {
|
|
86
|
+
return new ThemeToggle(config);
|
|
204
87
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
};
|
|
209
|
-
//# sourceMappingURL=index.mjs.map
|
|
88
|
+
|
|
89
|
+
export { ThemeToggle, createThemeToggle };
|
|
90
|
+
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/storage.ts","../src/system.ts","../src/dom.ts","../src/manager.ts","../src/react.ts"],"sourcesContent":["// src/storage.ts\n\nimport type { ThemeMode } from \"./types\";\n\nexport interface StorageAdapter {\n get(): ThemeMode | null;\n set(value: ThemeMode): void;\n clear(): void;\n}\n\nexport function createPersistence(key: string): StorageAdapter {\n // SSR / non-browser guard\n if (typeof window === \"undefined\") {\n return memoryStorage();\n }\n\n try {\n const testKey = \"__theme_test__\";\n window.localStorage.setItem(testKey, testKey);\n window.localStorage.removeItem(testKey);\n\n return localStorageAdapter(key);\n } catch {\n return memoryStorage();\n }\n}\n\nfunction localStorageAdapter(key: string): StorageAdapter {\n return {\n get() {\n const value = window.localStorage.getItem(key);\n\n if (value === \"light\" || value === \"dark\" || value === \"system\") {\n return value;\n }\n\n // clear corrupted or unknown values\n if (value !== null) {\n window.localStorage.removeItem(key);\n }\n\n return null;\n },\n\n set(value) {\n window.localStorage.setItem(key, value);\n },\n\n clear() {\n window.localStorage.removeItem(key);\n },\n };\n}\n\nfunction memoryStorage(): StorageAdapter {\n let value: ThemeMode | null = null;\n\n return {\n get() {\n return value;\n },\n set(v) {\n value = v;\n },\n clear() {\n value = null;\n },\n };\n}\n","// src/system.ts\n\nimport type { Theme } from \"./types\";\n\nconst QUERY = \"(prefers-color-scheme: dark)\";\n\nexport function getSystemTheme(): Theme {\n if (typeof window === \"undefined\") {\n return \"light\"; // safe default for SSR\n }\n\n if (!window.matchMedia) {\n return \"light\";\n }\n\n return window.matchMedia(QUERY).matches ? \"dark\" : \"light\";\n}\n\nexport function subscribeSystemTheme(\n callback: (theme: Theme) => void\n): () => void {\n if (typeof window === \"undefined\" || !window.matchMedia) {\n return () => {};\n }\n\n const media = window.matchMedia(QUERY);\n\n const handler = (event: MediaQueryListEvent) => {\n callback(event.matches ? \"dark\" : \"light\");\n };\n\n // Modern browsers\n if (media.addEventListener) {\n media.addEventListener(\"change\", handler);\n return () => media.removeEventListener(\"change\", handler);\n }\n\n // Legacy Safari fallback\n media.addListener(handler);\n return () => media.removeListener(handler);\n}","// src/dom.ts\n\nimport type { Theme } from \"./types\";\n\nfunction getRoot(): HTMLElement | null {\n if (typeof document === \"undefined\") {\n return null;\n }\n return document.documentElement;\n}\n\nexport function applyTheme(theme: Theme, attribute: string): void {\n const root = getRoot();\n if (!root) return;\n\n const current = root.getAttribute(attribute);\n if (current === theme) return; // avoid unnecessary DOM writes\n\n root.setAttribute(attribute, theme);\n}\n\nexport function getAppliedTheme(attribute: string): Theme | null {\n const root = getRoot();\n if (!root) return null;\n\n const value = root.getAttribute(attribute);\n if (value === \"light\" || value === \"dark\") {\n return value;\n }\n\n return null;\n}","// src/manager.ts\n\nimport type {\n Theme,\n ThemeMode,\n ThemeManager,\n ThemeManagerOptions,\n ThemeSubscriber,\n} from \"./types\";\n\nimport { createPersistence } from \"./storage\";\nimport { getSystemTheme, subscribeSystemTheme } from \"./system\";\nimport { applyTheme } from \"./dom\";\n\nconst DEFAULT_OPTIONS: Required<ThemeManagerOptions> = {\n attribute: \"data-theme\",\n storageKey: \"theme-mode\",\n defaultMode: \"system\",\n};\n\nexport function createThemeManager(\n options: ThemeManagerOptions = {}\n): ThemeManager {\n const config = { ...DEFAULT_OPTIONS, ...options };\n\n const storage = createPersistence(config.storageKey);\n const subscribers = new Set<ThemeSubscriber>();\n\n let mode: ThemeMode = storage.get() ?? config.defaultMode;\n\n let theme: Theme = resolveTheme(mode);\n\n // Apply initial theme immediately\n applyTheme(theme, config.attribute);\n\n // Listen to OS theme changes only when needed\n let unsubscribeSystem: (() => void) | null = null;\n if (mode === \"system\") {\n unsubscribeSystem = subscribeSystemTheme(handleSystemChange);\n }\n\n function resolveTheme(m: ThemeMode): Theme {\n return m === \"system\" ? getSystemTheme() : m;\n }\n let notifying = false;\n\n function notify() {\n if (notifying) return;\n\n notifying = true;\n subscribers.forEach((fn) => fn(theme, mode));\n notifying = false;\n }\n\n function setMode(nextMode: ThemeMode) {\n if (nextMode === mode) return;\n\n mode = nextMode;\n storage.set(mode);\n\n if (unsubscribeSystem) {\n unsubscribeSystem();\n unsubscribeSystem = null;\n }\n\n theme = resolveTheme(mode);\n applyTheme(theme, config.attribute);\n\n if (mode === \"system\") {\n unsubscribeSystem = subscribeSystemTheme(handleSystemChange);\n }\n\n notify();\n }\n\n function handleSystemChange(nextTheme: Theme) {\n if (mode !== \"system\") return;\n if (theme === nextTheme) return;\n\n theme = nextTheme;\n applyTheme(theme, config.attribute);\n notify();\n }\n\n return {\n get() {\n return theme;\n },\n\n getMode() {\n return mode;\n },\n\n set(nextMode) {\n setMode(nextMode);\n },\n toggle() {\n setMode(theme === \"dark\" ? \"light\" : \"dark\");\n },\n\n subscribe(fn) {\n subscribers.add(fn);\n fn(theme, mode); // immediate sync\n return () => {\n subscribers.delete(fn);\n };\n },\n };\n}\n","import { useEffect, useState } from \"react\";\nimport type { Theme, ThemeMode, ThemeManagerOptions } from \"./types\";\nimport { createThemeManager } from \"./manager\";\n\ntype UseThemeResult = {\n theme: Theme;\n mode: ThemeMode;\n setTheme: (mode: ThemeMode) => void;\n toggleTheme: () => void;\n};\n\n/**\n * Create a React hook bound to a ThemeManager instance.\n *\n * IMPORTANT:\n * - Call this once (e.g. in a module or provider)\n * - Do NOT call inside components repeatedly\n */\nexport function createUseTheme(options?: ThemeManagerOptions) {\n const manager = createThemeManager(options);\n\n return function useTheme(): UseThemeResult {\n const [theme, setThemeState] = useState<Theme>(manager.get());\n const [mode, setModeState] = useState<ThemeMode>(manager.getMode());\n\n useEffect(() => {\n const unsubscribe = manager.subscribe((t, m) => {\n setThemeState(t);\n setModeState(m);\n });\n\n return unsubscribe;\n }, []);\n\n return {\n theme,\n mode,\n setTheme: (nextMode: ThemeMode) => manager.set(nextMode),\n toggleTheme: () => manager.toggle(),\n };\n };\n}"],"mappings":";;;;;;;;;;;;;;;;;;AAUO,SAAS,kBAAkB,KAA6B;AAE7D,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO,cAAc;AAAA,EACvB;AAEA,MAAI;AACF,UAAM,UAAU;AAChB,WAAO,aAAa,QAAQ,SAAS,OAAO;AAC5C,WAAO,aAAa,WAAW,OAAO;AAEtC,WAAO,oBAAoB,GAAG;AAAA,EAChC,SAAQ;AACN,WAAO,cAAc;AAAA,EACvB;AACF;AAEA,SAAS,oBAAoB,KAA6B;AACxD,SAAO;AAAA,IACL,MAAM;AACJ,YAAM,QAAQ,OAAO,aAAa,QAAQ,GAAG;AAE7C,UAAI,UAAU,WAAW,UAAU,UAAU,UAAU,UAAU;AAC/D,eAAO;AAAA,MACT;AAGA,UAAI,UAAU,MAAM;AAClB,eAAO,aAAa,WAAW,GAAG;AAAA,MACpC;AAEA,aAAO;AAAA,IACT;AAAA,IAEA,IAAI,OAAO;AACT,aAAO,aAAa,QAAQ,KAAK,KAAK;AAAA,IACxC;AAAA,IAEA,QAAQ;AACN,aAAO,aAAa,WAAW,GAAG;AAAA,IACpC;AAAA,EACF;AACF;AAEA,SAAS,gBAAgC;AACvC,MAAI,QAA0B;AAE9B,SAAO;AAAA,IACL,MAAM;AACJ,aAAO;AAAA,IACT;AAAA,IACA,IAAI,GAAG;AACL,cAAQ;AAAA,IACV;AAAA,IACA,QAAQ;AACN,cAAQ;AAAA,IACV;AAAA,EACF;AACF;;;AChEA,IAAM,QAAQ;AAEP,SAAS,iBAAwB;AACtC,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,OAAO,YAAY;AACtB,WAAO;AAAA,EACT;AAEA,SAAO,OAAO,WAAW,KAAK,EAAE,UAAU,SAAS;AACrD;AAEO,SAAS,qBACd,UACY;AACZ,MAAI,OAAO,WAAW,eAAe,CAAC,OAAO,YAAY;AACvD,WAAO,MAAM;AAAA,IAAC;AAAA,EAChB;AAEA,QAAM,QAAQ,OAAO,WAAW,KAAK;AAErC,QAAM,UAAU,CAAC,UAA+B;AAC9C,aAAS,MAAM,UAAU,SAAS,OAAO;AAAA,EAC3C;AAGA,MAAI,MAAM,kBAAkB;AAC1B,UAAM,iBAAiB,UAAU,OAAO;AACxC,WAAO,MAAM,MAAM,oBAAoB,UAAU,OAAO;AAAA,EAC1D;AAGA,QAAM,YAAY,OAAO;AACzB,SAAO,MAAM,MAAM,eAAe,OAAO;AAC3C;;;ACpCA,SAAS,UAA8B;AACrC,MAAI,OAAO,aAAa,aAAa;AACnC,WAAO;AAAA,EACT;AACA,SAAO,SAAS;AAClB;AAEO,SAAS,WAAW,OAAc,WAAyB;AAChE,QAAM,OAAO,QAAQ;AACrB,MAAI,CAAC,KAAM;AAEX,QAAM,UAAU,KAAK,aAAa,SAAS;AAC3C,MAAI,YAAY,MAAO;AAEvB,OAAK,aAAa,WAAW,KAAK;AACpC;;;ACLA,IAAM,kBAAiD;AAAA,EACrD,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,aAAa;AACf;AAEO,SAAS,mBACd,UAA+B,CAAC,GAClB;AAtBhB;AAuBE,QAAM,SAAS,kCAAK,kBAAoB;AAExC,QAAM,UAAU,kBAAkB,OAAO,UAAU;AACnD,QAAM,cAAc,oBAAI,IAAqB;AAE7C,MAAI,QAAkB,aAAQ,IAAI,MAAZ,YAAiB,OAAO;AAE9C,MAAI,QAAe,aAAa,IAAI;AAGpC,aAAW,OAAO,OAAO,SAAS;AAGlC,MAAI,oBAAyC;AAC7C,MAAI,SAAS,UAAU;AACrB,wBAAoB,qBAAqB,kBAAkB;AAAA,EAC7D;AAEA,WAAS,aAAa,GAAqB;AACzC,WAAO,MAAM,WAAW,eAAe,IAAI;AAAA,EAC7C;AACA,MAAI,YAAY;AAEhB,WAAS,SAAS;AAChB,QAAI,UAAW;AAEf,gBAAY;AACZ,gBAAY,QAAQ,CAAC,OAAO,GAAG,OAAO,IAAI,CAAC;AAC3C,gBAAY;AAAA,EACd;AAEA,WAAS,QAAQ,UAAqB;AACpC,QAAI,aAAa,KAAM;AAEvB,WAAO;AACP,YAAQ,IAAI,IAAI;AAEhB,QAAI,mBAAmB;AACrB,wBAAkB;AAClB,0BAAoB;AAAA,IACtB;AAEA,YAAQ,aAAa,IAAI;AACzB,eAAW,OAAO,OAAO,SAAS;AAElC,QAAI,SAAS,UAAU;AACrB,0BAAoB,qBAAqB,kBAAkB;AAAA,IAC7D;AAEA,WAAO;AAAA,EACT;AAEA,WAAS,mBAAmB,WAAkB;AAC5C,QAAI,SAAS,SAAU;AACvB,QAAI,UAAU,UAAW;AAEzB,YAAQ;AACR,eAAW,OAAO,OAAO,SAAS;AAClC,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,MAAM;AACJ,aAAO;AAAA,IACT;AAAA,IAEA,UAAU;AACR,aAAO;AAAA,IACT;AAAA,IAEA,IAAI,UAAU;AACZ,cAAQ,QAAQ;AAAA,IAClB;AAAA,IACA,SAAS;AACP,cAAQ,UAAU,SAAS,UAAU,MAAM;AAAA,IAC7C;AAAA,IAEA,UAAU,IAAI;AACZ,kBAAY,IAAI,EAAE;AAClB,SAAG,OAAO,IAAI;AACd,aAAO,MAAM;AACX,oBAAY,OAAO,EAAE;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AACF;;;AC5GA,SAAS,WAAW,gBAAgB;AAkB7B,SAAS,eAAe,SAA+B;AAC5D,QAAM,UAAU,mBAAmB,OAAO;AAE1C,SAAO,SAAS,WAA2B;AACzC,UAAM,CAAC,OAAO,aAAa,IAAI,SAAgB,QAAQ,IAAI,CAAC;AAC5D,UAAM,CAAC,MAAM,YAAY,IAAI,SAAoB,QAAQ,QAAQ,CAAC;AAElE,cAAU,MAAM;AACd,YAAM,cAAc,QAAQ,UAAU,CAAC,GAAG,MAAM;AAC9C,sBAAc,CAAC;AACf,qBAAa,CAAC;AAAA,MAChB,CAAC;AAED,aAAO;AAAA,IACT,GAAG,CAAC,CAAC;AAEL,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,UAAU,CAAC,aAAwB,QAAQ,IAAI,QAAQ;AAAA,MACvD,aAAa,MAAM,QAAQ,OAAO;AAAA,IACpC;AAAA,EACF;AACF;","names":[]}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/index.ts"],"sourcesContent":["export type Theme = \"light\" | \"dark\" | \"auto\";\n\nexport interface ThemeConfig {\n storageKey?: string;\n defaultTheme?: Theme;\n useSystemPreference?: boolean;\n onChange?: (theme: Theme, effective: \"light\" | \"dark\") => void;\n}\n\nconst isBrowser =\n typeof window !== \"undefined\" &&\n typeof document !== \"undefined\";\n\nexport class ThemeToggle {\n private storageKey: string;\n private currentTheme: Theme;\n private useSystemPreference: boolean;\n private onChange?: ThemeConfig[\"onChange\"];\n private mediaQuery?: MediaQueryList;\n\n private handleSystemChange = () => {\n if (this.currentTheme === \"auto\") {\n this.applyTheme(\"auto\");\n }\n };\n\n constructor(config: ThemeConfig = {}) {\n this.storageKey = config.storageKey ?? \"app-theme\";\n this.useSystemPreference = config.useSystemPreference ?? true;\n this.onChange = config.onChange;\n\n const stored = this.getStoredTheme();\n this.currentTheme = stored ?? config.defaultTheme ?? \"auto\";\n\n this.init();\n }\n\n private init(): void {\n if (!isBrowser) return;\n\n if (this.useSystemPreference) {\n this.mediaQuery = window.matchMedia(\"(prefers-color-scheme: dark)\");\n this.mediaQuery.addEventListener(\"change\", this.handleSystemChange);\n }\n\n this.applyTheme(this.currentTheme);\n }\n\n private getStoredTheme(): Theme | null {\n if (!isBrowser) return null;\n\n try {\n const stored = localStorage.getItem(this.storageKey);\n return stored === \"light\" || stored === \"dark\" || stored === \"auto\"\n ? stored\n : null;\n } catch {\n return null;\n }\n }\n\n private setStoredTheme(theme: Theme): void {\n if (!isBrowser) return;\n\n try {\n localStorage.setItem(this.storageKey, theme);\n } catch {\n /* noop */\n }\n }\n\n public getEffectiveTheme(): \"light\" | \"dark\" {\n if (this.currentTheme === \"auto\") {\n return this.mediaQuery?.matches ? \"dark\" : \"light\";\n }\n return this.currentTheme;\n }\n\n private applyTheme(theme: Theme): void {\n if (!isBrowser) return;\n\n const effective = this.getEffectiveTheme();\n\n document.documentElement.setAttribute(\"data-theme\", effective);\n document.documentElement.classList.remove(\"light\", \"dark\");\n document.documentElement.classList.add(effective);\n\n this.onChange?.(theme, effective);\n }\n\n public setTheme(theme: Theme): void {\n this.currentTheme = theme;\n this.setStoredTheme(theme);\n this.applyTheme(theme);\n }\n\n public toggle(): void {\n const next =\n this.getEffectiveTheme() === \"dark\" ? \"light\" : \"dark\";\n this.setTheme(next);\n }\n\n public getTheme(): Theme {\n return this.currentTheme;\n }\n\n public destroy(): void {\n if (this.mediaQuery) {\n this.mediaQuery.removeEventListener(\n \"change\",\n this.handleSystemChange\n );\n }\n }\n}\n\nexport function createThemeToggle(\n config?: ThemeConfig\n): ThemeToggle {\n return new ThemeToggle(config);\n}"],"names":[],"mappings":"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"}
|
package/dist/react.cjs
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
var index = require('./index.cjs');
|
|
5
|
+
|
|
6
|
+
function _interopNamespaceDefault(e) {
|
|
7
|
+
var n = Object.create(null);
|
|
8
|
+
if (e) {
|
|
9
|
+
Object.keys(e).forEach(function (k) {
|
|
10
|
+
if (k !== 'default') {
|
|
11
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
12
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function () { return e[k]; }
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
n.default = e;
|
|
20
|
+
return Object.freeze(n);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Create a React hook bound to a single ThemeToggle instance.
|
|
27
|
+
*
|
|
28
|
+
* IMPORTANT:
|
|
29
|
+
* - Call this ONCE at module scope
|
|
30
|
+
* - Do NOT call inside components
|
|
31
|
+
*/
|
|
32
|
+
function createUseTheme(config) {
|
|
33
|
+
const manager = index.createThemeToggle(config);
|
|
34
|
+
return function useTheme() {
|
|
35
|
+
const [theme, setTheme] = React__namespace.useState(manager.getTheme());
|
|
36
|
+
const [effectiveTheme, setEffectiveTheme] = React__namespace.useState(manager.getEffectiveTheme());
|
|
37
|
+
React__namespace.useEffect(() => {
|
|
38
|
+
const prev = manager["onChange"];
|
|
39
|
+
manager["onChange"] = (t, e) => {
|
|
40
|
+
setTheme(t);
|
|
41
|
+
setEffectiveTheme(e);
|
|
42
|
+
};
|
|
43
|
+
return () => {
|
|
44
|
+
manager["onChange"] = prev;
|
|
45
|
+
};
|
|
46
|
+
}, []);
|
|
47
|
+
return {
|
|
48
|
+
theme,
|
|
49
|
+
effectiveTheme,
|
|
50
|
+
setTheme: (t) => manager.setTheme(t),
|
|
51
|
+
toggleTheme: () => manager.toggle(),
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
exports.createUseTheme = createUseTheme;
|
|
57
|
+
//# sourceMappingURL=react.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.cjs","sources":["../src/react-adapter.ts"],"sourcesContent":["import * as React from \"react\";\nimport type { Theme, ThemeConfig } from \"./index\";\nimport { createThemeToggle } from \"./index\";\n\ntype UseThemeResult = {\n theme: Theme;\n effectiveTheme: \"light\" | \"dark\";\n setTheme: (theme: Theme) => void;\n toggleTheme: () => void;\n};\n\n/**\n * Create a React hook bound to a single ThemeToggle instance.\n *\n * IMPORTANT:\n * - Call this ONCE at module scope\n * - Do NOT call inside components\n */\nexport function createUseTheme(config?: ThemeConfig) {\n const manager = createThemeToggle(config);\n\n return function useTheme(): UseThemeResult {\n const [theme, setTheme] = React.useState<Theme>(\n manager.getTheme()\n );\n const [effectiveTheme, setEffectiveTheme] = React.useState<\n \"light\" | \"dark\"\n >(manager.getEffectiveTheme());\n\n React.useEffect(() => {\n const prev = manager[\"onChange\"];\n\n manager[\"onChange\"] = (t, e) => {\n setTheme(t);\n setEffectiveTheme(e);\n };\n\n return () => {\n manager[\"onChange\"] = prev;\n };\n }, []);\n\n return {\n theme,\n effectiveTheme,\n setTheme: (t) => manager.setTheme(t),\n toggleTheme: () => manager.toggle(),\n };\n };\n}"],"names":["createThemeToggle","React"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAWA;;;;;;AAMG;AACG,SAAU,cAAc,CAAC,MAAoB,EAAA;AACjD,IAAA,MAAM,OAAO,GAAGA,uBAAiB,CAAC,MAAM,CAAC;AAEzC,IAAA,OAAO,SAAS,QAAQ,GAAA;AACtB,QAAA,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAGC,gBAAK,CAAC,QAAQ,CACtC,OAAO,CAAC,QAAQ,EAAE,CACnB;AACD,QAAA,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAGA,gBAAK,CAAC,QAAQ,CAExD,OAAO,CAAC,iBAAiB,EAAE,CAAC;AAE9B,QAAAA,gBAAK,CAAC,SAAS,CAAC,MAAK;AACnB,YAAA,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC;YAEhC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,KAAI;gBAC7B,QAAQ,CAAC,CAAC,CAAC;gBACX,iBAAiB,CAAC,CAAC,CAAC;AACtB,YAAA,CAAC;AAED,YAAA,OAAO,MAAK;AACV,gBAAA,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI;AAC5B,YAAA,CAAC;QACH,CAAC,EAAE,EAAE,CAAC;QAEN,OAAO;YACL,KAAK;YACL,cAAc;YACd,QAAQ,EAAE,CAAC,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;AACpC,YAAA,WAAW,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE;SACpC;AACH,IAAA,CAAC;AACH;;;;"}
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ThemeConfig, Theme } from './index.js';
|
|
2
|
+
|
|
3
|
+
type UseThemeResult = {
|
|
4
|
+
theme: Theme;
|
|
5
|
+
effectiveTheme: "light" | "dark";
|
|
6
|
+
setTheme: (theme: Theme) => void;
|
|
7
|
+
toggleTheme: () => void;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Create a React hook bound to a single ThemeToggle instance.
|
|
11
|
+
*
|
|
12
|
+
* IMPORTANT:
|
|
13
|
+
* - Call this ONCE at module scope
|
|
14
|
+
* - Do NOT call inside components
|
|
15
|
+
*/
|
|
16
|
+
declare function createUseTheme(config?: ThemeConfig): () => UseThemeResult;
|
|
17
|
+
|
|
18
|
+
export { createUseTheme };
|
package/dist/react.mjs
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { createThemeToggle } from './index.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Create a React hook bound to a single ThemeToggle instance.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT:
|
|
8
|
+
* - Call this ONCE at module scope
|
|
9
|
+
* - Do NOT call inside components
|
|
10
|
+
*/
|
|
11
|
+
function createUseTheme(config) {
|
|
12
|
+
const manager = createThemeToggle(config);
|
|
13
|
+
return function useTheme() {
|
|
14
|
+
const [theme, setTheme] = React.useState(manager.getTheme());
|
|
15
|
+
const [effectiveTheme, setEffectiveTheme] = React.useState(manager.getEffectiveTheme());
|
|
16
|
+
React.useEffect(() => {
|
|
17
|
+
const prev = manager["onChange"];
|
|
18
|
+
manager["onChange"] = (t, e) => {
|
|
19
|
+
setTheme(t);
|
|
20
|
+
setEffectiveTheme(e);
|
|
21
|
+
};
|
|
22
|
+
return () => {
|
|
23
|
+
manager["onChange"] = prev;
|
|
24
|
+
};
|
|
25
|
+
}, []);
|
|
26
|
+
return {
|
|
27
|
+
theme,
|
|
28
|
+
effectiveTheme,
|
|
29
|
+
setTheme: (t) => manager.setTheme(t),
|
|
30
|
+
toggleTheme: () => manager.toggle(),
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { createUseTheme };
|
|
36
|
+
//# sourceMappingURL=react.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.mjs","sources":["../src/react-adapter.ts"],"sourcesContent":["import * as React from \"react\";\nimport type { Theme, ThemeConfig } from \"./index\";\nimport { createThemeToggle } from \"./index\";\n\ntype UseThemeResult = {\n theme: Theme;\n effectiveTheme: \"light\" | \"dark\";\n setTheme: (theme: Theme) => void;\n toggleTheme: () => void;\n};\n\n/**\n * Create a React hook bound to a single ThemeToggle instance.\n *\n * IMPORTANT:\n * - Call this ONCE at module scope\n * - Do NOT call inside components\n */\nexport function createUseTheme(config?: ThemeConfig) {\n const manager = createThemeToggle(config);\n\n return function useTheme(): UseThemeResult {\n const [theme, setTheme] = React.useState<Theme>(\n manager.getTheme()\n );\n const [effectiveTheme, setEffectiveTheme] = React.useState<\n \"light\" | \"dark\"\n >(manager.getEffectiveTheme());\n\n React.useEffect(() => {\n const prev = manager[\"onChange\"];\n\n manager[\"onChange\"] = (t, e) => {\n setTheme(t);\n setEffectiveTheme(e);\n };\n\n return () => {\n manager[\"onChange\"] = prev;\n };\n }, []);\n\n return {\n theme,\n effectiveTheme,\n setTheme: (t) => manager.setTheme(t),\n toggleTheme: () => manager.toggle(),\n };\n };\n}"],"names":[],"mappings":";;;AAWA;;;;;;AAMG;AACG,SAAU,cAAc,CAAC,MAAoB,EAAA;AACjD,IAAA,MAAM,OAAO,GAAG,iBAAiB,CAAC,MAAM,CAAC;AAEzC,IAAA,OAAO,SAAS,QAAQ,GAAA;AACtB,QAAA,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC,QAAQ,CACtC,OAAO,CAAC,QAAQ,EAAE,CACnB;AACD,QAAA,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,KAAK,CAAC,QAAQ,CAExD,OAAO,CAAC,iBAAiB,EAAE,CAAC;AAE9B,QAAA,KAAK,CAAC,SAAS,CAAC,MAAK;AACnB,YAAA,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC;YAEhC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,KAAI;gBAC7B,QAAQ,CAAC,CAAC,CAAC;gBACX,iBAAiB,CAAC,CAAC,CAAC;AACtB,YAAA,CAAC;AAED,YAAA,OAAO,MAAK;AACV,gBAAA,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI;AAC5B,YAAA,CAAC;QACH,CAAC,EAAE,EAAE,CAAC;QAEN,OAAO;YACL,KAAK;YACL,cAAc;YACd,QAAQ,EAAE,CAAC,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;AACpC,YAAA,WAAW,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE;SACpC;AACH,IAAA,CAAC;AACH;;;;"}
|
package/package.json
CHANGED
|
@@ -1,47 +1,56 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
"name": "@pshah-lab/themeswitcher",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "A lightweight, framework-agnostic theme manager with SSR-safe dark/light/system mode support",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.cjs",
|
|
8
|
+
"module": "./dist/index.mjs",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
},
|
|
15
|
+
"./react": {
|
|
16
|
+
"import": "./dist/react.mjs",
|
|
17
|
+
"require": "./dist/react.cjs",
|
|
18
|
+
"types": "./dist/react.d.ts"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "rollup -c",
|
|
26
|
+
"prepublishOnly": "npm run build"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"react": ">=16.8"
|
|
30
|
+
},
|
|
31
|
+
"peerDependenciesMeta": {
|
|
32
|
+
"react": {
|
|
33
|
+
"optional": true
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@rollup/plugin-typescript": "^11.1.6",
|
|
38
|
+
"@types/react": "^19.2.8",
|
|
39
|
+
"react": "^19.2.3",
|
|
40
|
+
"rollup": "^4.55.1",
|
|
41
|
+
"rollup-plugin-dts": "^6.3.0",
|
|
42
|
+
"typescript": "^5.9.3"
|
|
43
|
+
},
|
|
44
|
+
"keywords": [
|
|
45
|
+
"dark-mode",
|
|
46
|
+
"light-mode",
|
|
47
|
+
"theme",
|
|
48
|
+
"toggle",
|
|
49
|
+
"nextjs",
|
|
50
|
+
"react",
|
|
51
|
+
"ssr"
|
|
52
|
+
],
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"tslib": "^2.8.1"
|
|
13
55
|
}
|
|
14
|
-
},
|
|
15
|
-
"files": [
|
|
16
|
-
"dist",
|
|
17
|
-
"scripts"
|
|
18
|
-
],
|
|
19
|
-
"scripts": {
|
|
20
|
-
"build": "tsup",
|
|
21
|
-
"test": "vitest",
|
|
22
|
-
"prepublishOnly": "npm run build"
|
|
23
|
-
},
|
|
24
|
-
"keywords": [
|
|
25
|
-
"theme",
|
|
26
|
-
"dark-mode",
|
|
27
|
-
"light-mode",
|
|
28
|
-
"theme-switcher",
|
|
29
|
-
"css-theme",
|
|
30
|
-
"ssr"
|
|
31
|
-
],
|
|
32
|
-
"devDependencies": {
|
|
33
|
-
"@types/react": "^19.2.7",
|
|
34
|
-
"jsdom": "^27.4.0",
|
|
35
|
-
"tsup": "^8.5.1",
|
|
36
|
-
"typescript": "^5.0.0",
|
|
37
|
-
"vitest": "^4.0.16"
|
|
38
|
-
},
|
|
39
|
-
"peerDependencies": {
|
|
40
|
-
"react": ">=16.8"
|
|
41
|
-
},
|
|
42
|
-
"peerDependenciesMeta": {
|
|
43
|
-
"react": {
|
|
44
|
-
"optional": true
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
56
|
}
|
package/README.md
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
Prevent Theme Flash (Recommended)
|
|
2
|
-
|
|
3
|
-
When using light/dark themes, browsers may briefly render the page in the default theme before JavaScript loads. This results in a visible flash of incorrect theme.
|
|
4
|
-
|
|
5
|
-
To prevent this, add the following inline script to your HTML before any CSS is loaded.
|
|
6
|
-
|
|
7
|
-
Why this is needed
|
|
8
|
-
• The script runs before first paint
|
|
9
|
-
• It applies the correct theme synchronously
|
|
10
|
-
• It works even before your JavaScript bundle loads
|
|
11
|
-
• This is required for SSR and strongly recommended for all setups
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
Add this to <head>
|
|
15
|
-
|
|
16
|
-
<script>
|
|
17
|
-
(function () {
|
|
18
|
-
try {
|
|
19
|
-
var key = "theme-mode";
|
|
20
|
-
var attribute = "data-theme";
|
|
21
|
-
|
|
22
|
-
var stored = localStorage.getItem(key);
|
|
23
|
-
var mode =
|
|
24
|
-
stored === "light" || stored === "dark" || stored === "system"
|
|
25
|
-
? stored
|
|
26
|
-
: "system";
|
|
27
|
-
|
|
28
|
-
var isDark =
|
|
29
|
-
window.matchMedia &&
|
|
30
|
-
window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
31
|
-
|
|
32
|
-
var theme =
|
|
33
|
-
mode === "system"
|
|
34
|
-
? isDark
|
|
35
|
-
? "dark"
|
|
36
|
-
: "light"
|
|
37
|
-
: mode;
|
|
38
|
-
|
|
39
|
-
document.documentElement.setAttribute(attribute, theme);
|
|
40
|
-
} catch (e) {
|
|
41
|
-
// fail silently
|
|
42
|
-
}
|
|
43
|
-
})();
|
|
44
|
-
</script>
|
|
45
|
-
|
|
46
|
-
Place this before your CSS:
|
|
47
|
-
|
|
48
|
-
<head>
|
|
49
|
-
<!-- Prevent theme flash -->
|
|
50
|
-
<script>/* theme script */</script>
|
|
51
|
-
|
|
52
|
-
<link rel="stylesheet" href="styles.css" />
|
|
53
|
-
</head>
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
Notes
|
|
60
|
-
• Do not import this script from the package
|
|
61
|
-
• Do not bundle it
|
|
62
|
-
• Copy-paste is intentional and required for correct timing
|
|
63
|
-
• The script logic matches the internal theme manager
|
package/dist/index.d.mts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
type Theme = "light" | "dark";
|
|
2
|
-
type ThemeMode = Theme | "system";
|
|
3
|
-
interface ThemeManagerOptions {
|
|
4
|
-
/**
|
|
5
|
-
* Attribute applied to the root element
|
|
6
|
-
* Example: data-theme="dark"
|
|
7
|
-
*/
|
|
8
|
-
attribute?: string;
|
|
9
|
-
/**
|
|
10
|
-
* Storage key for persistence
|
|
11
|
-
*/
|
|
12
|
-
storageKey?: string;
|
|
13
|
-
/**
|
|
14
|
-
* Default mode when nothing is stored
|
|
15
|
-
*/
|
|
16
|
-
defaultMode?: ThemeMode;
|
|
17
|
-
}
|
|
18
|
-
type ThemeSubscriber = (theme: Theme, mode: ThemeMode) => void;
|
|
19
|
-
interface ThemeManager {
|
|
20
|
-
get(): Theme;
|
|
21
|
-
getMode(): ThemeMode;
|
|
22
|
-
set(mode: ThemeMode): void;
|
|
23
|
-
toggle(): void;
|
|
24
|
-
subscribe(fn: ThemeSubscriber): () => void;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
declare function createThemeManager(options?: ThemeManagerOptions): ThemeManager;
|
|
28
|
-
|
|
29
|
-
type UseThemeResult = {
|
|
30
|
-
theme: Theme;
|
|
31
|
-
mode: ThemeMode;
|
|
32
|
-
setTheme: (mode: ThemeMode) => void;
|
|
33
|
-
toggleTheme: () => void;
|
|
34
|
-
};
|
|
35
|
-
/**
|
|
36
|
-
* Create a React hook bound to a ThemeManager instance.
|
|
37
|
-
*
|
|
38
|
-
* IMPORTANT:
|
|
39
|
-
* - Call this once (e.g. in a module or provider)
|
|
40
|
-
* - Do NOT call inside components repeatedly
|
|
41
|
-
*/
|
|
42
|
-
declare function createUseTheme(options?: ThemeManagerOptions): () => UseThemeResult;
|
|
43
|
-
|
|
44
|
-
export { type Theme, type ThemeManager, type ThemeManagerOptions, type ThemeMode, type ThemeSubscriber, createThemeManager, createUseTheme };
|
package/dist/index.js
DELETED
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
6
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
-
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
8
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
9
|
-
var __spreadValues = (a, b) => {
|
|
10
|
-
for (var prop in b || (b = {}))
|
|
11
|
-
if (__hasOwnProp.call(b, prop))
|
|
12
|
-
__defNormalProp(a, prop, b[prop]);
|
|
13
|
-
if (__getOwnPropSymbols)
|
|
14
|
-
for (var prop of __getOwnPropSymbols(b)) {
|
|
15
|
-
if (__propIsEnum.call(b, prop))
|
|
16
|
-
__defNormalProp(a, prop, b[prop]);
|
|
17
|
-
}
|
|
18
|
-
return a;
|
|
19
|
-
};
|
|
20
|
-
var __export = (target, all) => {
|
|
21
|
-
for (var name in all)
|
|
22
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
23
|
-
};
|
|
24
|
-
var __copyProps = (to, from, except, desc) => {
|
|
25
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
26
|
-
for (let key of __getOwnPropNames(from))
|
|
27
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
28
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
29
|
-
}
|
|
30
|
-
return to;
|
|
31
|
-
};
|
|
32
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
33
|
-
|
|
34
|
-
// src/index.ts
|
|
35
|
-
var index_exports = {};
|
|
36
|
-
__export(index_exports, {
|
|
37
|
-
createThemeManager: () => createThemeManager,
|
|
38
|
-
createUseTheme: () => createUseTheme
|
|
39
|
-
});
|
|
40
|
-
module.exports = __toCommonJS(index_exports);
|
|
41
|
-
|
|
42
|
-
// src/storage.ts
|
|
43
|
-
function createPersistence(key) {
|
|
44
|
-
if (typeof window === "undefined") {
|
|
45
|
-
return memoryStorage();
|
|
46
|
-
}
|
|
47
|
-
try {
|
|
48
|
-
const testKey = "__theme_test__";
|
|
49
|
-
window.localStorage.setItem(testKey, testKey);
|
|
50
|
-
window.localStorage.removeItem(testKey);
|
|
51
|
-
return localStorageAdapter(key);
|
|
52
|
-
} catch (e) {
|
|
53
|
-
return memoryStorage();
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
function localStorageAdapter(key) {
|
|
57
|
-
return {
|
|
58
|
-
get() {
|
|
59
|
-
const value = window.localStorage.getItem(key);
|
|
60
|
-
if (value === "light" || value === "dark" || value === "system") {
|
|
61
|
-
return value;
|
|
62
|
-
}
|
|
63
|
-
if (value !== null) {
|
|
64
|
-
window.localStorage.removeItem(key);
|
|
65
|
-
}
|
|
66
|
-
return null;
|
|
67
|
-
},
|
|
68
|
-
set(value) {
|
|
69
|
-
window.localStorage.setItem(key, value);
|
|
70
|
-
},
|
|
71
|
-
clear() {
|
|
72
|
-
window.localStorage.removeItem(key);
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
function memoryStorage() {
|
|
77
|
-
let value = null;
|
|
78
|
-
return {
|
|
79
|
-
get() {
|
|
80
|
-
return value;
|
|
81
|
-
},
|
|
82
|
-
set(v) {
|
|
83
|
-
value = v;
|
|
84
|
-
},
|
|
85
|
-
clear() {
|
|
86
|
-
value = null;
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// src/system.ts
|
|
92
|
-
var QUERY = "(prefers-color-scheme: dark)";
|
|
93
|
-
function getSystemTheme() {
|
|
94
|
-
if (typeof window === "undefined") {
|
|
95
|
-
return "light";
|
|
96
|
-
}
|
|
97
|
-
if (!window.matchMedia) {
|
|
98
|
-
return "light";
|
|
99
|
-
}
|
|
100
|
-
return window.matchMedia(QUERY).matches ? "dark" : "light";
|
|
101
|
-
}
|
|
102
|
-
function subscribeSystemTheme(callback) {
|
|
103
|
-
if (typeof window === "undefined" || !window.matchMedia) {
|
|
104
|
-
return () => {
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
const media = window.matchMedia(QUERY);
|
|
108
|
-
const handler = (event) => {
|
|
109
|
-
callback(event.matches ? "dark" : "light");
|
|
110
|
-
};
|
|
111
|
-
if (media.addEventListener) {
|
|
112
|
-
media.addEventListener("change", handler);
|
|
113
|
-
return () => media.removeEventListener("change", handler);
|
|
114
|
-
}
|
|
115
|
-
media.addListener(handler);
|
|
116
|
-
return () => media.removeListener(handler);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// src/dom.ts
|
|
120
|
-
function getRoot() {
|
|
121
|
-
if (typeof document === "undefined") {
|
|
122
|
-
return null;
|
|
123
|
-
}
|
|
124
|
-
return document.documentElement;
|
|
125
|
-
}
|
|
126
|
-
function applyTheme(theme, attribute) {
|
|
127
|
-
const root = getRoot();
|
|
128
|
-
if (!root) return;
|
|
129
|
-
const current = root.getAttribute(attribute);
|
|
130
|
-
if (current === theme) return;
|
|
131
|
-
root.setAttribute(attribute, theme);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// src/manager.ts
|
|
135
|
-
var DEFAULT_OPTIONS = {
|
|
136
|
-
attribute: "data-theme",
|
|
137
|
-
storageKey: "theme-mode",
|
|
138
|
-
defaultMode: "system"
|
|
139
|
-
};
|
|
140
|
-
function createThemeManager(options = {}) {
|
|
141
|
-
var _a;
|
|
142
|
-
const config = __spreadValues(__spreadValues({}, DEFAULT_OPTIONS), options);
|
|
143
|
-
const storage = createPersistence(config.storageKey);
|
|
144
|
-
const subscribers = /* @__PURE__ */ new Set();
|
|
145
|
-
let mode = (_a = storage.get()) != null ? _a : config.defaultMode;
|
|
146
|
-
let theme = resolveTheme(mode);
|
|
147
|
-
applyTheme(theme, config.attribute);
|
|
148
|
-
let unsubscribeSystem = null;
|
|
149
|
-
if (mode === "system") {
|
|
150
|
-
unsubscribeSystem = subscribeSystemTheme(handleSystemChange);
|
|
151
|
-
}
|
|
152
|
-
function resolveTheme(m) {
|
|
153
|
-
return m === "system" ? getSystemTheme() : m;
|
|
154
|
-
}
|
|
155
|
-
let notifying = false;
|
|
156
|
-
function notify() {
|
|
157
|
-
if (notifying) return;
|
|
158
|
-
notifying = true;
|
|
159
|
-
subscribers.forEach((fn) => fn(theme, mode));
|
|
160
|
-
notifying = false;
|
|
161
|
-
}
|
|
162
|
-
function setMode(nextMode) {
|
|
163
|
-
if (nextMode === mode) return;
|
|
164
|
-
mode = nextMode;
|
|
165
|
-
storage.set(mode);
|
|
166
|
-
if (unsubscribeSystem) {
|
|
167
|
-
unsubscribeSystem();
|
|
168
|
-
unsubscribeSystem = null;
|
|
169
|
-
}
|
|
170
|
-
theme = resolveTheme(mode);
|
|
171
|
-
applyTheme(theme, config.attribute);
|
|
172
|
-
if (mode === "system") {
|
|
173
|
-
unsubscribeSystem = subscribeSystemTheme(handleSystemChange);
|
|
174
|
-
}
|
|
175
|
-
notify();
|
|
176
|
-
}
|
|
177
|
-
function handleSystemChange(nextTheme) {
|
|
178
|
-
if (mode !== "system") return;
|
|
179
|
-
if (theme === nextTheme) return;
|
|
180
|
-
theme = nextTheme;
|
|
181
|
-
applyTheme(theme, config.attribute);
|
|
182
|
-
notify();
|
|
183
|
-
}
|
|
184
|
-
return {
|
|
185
|
-
get() {
|
|
186
|
-
return theme;
|
|
187
|
-
},
|
|
188
|
-
getMode() {
|
|
189
|
-
return mode;
|
|
190
|
-
},
|
|
191
|
-
set(nextMode) {
|
|
192
|
-
setMode(nextMode);
|
|
193
|
-
},
|
|
194
|
-
toggle() {
|
|
195
|
-
setMode(theme === "dark" ? "light" : "dark");
|
|
196
|
-
},
|
|
197
|
-
subscribe(fn) {
|
|
198
|
-
subscribers.add(fn);
|
|
199
|
-
fn(theme, mode);
|
|
200
|
-
return () => {
|
|
201
|
-
subscribers.delete(fn);
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// src/react.ts
|
|
208
|
-
var import_react = require("react");
|
|
209
|
-
function createUseTheme(options) {
|
|
210
|
-
const manager = createThemeManager(options);
|
|
211
|
-
return function useTheme() {
|
|
212
|
-
const [theme, setThemeState] = (0, import_react.useState)(manager.get());
|
|
213
|
-
const [mode, setModeState] = (0, import_react.useState)(manager.getMode());
|
|
214
|
-
(0, import_react.useEffect)(() => {
|
|
215
|
-
const unsubscribe = manager.subscribe((t, m) => {
|
|
216
|
-
setThemeState(t);
|
|
217
|
-
setModeState(m);
|
|
218
|
-
});
|
|
219
|
-
return unsubscribe;
|
|
220
|
-
}, []);
|
|
221
|
-
return {
|
|
222
|
-
theme,
|
|
223
|
-
mode,
|
|
224
|
-
setTheme: (nextMode) => manager.set(nextMode),
|
|
225
|
-
toggleTheme: () => manager.toggle()
|
|
226
|
-
};
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
230
|
-
0 && (module.exports = {
|
|
231
|
-
createThemeManager,
|
|
232
|
-
createUseTheme
|
|
233
|
-
});
|
|
234
|
-
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/storage.ts","../src/system.ts","../src/dom.ts","../src/manager.ts","../src/react.ts"],"sourcesContent":["export type {\n Theme,\n ThemeMode,\n ThemeManager,\n ThemeManagerOptions,\n ThemeSubscriber,\n } from \"./types\";\n \n export { createThemeManager } from \"./manager\";\n export { createUseTheme } from \"./react\";","// src/storage.ts\n\nimport type { ThemeMode } from \"./types\";\n\nexport interface StorageAdapter {\n get(): ThemeMode | null;\n set(value: ThemeMode): void;\n clear(): void;\n}\n\nexport function createPersistence(key: string): StorageAdapter {\n // SSR / non-browser guard\n if (typeof window === \"undefined\") {\n return memoryStorage();\n }\n\n try {\n const testKey = \"__theme_test__\";\n window.localStorage.setItem(testKey, testKey);\n window.localStorage.removeItem(testKey);\n\n return localStorageAdapter(key);\n } catch {\n return memoryStorage();\n }\n}\n\nfunction localStorageAdapter(key: string): StorageAdapter {\n return {\n get() {\n const value = window.localStorage.getItem(key);\n\n if (value === \"light\" || value === \"dark\" || value === \"system\") {\n return value;\n }\n\n // clear corrupted or unknown values\n if (value !== null) {\n window.localStorage.removeItem(key);\n }\n\n return null;\n },\n\n set(value) {\n window.localStorage.setItem(key, value);\n },\n\n clear() {\n window.localStorage.removeItem(key);\n },\n };\n}\n\nfunction memoryStorage(): StorageAdapter {\n let value: ThemeMode | null = null;\n\n return {\n get() {\n return value;\n },\n set(v) {\n value = v;\n },\n clear() {\n value = null;\n },\n };\n}\n","// src/system.ts\n\nimport type { Theme } from \"./types\";\n\nconst QUERY = \"(prefers-color-scheme: dark)\";\n\nexport function getSystemTheme(): Theme {\n if (typeof window === \"undefined\") {\n return \"light\"; // safe default for SSR\n }\n\n if (!window.matchMedia) {\n return \"light\";\n }\n\n return window.matchMedia(QUERY).matches ? \"dark\" : \"light\";\n}\n\nexport function subscribeSystemTheme(\n callback: (theme: Theme) => void\n): () => void {\n if (typeof window === \"undefined\" || !window.matchMedia) {\n return () => {};\n }\n\n const media = window.matchMedia(QUERY);\n\n const handler = (event: MediaQueryListEvent) => {\n callback(event.matches ? \"dark\" : \"light\");\n };\n\n // Modern browsers\n if (media.addEventListener) {\n media.addEventListener(\"change\", handler);\n return () => media.removeEventListener(\"change\", handler);\n }\n\n // Legacy Safari fallback\n media.addListener(handler);\n return () => media.removeListener(handler);\n}","// src/dom.ts\n\nimport type { Theme } from \"./types\";\n\nfunction getRoot(): HTMLElement | null {\n if (typeof document === \"undefined\") {\n return null;\n }\n return document.documentElement;\n}\n\nexport function applyTheme(theme: Theme, attribute: string): void {\n const root = getRoot();\n if (!root) return;\n\n const current = root.getAttribute(attribute);\n if (current === theme) return; // avoid unnecessary DOM writes\n\n root.setAttribute(attribute, theme);\n}\n\nexport function getAppliedTheme(attribute: string): Theme | null {\n const root = getRoot();\n if (!root) return null;\n\n const value = root.getAttribute(attribute);\n if (value === \"light\" || value === \"dark\") {\n return value;\n }\n\n return null;\n}","// src/manager.ts\n\nimport type {\n Theme,\n ThemeMode,\n ThemeManager,\n ThemeManagerOptions,\n ThemeSubscriber,\n} from \"./types\";\n\nimport { createPersistence } from \"./storage\";\nimport { getSystemTheme, subscribeSystemTheme } from \"./system\";\nimport { applyTheme } from \"./dom\";\n\nconst DEFAULT_OPTIONS: Required<ThemeManagerOptions> = {\n attribute: \"data-theme\",\n storageKey: \"theme-mode\",\n defaultMode: \"system\",\n};\n\nexport function createThemeManager(\n options: ThemeManagerOptions = {}\n): ThemeManager {\n const config = { ...DEFAULT_OPTIONS, ...options };\n\n const storage = createPersistence(config.storageKey);\n const subscribers = new Set<ThemeSubscriber>();\n\n let mode: ThemeMode = storage.get() ?? config.defaultMode;\n\n let theme: Theme = resolveTheme(mode);\n\n // Apply initial theme immediately\n applyTheme(theme, config.attribute);\n\n // Listen to OS theme changes only when needed\n let unsubscribeSystem: (() => void) | null = null;\n if (mode === \"system\") {\n unsubscribeSystem = subscribeSystemTheme(handleSystemChange);\n }\n\n function resolveTheme(m: ThemeMode): Theme {\n return m === \"system\" ? getSystemTheme() : m;\n }\n let notifying = false;\n\n function notify() {\n if (notifying) return;\n\n notifying = true;\n subscribers.forEach((fn) => fn(theme, mode));\n notifying = false;\n }\n\n function setMode(nextMode: ThemeMode) {\n if (nextMode === mode) return;\n\n mode = nextMode;\n storage.set(mode);\n\n if (unsubscribeSystem) {\n unsubscribeSystem();\n unsubscribeSystem = null;\n }\n\n theme = resolveTheme(mode);\n applyTheme(theme, config.attribute);\n\n if (mode === \"system\") {\n unsubscribeSystem = subscribeSystemTheme(handleSystemChange);\n }\n\n notify();\n }\n\n function handleSystemChange(nextTheme: Theme) {\n if (mode !== \"system\") return;\n if (theme === nextTheme) return;\n\n theme = nextTheme;\n applyTheme(theme, config.attribute);\n notify();\n }\n\n return {\n get() {\n return theme;\n },\n\n getMode() {\n return mode;\n },\n\n set(nextMode) {\n setMode(nextMode);\n },\n toggle() {\n setMode(theme === \"dark\" ? \"light\" : \"dark\");\n },\n\n subscribe(fn) {\n subscribers.add(fn);\n fn(theme, mode); // immediate sync\n return () => {\n subscribers.delete(fn);\n };\n },\n };\n}\n","import { useEffect, useState } from \"react\";\nimport type { Theme, ThemeMode, ThemeManagerOptions } from \"./types\";\nimport { createThemeManager } from \"./manager\";\n\ntype UseThemeResult = {\n theme: Theme;\n mode: ThemeMode;\n setTheme: (mode: ThemeMode) => void;\n toggleTheme: () => void;\n};\n\n/**\n * Create a React hook bound to a ThemeManager instance.\n *\n * IMPORTANT:\n * - Call this once (e.g. in a module or provider)\n * - Do NOT call inside components repeatedly\n */\nexport function createUseTheme(options?: ThemeManagerOptions) {\n const manager = createThemeManager(options);\n\n return function useTheme(): UseThemeResult {\n const [theme, setThemeState] = useState<Theme>(manager.get());\n const [mode, setModeState] = useState<ThemeMode>(manager.getMode());\n\n useEffect(() => {\n const unsubscribe = manager.subscribe((t, m) => {\n setThemeState(t);\n setModeState(m);\n });\n\n return unsubscribe;\n }, []);\n\n return {\n theme,\n mode,\n setTheme: (nextMode: ThemeMode) => manager.set(nextMode),\n toggleTheme: () => manager.toggle(),\n };\n };\n}"],"mappings":"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","names":[]}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
(function () {
|
|
2
|
-
try {
|
|
3
|
-
var key = "theme-mode";
|
|
4
|
-
var attribute = "data-theme";
|
|
5
|
-
|
|
6
|
-
var stored = localStorage.getItem(key);
|
|
7
|
-
var mode =
|
|
8
|
-
stored === "light" || stored === "dark" || stored === "system"
|
|
9
|
-
? stored
|
|
10
|
-
: "system";
|
|
11
|
-
|
|
12
|
-
var isDark =
|
|
13
|
-
window.matchMedia &&
|
|
14
|
-
window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
15
|
-
|
|
16
|
-
var theme = mode === "system" ? (isDark ? "dark" : "light") : mode;
|
|
17
|
-
|
|
18
|
-
document.documentElement.setAttribute(attribute, theme);
|
|
19
|
-
} catch (e) {
|
|
20
|
-
// fail silently
|
|
21
|
-
}
|
|
22
|
-
})();
|