@tamagui/next-theme 1.0.10 → 1.0.12
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/cjs/NextTheme.js +98 -68
- package/dist/cjs/NextTheme.js.map +3 -3
- package/dist/esm/NextTheme.js +98 -69
- package/dist/esm/NextTheme.js.map +2 -2
- package/package.json +3 -3
- package/src/NextTheme.tsx +119 -99
- package/types/NextTheme.d.ts +4 -2
package/dist/cjs/NextTheme.js
CHANGED
|
@@ -25,6 +25,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
25
25
|
var NextTheme_exports = {};
|
|
26
26
|
__export(NextTheme_exports, {
|
|
27
27
|
NextThemeProvider: () => NextThemeProvider,
|
|
28
|
+
Theme: () => Theme,
|
|
28
29
|
useRootTheme: () => useRootTheme,
|
|
29
30
|
useTheme: () => useTheme,
|
|
30
31
|
useThemeSetting: () => useThemeSetting
|
|
@@ -32,19 +33,12 @@ __export(NextTheme_exports, {
|
|
|
32
33
|
module.exports = __toCommonJS(NextTheme_exports);
|
|
33
34
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
34
35
|
var import_use_event = require("@tamagui/use-event");
|
|
35
|
-
var import_head = __toESM(require("next/head"));
|
|
36
36
|
var React = __toESM(require("react"));
|
|
37
37
|
var import_react = require("react");
|
|
38
38
|
const useIsomorphicLayoutEffect = typeof window !== "undefined" ? import_react.useLayoutEffect : import_react.useEffect;
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
set: (_) => {
|
|
43
|
-
},
|
|
44
|
-
themes: []
|
|
45
|
-
});
|
|
46
|
-
const useTheme = () => (0, import_react.useContext)(ThemeSettingContext);
|
|
47
|
-
const useThemeSetting = () => (0, import_react.useContext)(ThemeSettingContext);
|
|
39
|
+
const ThemeContext = (0, import_react.createContext)(null);
|
|
40
|
+
const useTheme = () => (0, import_react.useContext)(ThemeContext);
|
|
41
|
+
const useThemeSetting = () => (0, import_react.useContext)(ThemeContext);
|
|
48
42
|
const colorSchemes = ["light", "dark"];
|
|
49
43
|
const MEDIA = "(prefers-color-scheme: dark)";
|
|
50
44
|
const useRootTheme = () => {
|
|
@@ -60,7 +54,19 @@ const useRootTheme = () => {
|
|
|
60
54
|
}
|
|
61
55
|
return [val, setVal];
|
|
62
56
|
};
|
|
63
|
-
const NextThemeProvider = ({
|
|
57
|
+
const NextThemeProvider = (props) => {
|
|
58
|
+
const context = (0, import_react.useContext)(ThemeContext);
|
|
59
|
+
if (context) {
|
|
60
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, {
|
|
61
|
+
children: props.children
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Theme, {
|
|
65
|
+
...props
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
const Theme = ({
|
|
69
|
+
nonce,
|
|
64
70
|
forcedTheme,
|
|
65
71
|
disableTransitionOnChange = true,
|
|
66
72
|
enableSystem = true,
|
|
@@ -90,31 +96,29 @@ const NextThemeProvider = ({
|
|
|
90
96
|
});
|
|
91
97
|
const mediaListener = (0, import_react.useRef)(handleMediaQuery);
|
|
92
98
|
mediaListener.current = handleMediaQuery;
|
|
93
|
-
const handleChangeTheme = (0, import_use_event.useEvent)(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
} catch (e) {
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
if (theme2 === "system" && enableSystem) {
|
|
103
|
-
const resolved = getSystemTheme();
|
|
104
|
-
name = (value == null ? void 0 : value[resolved]) || resolved;
|
|
99
|
+
const handleChangeTheme = (0, import_use_event.useEvent)((theme2, updateStorage = true, updateDOM = true) => {
|
|
100
|
+
let name = (value == null ? void 0 : value[theme2]) || theme2;
|
|
101
|
+
if (updateStorage) {
|
|
102
|
+
try {
|
|
103
|
+
localStorage.setItem(storageKey, theme2);
|
|
104
|
+
} catch (e) {
|
|
105
105
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
106
|
+
}
|
|
107
|
+
if (theme2 === "system" && enableSystem) {
|
|
108
|
+
const resolved = getSystemTheme();
|
|
109
|
+
name = (value == null ? void 0 : value[resolved]) || resolved;
|
|
110
|
+
}
|
|
111
|
+
onChangeTheme == null ? void 0 : onChangeTheme(name.replace("t_", ""));
|
|
112
|
+
if (updateDOM) {
|
|
113
|
+
const d = document.documentElement;
|
|
114
|
+
if (attribute === "class") {
|
|
115
|
+
d.classList.remove(...attrs);
|
|
116
|
+
d.classList.add(name);
|
|
117
|
+
} else {
|
|
118
|
+
d.setAttribute(attribute, name);
|
|
115
119
|
}
|
|
116
120
|
}
|
|
117
|
-
);
|
|
121
|
+
});
|
|
118
122
|
useIsomorphicLayoutEffect(() => {
|
|
119
123
|
const handler = (...args) => mediaListener.current(...args);
|
|
120
124
|
const media = window.matchMedia(MEDIA);
|
|
@@ -184,19 +188,23 @@ const NextThemeProvider = ({
|
|
|
184
188
|
themes,
|
|
185
189
|
systemTheme
|
|
186
190
|
]);
|
|
187
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
191
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ThemeContext.Provider, {
|
|
188
192
|
value: contextValue,
|
|
189
193
|
children: [
|
|
190
194
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemeScript, {
|
|
191
195
|
...{
|
|
192
196
|
forcedTheme,
|
|
197
|
+
disableTransitionOnChange,
|
|
193
198
|
storageKey,
|
|
194
199
|
systemTheme: resolvedTheme,
|
|
195
200
|
attribute,
|
|
201
|
+
enableColorScheme,
|
|
196
202
|
value,
|
|
203
|
+
themes,
|
|
197
204
|
enableSystem,
|
|
198
205
|
defaultTheme,
|
|
199
|
-
attrs
|
|
206
|
+
attrs,
|
|
207
|
+
nonce
|
|
200
208
|
}
|
|
201
209
|
}),
|
|
202
210
|
(0, import_react.useMemo)(() => children, [children])
|
|
@@ -209,52 +217,73 @@ const ThemeScript = (0, import_react.memo)(
|
|
|
209
217
|
storageKey,
|
|
210
218
|
attribute,
|
|
211
219
|
enableSystem,
|
|
220
|
+
enableColorScheme,
|
|
212
221
|
defaultTheme,
|
|
213
222
|
value,
|
|
214
|
-
attrs
|
|
223
|
+
attrs,
|
|
224
|
+
nonce
|
|
215
225
|
}) => {
|
|
226
|
+
const defaultSystem = defaultTheme === "system";
|
|
216
227
|
const optimization = (() => {
|
|
217
228
|
if (attribute === "class") {
|
|
218
|
-
const removeClasses = attrs.map((t) => `
|
|
219
|
-
return `var d=document.documentElement.classList;${removeClasses};`;
|
|
229
|
+
const removeClasses = `c.remove(${attrs.map((t) => `'${t}'`).join(",")})`;
|
|
230
|
+
return `var d=document.documentElement,c=d.classList;${removeClasses};`;
|
|
220
231
|
} else {
|
|
221
|
-
return `var d=document.documentElement;`;
|
|
232
|
+
return `var d=document.documentElement,n='${attribute}',s='setAttribute';`;
|
|
222
233
|
}
|
|
223
234
|
})();
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if (attribute === "class") {
|
|
228
|
-
return `d.add(${val})`;
|
|
235
|
+
const fallbackColorScheme = (() => {
|
|
236
|
+
if (!enableColorScheme) {
|
|
237
|
+
return "";
|
|
229
238
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
239
|
+
const fallback = colorSchemes.includes(defaultTheme) ? defaultTheme : null;
|
|
240
|
+
if (fallback) {
|
|
241
|
+
return `if(e==='light'||e==='dark'||!e)d.style.colorScheme=e||'${defaultTheme}'`;
|
|
242
|
+
} else {
|
|
243
|
+
return `if(e==='light'||e==='dark')d.style.colorScheme=e`;
|
|
244
|
+
}
|
|
245
|
+
})();
|
|
246
|
+
const updateDOM = (name, literal = false, setColorScheme = true) => {
|
|
247
|
+
const resolvedName = value ? value[name] : name;
|
|
248
|
+
const val = literal ? name + `|| ''` : `'${resolvedName}'`;
|
|
249
|
+
let text = "";
|
|
250
|
+
if (enableColorScheme && setColorScheme && !literal && colorSchemes.includes(name)) {
|
|
251
|
+
text += `d.style.colorScheme = '${name}';`;
|
|
252
|
+
}
|
|
253
|
+
if (attribute === "class") {
|
|
254
|
+
if (literal || resolvedName) {
|
|
255
|
+
text += `c.add(${val})`;
|
|
256
|
+
} else {
|
|
257
|
+
text += `null`;
|
|
243
258
|
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
defaultTheme
|
|
248
|
-
)};}}catch(t){}}();`
|
|
259
|
+
} else {
|
|
260
|
+
if (resolvedName) {
|
|
261
|
+
text += `d[s](n,${val})`;
|
|
249
262
|
}
|
|
250
|
-
}
|
|
263
|
+
}
|
|
264
|
+
return text;
|
|
265
|
+
};
|
|
266
|
+
const scriptSrc = (() => {
|
|
267
|
+
if (forcedTheme) {
|
|
268
|
+
return `!function(){${optimization}${updateDOM(forcedTheme)}}()`;
|
|
269
|
+
}
|
|
270
|
+
if (enableSystem) {
|
|
271
|
+
return `!function(){try{${optimization}var e=localStorage.getItem('${storageKey}');if('system'===e||(!e&&${defaultSystem})){var t='${MEDIA}',m=window.matchMedia(t);if(m.media!==t||m.matches){${updateDOM(
|
|
272
|
+
"dark"
|
|
273
|
+
)}}else{${updateDOM("light")}}}else if(e){${value ? `var x=${JSON.stringify(value)};` : ""}${updateDOM(value ? `x[e]` : "e", true)}}${!defaultSystem ? `else{` + updateDOM(defaultTheme, false, false) + "}" : ""}${fallbackColorScheme}}catch(e){}}()`;
|
|
274
|
+
}
|
|
275
|
+
return `!function(){try{${optimization}var e=localStorage.getItem('${storageKey}');if(e){${value ? `var x=${JSON.stringify(value)};` : ""}${updateDOM(value ? `x[e]` : "e", true)}}else{${updateDOM(
|
|
276
|
+
defaultTheme,
|
|
277
|
+
false,
|
|
278
|
+
false
|
|
279
|
+
)};}${fallbackColorScheme}}catch(t){}}();`;
|
|
280
|
+
})();
|
|
281
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("script", {
|
|
282
|
+
nonce,
|
|
283
|
+
dangerouslySetInnerHTML: { __html: scriptSrc }
|
|
251
284
|
});
|
|
252
285
|
},
|
|
253
|
-
(
|
|
254
|
-
if (prevProps.forcedTheme !== nextProps.forcedTheme)
|
|
255
|
-
return false;
|
|
256
|
-
return true;
|
|
257
|
-
}
|
|
286
|
+
() => true
|
|
258
287
|
);
|
|
259
288
|
const getTheme = (key, fallback) => {
|
|
260
289
|
if (typeof window === "undefined")
|
|
@@ -277,6 +306,7 @@ const getSystemTheme = (e) => {
|
|
|
277
306
|
// Annotate the CommonJS export names for ESM import in node:
|
|
278
307
|
0 && (module.exports = {
|
|
279
308
|
NextThemeProvider,
|
|
309
|
+
Theme,
|
|
280
310
|
useRootTheme,
|
|
281
311
|
useTheme,
|
|
282
312
|
useThemeSetting
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/NextTheme.tsx"],
|
|
4
|
-
"sourcesContent": ["// https://raw.githubusercontent.com/pacocoursey/next-themes/master/index.tsx\n// forked temporarily due to buggy theme change\n\nimport { useEvent } from '@tamagui/use-event'\nimport NextHead from 'next/head'\nimport * as React from 'react'\nimport {\n createContext,\n memo,\n useContext,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nconst useIsomorphicLayoutEffect =\n typeof window !== 'undefined' ? useLayoutEffect : useEffect\n\nexport interface UseThemeProps {\n /** List of all available theme names */\n themes: string[]\n /** Forced theme name for the current page */\n forcedTheme?: string\n /** Update the theme */\n set: (theme: string) => void\n toggle: () => void\n /** Active theme name - will return \"system\" if not overriden, see \"resolvedTheme\" for getting resolved system value */\n current?: string\n /** @deprecated Use `current` instead (deprecating avoid confusion with useTheme) */\n theme?: string\n /** If `enableSystem` is true and the active theme is \"system\", this returns whether the system preference resolved to \"dark\" or \"light\". Otherwise, identical to `theme` */\n resolvedTheme?: string\n /** If enableSystem is true, returns the System theme preference (\"dark\" or \"light\"), regardless what the active theme is */\n systemTheme?: 'dark' | 'light'\n}\n\nexport interface ThemeProviderProps {\n children?: any\n /** List of all available theme names */\n themes?: string[]\n /** Forced theme name for the current page */\n forcedTheme?: string\n /** Whether to switch between dark and light themes based on prefers-color-scheme */\n enableSystem?: boolean\n systemTheme?: string\n /** Disable all CSS transitions when switching themes */\n disableTransitionOnChange?: boolean\n /** Whether to indicate to browsers which color scheme is used (dark or light) for built-in UI like inputs and buttons */\n enableColorScheme?: boolean\n /** Key used to store theme setting in localStorage */\n storageKey?: string\n /** Default theme name (for v0.0.12 and lower the default was light). If `enableSystem` is false, the default theme is light */\n defaultTheme?: string\n /** HTML attribute modified based on the active theme. Accepts `class` and `data-*` (meaning any data attribute, `data-mode`, `data-color`, etc.) */\n attribute?: string | 'class'\n /** Mapping of theme name to HTML attribute value. Object where key is the theme name and value is the attribute value */\n value?: ValueObject\n onChangeTheme?: (name: string) => void\n}\n\nconst ThemeSettingContext = createContext<UseThemeProps>({\n toggle: () => {},\n set: (_) => {},\n themes: [],\n})\n\n/**\n * @deprecated renamed to `useThemeSetting` to avoid confusion with core `useTheme` hook\n */\nexport const useTheme = () => useContext(ThemeSettingContext)\n\nexport const useThemeSetting = () => useContext(ThemeSettingContext)\n\nconst colorSchemes = ['light', 'dark']\nconst MEDIA = '(prefers-color-scheme: dark)'\n\ninterface ValueObject {\n [themeName: string]: string\n}\n\n// note this only works for light being default for now...\nexport const useRootTheme = () => {\n const [val, setVal] = useState('light')\n\n if (typeof document !== 'undefined') {\n useLayoutEffect(() => {\n // @ts-ignore\n const classes = [...document.documentElement.classList]\n const isDark = classes.includes('t_dark')\n React.startTransition(() => {\n setVal(isDark ? 'dark' : 'light')\n })\n }, [])\n }\n\n return [val, setVal] as const\n}\n\nexport const NextThemeProvider: React.FC<ThemeProviderProps> = ({\n forcedTheme,\n disableTransitionOnChange = true,\n enableSystem = true,\n enableColorScheme = true,\n storageKey = 'theme',\n themes = colorSchemes,\n defaultTheme = enableSystem ? 'system' : 'light',\n attribute = 'class',\n onChangeTheme,\n value = {\n dark: 't_dark',\n light: 't_light',\n },\n children,\n}) => {\n const [theme, setThemeState] = useState(() => getTheme(storageKey, defaultTheme))\n const [resolvedTheme, setResolvedTheme] = useState(() => getTheme(storageKey))\n // const resolvedTheme = React.useDeferredValue(resolvedThemeFast)\n const attrs = !value ? themes : Object.values(value)\n\n const handleMediaQuery = useEvent((e?) => {\n const systemTheme = getSystemTheme(e)\n React.startTransition(() => {\n setResolvedTheme(systemTheme)\n })\n if (theme === 'system' && !forcedTheme) {\n handleChangeTheme(systemTheme, false)\n }\n })\n\n // Ref hack to avoid adding handleMediaQuery as a dep\n const mediaListener = useRef(handleMediaQuery)\n mediaListener.current = handleMediaQuery\n\n const handleChangeTheme = useEvent(\n (theme, updateStorage = true, updateDOM = true) => {\n let name = value?.[theme] || theme\n\n if (updateStorage) {\n try {\n localStorage.setItem(storageKey, theme)\n } catch (e) {\n // Unsupported\n }\n }\n\n if (theme === 'system' && enableSystem) {\n const resolved = getSystemTheme()\n name = value?.[resolved] || resolved\n }\n\n onChangeTheme?.(name.replace('t_', ''))\n\n if (updateDOM) {\n const d = document.documentElement\n if (attribute === 'class') {\n d.classList.remove(...attrs)\n d.classList.add(name)\n } else {\n d.setAttribute(attribute, name)\n }\n }\n },\n )\n\n useIsomorphicLayoutEffect(() => {\n const handler = (...args: any) => mediaListener.current(...args)\n // Always listen to System preference\n const media = window.matchMedia(MEDIA)\n // Intentionally use deprecated listener methods to support iOS & old browsers\n media.addListener(handler)\n handler(media)\n return () => {\n media.removeListener(handler)\n }\n }, [])\n\n const set = useEvent((newTheme) => {\n if (forcedTheme) {\n handleChangeTheme(newTheme, true, false)\n } else {\n handleChangeTheme(newTheme)\n }\n setThemeState(newTheme)\n })\n\n // localStorage event handling\n useEffect(() => {\n const handleStorage = (e: StorageEvent) => {\n if (e.key !== storageKey) {\n return\n }\n // If default theme set, use it if localstorage === null (happens on local storage manual deletion)\n const theme = e.newValue || defaultTheme\n set(theme)\n }\n window.addEventListener('storage', handleStorage)\n return () => {\n window.removeEventListener('storage', handleStorage)\n }\n }, [defaultTheme, set, storageKey])\n\n // color-scheme handling\n useIsomorphicLayoutEffect(() => {\n if (!enableColorScheme) return\n\n const colorScheme =\n // If theme is forced to light or dark, use that\n forcedTheme && colorSchemes.includes(forcedTheme)\n ? forcedTheme\n : // If regular theme is light or dark\n theme && colorSchemes.includes(theme)\n ? theme\n : // If theme is system, use the resolved version\n theme === 'system'\n ? resolvedTheme || null\n : null\n\n // color-scheme tells browser how to render built-in elements like forms, scrollbars, etc.\n // if color-scheme is null, this will remove the property\n const userPrefers =\n typeof window !== 'undefined' &&\n window.matchMedia &&\n window.matchMedia('(prefers-color-scheme: dark)').matches\n ? 'dark'\n : 'light'\n\n const wePrefer = colorScheme || 'light'\n\n // avoid running this because it causes full page reflow\n if (userPrefers !== wePrefer) {\n document.documentElement.style.setProperty('color-scheme', colorScheme)\n }\n }, [enableColorScheme, theme, resolvedTheme, forcedTheme])\n\n const toggle = useEvent(() => {\n const order =\n resolvedTheme === 'dark'\n ? ['system', 'light', 'dark']\n : ['system', 'dark', 'light']\n const next = order[(order.indexOf(theme) + 1) % order.length]\n set(next)\n })\n\n const contextResolvedTheme = theme === 'system' ? resolvedTheme : theme\n const systemTheme = (enableSystem ? resolvedTheme : undefined) as\n | 'light'\n | 'dark'\n | undefined\n const contextValue = useMemo(() => {\n const value: UseThemeProps = {\n theme,\n current: theme,\n set,\n toggle,\n forcedTheme,\n resolvedTheme: contextResolvedTheme,\n themes: enableSystem ? [...themes, 'system'] : themes,\n systemTheme,\n } as const\n return value\n }, [\n theme,\n set,\n toggle,\n forcedTheme,\n contextResolvedTheme,\n enableSystem,\n themes,\n systemTheme,\n ])\n\n return (\n <ThemeSettingContext.Provider value={contextValue}>\n <ThemeScript\n {...{\n forcedTheme,\n storageKey,\n systemTheme: resolvedTheme,\n attribute,\n value,\n enableSystem,\n defaultTheme,\n attrs,\n }}\n />\n {/* because on SSR we re-run and can avoid whole tree re-render */}\n {useMemo(() => children, [children])}\n </ThemeSettingContext.Provider>\n )\n}\n\nconst ThemeScript = memo(\n ({\n forcedTheme,\n storageKey,\n attribute,\n enableSystem,\n defaultTheme,\n value,\n attrs,\n }: {\n forcedTheme?: string\n storageKey: string\n attribute?: string\n enableSystem?: boolean\n defaultTheme: string\n value?: ValueObject\n attrs: any\n }) => {\n // Code-golfing the amount of characters in the script\n const optimization = (() => {\n if (attribute === 'class') {\n const removeClasses = attrs.map((t: string) => `d.remove('${t}')`).join(';')\n return `var d=document.documentElement.classList;${removeClasses};`\n } else {\n return `var d=document.documentElement;`\n }\n })()\n\n const updateDOM = (name: string, literal?: boolean) => {\n name = value?.[name] || name\n const val = literal ? name : `'${name}'`\n\n if (attribute === 'class') {\n return `d.add(${val})`\n }\n\n return `d.setAttribute('${attribute}', ${val})`\n }\n\n const defaultSystem = defaultTheme === 'system'\n\n return (\n <NextHead>\n {forcedTheme ? (\n <script\n key=\"next-themes-script\"\n dangerouslySetInnerHTML={{\n // These are minified via Terser and then updated by hand, don't recommend\n __html: `!function(){${optimization}${updateDOM(forcedTheme)}}()`,\n }}\n />\n ) : enableSystem ? (\n <script\n key=\"next-themes-script\"\n dangerouslySetInnerHTML={{\n __html: `!function(){try {${optimization}var e=localStorage.getItem('${storageKey}');${\n !defaultSystem ? updateDOM(defaultTheme) + ';' : ''\n }if(\"system\"===e||(!e&&${defaultSystem})){var t=\"${MEDIA}\",m=window.matchMedia(t);m.media!==t||m.matches?${updateDOM(\n 'dark',\n )}:${updateDOM('light')}}else if(e) ${\n value ? `var x=${JSON.stringify(value)};` : ''\n }${updateDOM(value ? 'x[e]' : 'e', true)}}catch(e){}}()`,\n }}\n />\n ) : (\n <script\n key=\"next-themes-script\"\n dangerouslySetInnerHTML={{\n __html: `!function(){try{${optimization}var e=localStorage.getItem(\"${storageKey}\");if(e){${\n value ? `var x=${JSON.stringify(value)};` : ''\n }${updateDOM(value ? 'x[e]' : 'e', true)}}else{${updateDOM(\n defaultTheme,\n )};}}catch(t){}}();`,\n }}\n />\n )}\n </NextHead>\n )\n },\n (prevProps, nextProps) => {\n // Only re-render when forcedTheme changes\n // the rest of the props should be completely stable\n if (prevProps.forcedTheme !== nextProps.forcedTheme) return false\n return true\n },\n)\n\n// Helpers\nconst getTheme = (key: string, fallback?: string) => {\n if (typeof window === 'undefined') return undefined\n let theme\n try {\n theme = localStorage.getItem(key) || undefined\n } catch (e) {\n // Unsupported\n }\n return theme || fallback\n}\n\nconst getSystemTheme = (e?: MediaQueryList) => {\n if (!e) {\n e = window.matchMedia(MEDIA)\n }\n\n const isDark = e.matches\n const systemTheme = isDark ? 'dark' : 'light'\n return systemTheme\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
6
|
-
"names": ["systemTheme", "theme", "value"
|
|
4
|
+
"sourcesContent": ["// https://raw.githubusercontent.com/pacocoursey/next-themes/master/index.tsx\n// forked temporarily due to buggy theme change\n\nimport { useEvent } from '@tamagui/use-event'\nimport * as React from 'react'\nimport {\n createContext,\n memo,\n useContext,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nconst useIsomorphicLayoutEffect =\n typeof window !== 'undefined' ? useLayoutEffect : useEffect\n\nexport interface UseThemeProps {\n /** List of all available theme names */\n themes: string[]\n /** Forced theme name for the current page */\n forcedTheme?: string\n /** Update the theme */\n set: (theme: string) => void\n toggle: () => void\n /** Active theme name - will return \"system\" if not overriden, see \"resolvedTheme\" for getting resolved system value */\n current?: string\n /** @deprecated Use `current` instead (deprecating avoid confusion with useTheme) */\n theme?: string\n /** If `enableSystem` is true and the active theme is \"system\", this returns whether the system preference resolved to \"dark\" or \"light\". Otherwise, identical to `theme` */\n resolvedTheme?: string\n /** If enableSystem is true, returns the System theme preference (\"dark\" or \"light\"), regardless what the active theme is */\n systemTheme?: 'dark' | 'light'\n}\n\nexport interface ThemeProviderProps {\n children?: any\n /** List of all available theme names */\n themes?: string[]\n /** Forced theme name for the current page */\n forcedTheme?: string\n /** Whether to switch between dark and light themes based on prefers-color-scheme */\n enableSystem?: boolean\n systemTheme?: string\n /** Disable all CSS transitions when switching themes */\n disableTransitionOnChange?: boolean\n /** Whether to indicate to browsers which color scheme is used (dark or light) for built-in UI like inputs and buttons */\n enableColorScheme?: boolean\n /** Key used to store theme setting in localStorage */\n storageKey?: string\n /** Default theme name (for v0.0.12 and lower the default was light). If `enableSystem` is false, the default theme is light */\n defaultTheme?: string\n /** HTML attribute modified based on the active theme. Accepts `class` and `data-*` (meaning any data attribute, `data-mode`, `data-color`, etc.) */\n attribute?: string | 'class'\n /** Mapping of theme name to HTML attribute value. Object where key is the theme name and value is the attribute value */\n value?: ValueObject\n onChangeTheme?: (name: string) => void\n nonce?: string\n}\n\nconst ThemeContext = createContext<UseThemeProps | null>(null)\n\n/**\n * @deprecated renamed to `useThemeSetting` to avoid confusion with core `useTheme` hook\n */\nexport const useTheme = () => useContext(ThemeContext)\n\nexport const useThemeSetting = () => useContext(ThemeContext)\n\nconst colorSchemes = ['light', 'dark']\nconst MEDIA = '(prefers-color-scheme: dark)'\n\ninterface ValueObject {\n [themeName: string]: string\n}\n\n// note this only works for light being default for now...\nexport const useRootTheme = () => {\n const [val, setVal] = useState('light')\n\n if (typeof document !== 'undefined') {\n useLayoutEffect(() => {\n // @ts-ignore\n const classes = [...document.documentElement.classList]\n const isDark = classes.includes('t_dark')\n React.startTransition(() => {\n setVal(isDark ? 'dark' : 'light')\n })\n }, [])\n }\n\n return [val, setVal] as const\n}\n\nexport const NextThemeProvider: React.FC<ThemeProviderProps> = (props) => {\n const context = useContext(ThemeContext)\n // Ignore nested context providers, just passthrough children\n if (context) {\n return <>{props.children}</>\n }\n return <Theme {...props} />\n}\n\nexport const Theme: React.FC<ThemeProviderProps> = ({\n nonce,\n forcedTheme,\n disableTransitionOnChange = true,\n enableSystem = true,\n enableColorScheme = true,\n storageKey = 'theme',\n themes = colorSchemes,\n defaultTheme = enableSystem ? 'system' : 'light',\n attribute = 'class',\n onChangeTheme,\n value = {\n dark: 't_dark',\n light: 't_light',\n },\n children,\n}) => {\n const [theme, setThemeState] = useState(() => getTheme(storageKey, defaultTheme))\n const [resolvedTheme, setResolvedTheme] = useState(() => getTheme(storageKey))\n // const resolvedTheme = React.useDeferredValue(resolvedThemeFast)\n const attrs = !value ? themes : Object.values(value)\n\n const handleMediaQuery = useEvent((e?) => {\n const systemTheme = getSystemTheme(e)\n React.startTransition(() => {\n setResolvedTheme(systemTheme)\n })\n if (theme === 'system' && !forcedTheme) {\n handleChangeTheme(systemTheme, false)\n }\n })\n\n // Ref hack to avoid adding handleMediaQuery as a dep\n const mediaListener = useRef(handleMediaQuery)\n mediaListener.current = handleMediaQuery\n\n const handleChangeTheme = useEvent((theme, updateStorage = true, updateDOM = true) => {\n let name = value?.[theme] || theme\n\n if (updateStorage) {\n try {\n localStorage.setItem(storageKey, theme)\n } catch (e) {\n // Unsupported\n }\n }\n\n if (theme === 'system' && enableSystem) {\n const resolved = getSystemTheme()\n name = value?.[resolved] || resolved\n }\n\n onChangeTheme?.(name.replace('t_', ''))\n\n if (updateDOM) {\n const d = document.documentElement\n if (attribute === 'class') {\n d.classList.remove(...attrs)\n d.classList.add(name)\n } else {\n d.setAttribute(attribute, name)\n }\n }\n })\n\n useIsomorphicLayoutEffect(() => {\n const handler = (...args: any) => mediaListener.current(...args)\n // Always listen to System preference\n const media = window.matchMedia(MEDIA)\n // Intentionally use deprecated listener methods to support iOS & old browsers\n media.addListener(handler)\n handler(media)\n return () => {\n media.removeListener(handler)\n }\n }, [])\n\n const set = useEvent((newTheme) => {\n if (forcedTheme) {\n handleChangeTheme(newTheme, true, false)\n } else {\n handleChangeTheme(newTheme)\n }\n setThemeState(newTheme)\n })\n\n // localStorage event handling\n useEffect(() => {\n const handleStorage = (e: StorageEvent) => {\n if (e.key !== storageKey) {\n return\n }\n // If default theme set, use it if localstorage === null (happens on local storage manual deletion)\n const theme = e.newValue || defaultTheme\n set(theme)\n }\n window.addEventListener('storage', handleStorage)\n return () => {\n window.removeEventListener('storage', handleStorage)\n }\n }, [defaultTheme, set, storageKey])\n\n // color-scheme handling\n useIsomorphicLayoutEffect(() => {\n if (!enableColorScheme) return\n\n const colorScheme =\n // If theme is forced to light or dark, use that\n forcedTheme && colorSchemes.includes(forcedTheme)\n ? forcedTheme\n : // If regular theme is light or dark\n theme && colorSchemes.includes(theme)\n ? theme\n : // If theme is system, use the resolved version\n theme === 'system'\n ? resolvedTheme || null\n : null\n\n // color-scheme tells browser how to render built-in elements like forms, scrollbars, etc.\n // if color-scheme is null, this will remove the property\n const userPrefers =\n typeof window !== 'undefined' &&\n window.matchMedia &&\n window.matchMedia('(prefers-color-scheme: dark)').matches\n ? 'dark'\n : 'light'\n\n const wePrefer = colorScheme || 'light'\n\n // avoid running this because it causes full page reflow\n if (userPrefers !== wePrefer) {\n document.documentElement.style.setProperty('color-scheme', colorScheme)\n }\n }, [enableColorScheme, theme, resolvedTheme, forcedTheme])\n\n const toggle = useEvent(() => {\n const order =\n resolvedTheme === 'dark' ? ['system', 'light', 'dark'] : ['system', 'dark', 'light']\n const next = order[(order.indexOf(theme) + 1) % order.length]\n set(next)\n })\n\n const contextResolvedTheme = theme === 'system' ? resolvedTheme : theme\n const systemTheme = (enableSystem ? resolvedTheme : undefined) as\n | 'light'\n | 'dark'\n | undefined\n const contextValue = useMemo(() => {\n const value: UseThemeProps = {\n theme,\n current: theme,\n set,\n toggle,\n forcedTheme,\n resolvedTheme: contextResolvedTheme,\n themes: enableSystem ? [...themes, 'system'] : themes,\n systemTheme,\n } as const\n return value\n }, [\n theme,\n set,\n toggle,\n forcedTheme,\n contextResolvedTheme,\n enableSystem,\n themes,\n systemTheme,\n ])\n\n return (\n <ThemeContext.Provider value={contextValue}>\n <ThemeScript\n {...{\n forcedTheme,\n disableTransitionOnChange,\n storageKey,\n systemTheme: resolvedTheme,\n attribute,\n enableColorScheme,\n value,\n themes,\n enableSystem,\n defaultTheme,\n attrs,\n nonce,\n }}\n />\n {/* because on SSR we re-run and can avoid whole tree re-render */}\n {useMemo(() => children, [children])}\n </ThemeContext.Provider>\n )\n}\n\nconst ThemeScript = memo(\n ({\n forcedTheme,\n storageKey,\n attribute,\n enableSystem,\n enableColorScheme,\n defaultTheme,\n value,\n attrs,\n nonce,\n }: ThemeProviderProps & { attrs: string[]; defaultTheme: string }) => {\n const defaultSystem = defaultTheme === 'system'\n\n // Code-golfing the amount of characters in the script\n const optimization = (() => {\n if (attribute === 'class') {\n const removeClasses = `c.remove(${attrs.map((t: string) => `'${t}'`).join(',')})`\n\n return `var d=document.documentElement,c=d.classList;${removeClasses};`\n } else {\n return `var d=document.documentElement,n='${attribute}',s='setAttribute';`\n }\n })()\n\n const fallbackColorScheme = (() => {\n if (!enableColorScheme) {\n return ''\n }\n\n const fallback = colorSchemes.includes(defaultTheme) ? defaultTheme : null\n\n if (fallback) {\n return `if(e==='light'||e==='dark'||!e)d.style.colorScheme=e||'${defaultTheme}'`\n } else {\n return `if(e==='light'||e==='dark')d.style.colorScheme=e`\n }\n })()\n\n const updateDOM = (name: string, literal: boolean = false, setColorScheme = true) => {\n const resolvedName = value ? value[name] : name\n const val = literal ? name + `|| ''` : `'${resolvedName}'`\n let text = ''\n\n // MUCH faster to set colorScheme alongside HTML attribute/class\n // as it only incurs 1 style recalculation rather than 2\n // This can save over 250ms of work for pages with big DOM\n if (\n enableColorScheme &&\n setColorScheme &&\n !literal &&\n colorSchemes.includes(name)\n ) {\n text += `d.style.colorScheme = '${name}';`\n }\n\n if (attribute === 'class') {\n if (literal || resolvedName) {\n text += `c.add(${val})`\n } else {\n text += `null`\n }\n } else {\n if (resolvedName) {\n text += `d[s](n,${val})`\n }\n }\n\n return text\n }\n\n const scriptSrc = (() => {\n if (forcedTheme) {\n return `!function(){${optimization}${updateDOM(forcedTheme)}}()`\n }\n\n if (enableSystem) {\n return `!function(){try{${optimization}var e=localStorage.getItem('${storageKey}');if('system'===e||(!e&&${defaultSystem})){var t='${MEDIA}',m=window.matchMedia(t);if(m.media!==t||m.matches){${updateDOM(\n 'dark'\n )}}else{${updateDOM('light')}}}else if(e){${\n value ? `var x=${JSON.stringify(value)};` : ''\n }${updateDOM(value ? `x[e]` : 'e', true)}}${\n !defaultSystem ? `else{` + updateDOM(defaultTheme, false, false) + '}' : ''\n }${fallbackColorScheme}}catch(e){}}()`\n }\n\n return `!function(){try{${optimization}var e=localStorage.getItem('${storageKey}');if(e){${\n value ? `var x=${JSON.stringify(value)};` : ''\n }${updateDOM(value ? `x[e]` : 'e', true)}}else{${updateDOM(\n defaultTheme,\n false,\n false\n )};}${fallbackColorScheme}}catch(t){}}();`\n })()\n\n return <script nonce={nonce} dangerouslySetInnerHTML={{ __html: scriptSrc }} />\n },\n // Never re-render this component\n () => true\n)\n\n// Helpers\nconst getTheme = (key: string, fallback?: string) => {\n if (typeof window === 'undefined') return undefined\n let theme\n try {\n theme = localStorage.getItem(key) || undefined\n } catch (e) {\n // Unsupported\n }\n return theme || fallback\n}\n\nconst getSystemTheme = (e?: MediaQueryList) => {\n if (!e) {\n e = window.matchMedia(MEDIA)\n }\n\n const isDark = e.matches\n const systemTheme = isDark ? 'dark' : 'light'\n return systemTheme\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["systemTheme", "theme", "value"]
|
|
7
7
|
}
|
package/dist/esm/NextTheme.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEvent } from "@tamagui/use-event";
|
|
3
|
-
import NextHead from "next/head";
|
|
4
3
|
import * as React from "react";
|
|
5
4
|
import {
|
|
6
5
|
createContext,
|
|
@@ -13,15 +12,9 @@ import {
|
|
|
13
12
|
useState
|
|
14
13
|
} from "react";
|
|
15
14
|
const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
set: (_) => {
|
|
20
|
-
},
|
|
21
|
-
themes: []
|
|
22
|
-
});
|
|
23
|
-
const useTheme = () => useContext(ThemeSettingContext);
|
|
24
|
-
const useThemeSetting = () => useContext(ThemeSettingContext);
|
|
15
|
+
const ThemeContext = createContext(null);
|
|
16
|
+
const useTheme = () => useContext(ThemeContext);
|
|
17
|
+
const useThemeSetting = () => useContext(ThemeContext);
|
|
25
18
|
const colorSchemes = ["light", "dark"];
|
|
26
19
|
const MEDIA = "(prefers-color-scheme: dark)";
|
|
27
20
|
const useRootTheme = () => {
|
|
@@ -37,7 +30,19 @@ const useRootTheme = () => {
|
|
|
37
30
|
}
|
|
38
31
|
return [val, setVal];
|
|
39
32
|
};
|
|
40
|
-
const NextThemeProvider = ({
|
|
33
|
+
const NextThemeProvider = (props) => {
|
|
34
|
+
const context = useContext(ThemeContext);
|
|
35
|
+
if (context) {
|
|
36
|
+
return /* @__PURE__ */ jsx(Fragment, {
|
|
37
|
+
children: props.children
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return /* @__PURE__ */ jsx(Theme, {
|
|
41
|
+
...props
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
const Theme = ({
|
|
45
|
+
nonce,
|
|
41
46
|
forcedTheme,
|
|
42
47
|
disableTransitionOnChange = true,
|
|
43
48
|
enableSystem = true,
|
|
@@ -67,31 +72,29 @@ const NextThemeProvider = ({
|
|
|
67
72
|
});
|
|
68
73
|
const mediaListener = useRef(handleMediaQuery);
|
|
69
74
|
mediaListener.current = handleMediaQuery;
|
|
70
|
-
const handleChangeTheme = useEvent(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
} catch (e) {
|
|
77
|
-
}
|
|
75
|
+
const handleChangeTheme = useEvent((theme2, updateStorage = true, updateDOM = true) => {
|
|
76
|
+
let name = (value == null ? void 0 : value[theme2]) || theme2;
|
|
77
|
+
if (updateStorage) {
|
|
78
|
+
try {
|
|
79
|
+
localStorage.setItem(storageKey, theme2);
|
|
80
|
+
} catch (e) {
|
|
78
81
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
82
|
+
}
|
|
83
|
+
if (theme2 === "system" && enableSystem) {
|
|
84
|
+
const resolved = getSystemTheme();
|
|
85
|
+
name = (value == null ? void 0 : value[resolved]) || resolved;
|
|
86
|
+
}
|
|
87
|
+
onChangeTheme == null ? void 0 : onChangeTheme(name.replace("t_", ""));
|
|
88
|
+
if (updateDOM) {
|
|
89
|
+
const d = document.documentElement;
|
|
90
|
+
if (attribute === "class") {
|
|
91
|
+
d.classList.remove(...attrs);
|
|
92
|
+
d.classList.add(name);
|
|
93
|
+
} else {
|
|
94
|
+
d.setAttribute(attribute, name);
|
|
92
95
|
}
|
|
93
96
|
}
|
|
94
|
-
);
|
|
97
|
+
});
|
|
95
98
|
useIsomorphicLayoutEffect(() => {
|
|
96
99
|
const handler = (...args) => mediaListener.current(...args);
|
|
97
100
|
const media = window.matchMedia(MEDIA);
|
|
@@ -161,19 +164,23 @@ const NextThemeProvider = ({
|
|
|
161
164
|
themes,
|
|
162
165
|
systemTheme
|
|
163
166
|
]);
|
|
164
|
-
return /* @__PURE__ */ jsxs(
|
|
167
|
+
return /* @__PURE__ */ jsxs(ThemeContext.Provider, {
|
|
165
168
|
value: contextValue,
|
|
166
169
|
children: [
|
|
167
170
|
/* @__PURE__ */ jsx(ThemeScript, {
|
|
168
171
|
...{
|
|
169
172
|
forcedTheme,
|
|
173
|
+
disableTransitionOnChange,
|
|
170
174
|
storageKey,
|
|
171
175
|
systemTheme: resolvedTheme,
|
|
172
176
|
attribute,
|
|
177
|
+
enableColorScheme,
|
|
173
178
|
value,
|
|
179
|
+
themes,
|
|
174
180
|
enableSystem,
|
|
175
181
|
defaultTheme,
|
|
176
|
-
attrs
|
|
182
|
+
attrs,
|
|
183
|
+
nonce
|
|
177
184
|
}
|
|
178
185
|
}),
|
|
179
186
|
useMemo(() => children, [children])
|
|
@@ -186,52 +193,73 @@ const ThemeScript = memo(
|
|
|
186
193
|
storageKey,
|
|
187
194
|
attribute,
|
|
188
195
|
enableSystem,
|
|
196
|
+
enableColorScheme,
|
|
189
197
|
defaultTheme,
|
|
190
198
|
value,
|
|
191
|
-
attrs
|
|
199
|
+
attrs,
|
|
200
|
+
nonce
|
|
192
201
|
}) => {
|
|
202
|
+
const defaultSystem = defaultTheme === "system";
|
|
193
203
|
const optimization = (() => {
|
|
194
204
|
if (attribute === "class") {
|
|
195
|
-
const removeClasses = attrs.map((t) => `
|
|
196
|
-
return `var d=document.documentElement.classList;${removeClasses};`;
|
|
205
|
+
const removeClasses = `c.remove(${attrs.map((t) => `'${t}'`).join(",")})`;
|
|
206
|
+
return `var d=document.documentElement,c=d.classList;${removeClasses};`;
|
|
197
207
|
} else {
|
|
198
|
-
return `var d=document.documentElement;`;
|
|
208
|
+
return `var d=document.documentElement,n='${attribute}',s='setAttribute';`;
|
|
199
209
|
}
|
|
200
210
|
})();
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (attribute === "class") {
|
|
205
|
-
return `d.add(${val})`;
|
|
211
|
+
const fallbackColorScheme = (() => {
|
|
212
|
+
if (!enableColorScheme) {
|
|
213
|
+
return "";
|
|
206
214
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
215
|
+
const fallback = colorSchemes.includes(defaultTheme) ? defaultTheme : null;
|
|
216
|
+
if (fallback) {
|
|
217
|
+
return `if(e==='light'||e==='dark'||!e)d.style.colorScheme=e||'${defaultTheme}'`;
|
|
218
|
+
} else {
|
|
219
|
+
return `if(e==='light'||e==='dark')d.style.colorScheme=e`;
|
|
220
|
+
}
|
|
221
|
+
})();
|
|
222
|
+
const updateDOM = (name, literal = false, setColorScheme = true) => {
|
|
223
|
+
const resolvedName = value ? value[name] : name;
|
|
224
|
+
const val = literal ? name + `|| ''` : `'${resolvedName}'`;
|
|
225
|
+
let text = "";
|
|
226
|
+
if (enableColorScheme && setColorScheme && !literal && colorSchemes.includes(name)) {
|
|
227
|
+
text += `d.style.colorScheme = '${name}';`;
|
|
228
|
+
}
|
|
229
|
+
if (attribute === "class") {
|
|
230
|
+
if (literal || resolvedName) {
|
|
231
|
+
text += `c.add(${val})`;
|
|
232
|
+
} else {
|
|
233
|
+
text += `null`;
|
|
220
234
|
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
defaultTheme
|
|
225
|
-
)};}}catch(t){}}();`
|
|
235
|
+
} else {
|
|
236
|
+
if (resolvedName) {
|
|
237
|
+
text += `d[s](n,${val})`;
|
|
226
238
|
}
|
|
227
|
-
}
|
|
239
|
+
}
|
|
240
|
+
return text;
|
|
241
|
+
};
|
|
242
|
+
const scriptSrc = (() => {
|
|
243
|
+
if (forcedTheme) {
|
|
244
|
+
return `!function(){${optimization}${updateDOM(forcedTheme)}}()`;
|
|
245
|
+
}
|
|
246
|
+
if (enableSystem) {
|
|
247
|
+
return `!function(){try{${optimization}var e=localStorage.getItem('${storageKey}');if('system'===e||(!e&&${defaultSystem})){var t='${MEDIA}',m=window.matchMedia(t);if(m.media!==t||m.matches){${updateDOM(
|
|
248
|
+
"dark"
|
|
249
|
+
)}}else{${updateDOM("light")}}}else if(e){${value ? `var x=${JSON.stringify(value)};` : ""}${updateDOM(value ? `x[e]` : "e", true)}}${!defaultSystem ? `else{` + updateDOM(defaultTheme, false, false) + "}" : ""}${fallbackColorScheme}}catch(e){}}()`;
|
|
250
|
+
}
|
|
251
|
+
return `!function(){try{${optimization}var e=localStorage.getItem('${storageKey}');if(e){${value ? `var x=${JSON.stringify(value)};` : ""}${updateDOM(value ? `x[e]` : "e", true)}}else{${updateDOM(
|
|
252
|
+
defaultTheme,
|
|
253
|
+
false,
|
|
254
|
+
false
|
|
255
|
+
)};}${fallbackColorScheme}}catch(t){}}();`;
|
|
256
|
+
})();
|
|
257
|
+
return /* @__PURE__ */ jsx("script", {
|
|
258
|
+
nonce,
|
|
259
|
+
dangerouslySetInnerHTML: { __html: scriptSrc }
|
|
228
260
|
});
|
|
229
261
|
},
|
|
230
|
-
(
|
|
231
|
-
if (prevProps.forcedTheme !== nextProps.forcedTheme)
|
|
232
|
-
return false;
|
|
233
|
-
return true;
|
|
234
|
-
}
|
|
262
|
+
() => true
|
|
235
263
|
);
|
|
236
264
|
const getTheme = (key, fallback) => {
|
|
237
265
|
if (typeof window === "undefined")
|
|
@@ -253,6 +281,7 @@ const getSystemTheme = (e) => {
|
|
|
253
281
|
};
|
|
254
282
|
export {
|
|
255
283
|
NextThemeProvider,
|
|
284
|
+
Theme,
|
|
256
285
|
useRootTheme,
|
|
257
286
|
useTheme,
|
|
258
287
|
useThemeSetting
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/NextTheme.tsx"],
|
|
4
|
-
"sourcesContent": ["// https://raw.githubusercontent.com/pacocoursey/next-themes/master/index.tsx\n// forked temporarily due to buggy theme change\n\nimport { useEvent } from '@tamagui/use-event'\nimport NextHead from 'next/head'\nimport * as React from 'react'\nimport {\n createContext,\n memo,\n useContext,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nconst useIsomorphicLayoutEffect =\n typeof window !== 'undefined' ? useLayoutEffect : useEffect\n\nexport interface UseThemeProps {\n /** List of all available theme names */\n themes: string[]\n /** Forced theme name for the current page */\n forcedTheme?: string\n /** Update the theme */\n set: (theme: string) => void\n toggle: () => void\n /** Active theme name - will return \"system\" if not overriden, see \"resolvedTheme\" for getting resolved system value */\n current?: string\n /** @deprecated Use `current` instead (deprecating avoid confusion with useTheme) */\n theme?: string\n /** If `enableSystem` is true and the active theme is \"system\", this returns whether the system preference resolved to \"dark\" or \"light\". Otherwise, identical to `theme` */\n resolvedTheme?: string\n /** If enableSystem is true, returns the System theme preference (\"dark\" or \"light\"), regardless what the active theme is */\n systemTheme?: 'dark' | 'light'\n}\n\nexport interface ThemeProviderProps {\n children?: any\n /** List of all available theme names */\n themes?: string[]\n /** Forced theme name for the current page */\n forcedTheme?: string\n /** Whether to switch between dark and light themes based on prefers-color-scheme */\n enableSystem?: boolean\n systemTheme?: string\n /** Disable all CSS transitions when switching themes */\n disableTransitionOnChange?: boolean\n /** Whether to indicate to browsers which color scheme is used (dark or light) for built-in UI like inputs and buttons */\n enableColorScheme?: boolean\n /** Key used to store theme setting in localStorage */\n storageKey?: string\n /** Default theme name (for v0.0.12 and lower the default was light). If `enableSystem` is false, the default theme is light */\n defaultTheme?: string\n /** HTML attribute modified based on the active theme. Accepts `class` and `data-*` (meaning any data attribute, `data-mode`, `data-color`, etc.) */\n attribute?: string | 'class'\n /** Mapping of theme name to HTML attribute value. Object where key is the theme name and value is the attribute value */\n value?: ValueObject\n onChangeTheme?: (name: string) => void\n}\n\nconst ThemeSettingContext = createContext<UseThemeProps>({\n toggle: () => {},\n set: (_) => {},\n themes: [],\n})\n\n/**\n * @deprecated renamed to `useThemeSetting` to avoid confusion with core `useTheme` hook\n */\nexport const useTheme = () => useContext(ThemeSettingContext)\n\nexport const useThemeSetting = () => useContext(ThemeSettingContext)\n\nconst colorSchemes = ['light', 'dark']\nconst MEDIA = '(prefers-color-scheme: dark)'\n\ninterface ValueObject {\n [themeName: string]: string\n}\n\n// note this only works for light being default for now...\nexport const useRootTheme = () => {\n const [val, setVal] = useState('light')\n\n if (typeof document !== 'undefined') {\n useLayoutEffect(() => {\n // @ts-ignore\n const classes = [...document.documentElement.classList]\n const isDark = classes.includes('t_dark')\n React.startTransition(() => {\n setVal(isDark ? 'dark' : 'light')\n })\n }, [])\n }\n\n return [val, setVal] as const\n}\n\nexport const NextThemeProvider: React.FC<ThemeProviderProps> = ({\n forcedTheme,\n disableTransitionOnChange = true,\n enableSystem = true,\n enableColorScheme = true,\n storageKey = 'theme',\n themes = colorSchemes,\n defaultTheme = enableSystem ? 'system' : 'light',\n attribute = 'class',\n onChangeTheme,\n value = {\n dark: 't_dark',\n light: 't_light',\n },\n children,\n}) => {\n const [theme, setThemeState] = useState(() => getTheme(storageKey, defaultTheme))\n const [resolvedTheme, setResolvedTheme] = useState(() => getTheme(storageKey))\n // const resolvedTheme = React.useDeferredValue(resolvedThemeFast)\n const attrs = !value ? themes : Object.values(value)\n\n const handleMediaQuery = useEvent((e?) => {\n const systemTheme = getSystemTheme(e)\n React.startTransition(() => {\n setResolvedTheme(systemTheme)\n })\n if (theme === 'system' && !forcedTheme) {\n handleChangeTheme(systemTheme, false)\n }\n })\n\n // Ref hack to avoid adding handleMediaQuery as a dep\n const mediaListener = useRef(handleMediaQuery)\n mediaListener.current = handleMediaQuery\n\n const handleChangeTheme = useEvent(\n (theme, updateStorage = true, updateDOM = true) => {\n let name = value?.[theme] || theme\n\n if (updateStorage) {\n try {\n localStorage.setItem(storageKey, theme)\n } catch (e) {\n // Unsupported\n }\n }\n\n if (theme === 'system' && enableSystem) {\n const resolved = getSystemTheme()\n name = value?.[resolved] || resolved\n }\n\n onChangeTheme?.(name.replace('t_', ''))\n\n if (updateDOM) {\n const d = document.documentElement\n if (attribute === 'class') {\n d.classList.remove(...attrs)\n d.classList.add(name)\n } else {\n d.setAttribute(attribute, name)\n }\n }\n },\n )\n\n useIsomorphicLayoutEffect(() => {\n const handler = (...args: any) => mediaListener.current(...args)\n // Always listen to System preference\n const media = window.matchMedia(MEDIA)\n // Intentionally use deprecated listener methods to support iOS & old browsers\n media.addListener(handler)\n handler(media)\n return () => {\n media.removeListener(handler)\n }\n }, [])\n\n const set = useEvent((newTheme) => {\n if (forcedTheme) {\n handleChangeTheme(newTheme, true, false)\n } else {\n handleChangeTheme(newTheme)\n }\n setThemeState(newTheme)\n })\n\n // localStorage event handling\n useEffect(() => {\n const handleStorage = (e: StorageEvent) => {\n if (e.key !== storageKey) {\n return\n }\n // If default theme set, use it if localstorage === null (happens on local storage manual deletion)\n const theme = e.newValue || defaultTheme\n set(theme)\n }\n window.addEventListener('storage', handleStorage)\n return () => {\n window.removeEventListener('storage', handleStorage)\n }\n }, [defaultTheme, set, storageKey])\n\n // color-scheme handling\n useIsomorphicLayoutEffect(() => {\n if (!enableColorScheme) return\n\n const colorScheme =\n // If theme is forced to light or dark, use that\n forcedTheme && colorSchemes.includes(forcedTheme)\n ? forcedTheme\n : // If regular theme is light or dark\n theme && colorSchemes.includes(theme)\n ? theme\n : // If theme is system, use the resolved version\n theme === 'system'\n ? resolvedTheme || null\n : null\n\n // color-scheme tells browser how to render built-in elements like forms, scrollbars, etc.\n // if color-scheme is null, this will remove the property\n const userPrefers =\n typeof window !== 'undefined' &&\n window.matchMedia &&\n window.matchMedia('(prefers-color-scheme: dark)').matches\n ? 'dark'\n : 'light'\n\n const wePrefer = colorScheme || 'light'\n\n // avoid running this because it causes full page reflow\n if (userPrefers !== wePrefer) {\n document.documentElement.style.setProperty('color-scheme', colorScheme)\n }\n }, [enableColorScheme, theme, resolvedTheme, forcedTheme])\n\n const toggle = useEvent(() => {\n const order =\n resolvedTheme === 'dark'\n ? ['system', 'light', 'dark']\n : ['system', 'dark', 'light']\n const next = order[(order.indexOf(theme) + 1) % order.length]\n set(next)\n })\n\n const contextResolvedTheme = theme === 'system' ? resolvedTheme : theme\n const systemTheme = (enableSystem ? resolvedTheme : undefined) as\n | 'light'\n | 'dark'\n | undefined\n const contextValue = useMemo(() => {\n const value: UseThemeProps = {\n theme,\n current: theme,\n set,\n toggle,\n forcedTheme,\n resolvedTheme: contextResolvedTheme,\n themes: enableSystem ? [...themes, 'system'] : themes,\n systemTheme,\n } as const\n return value\n }, [\n theme,\n set,\n toggle,\n forcedTheme,\n contextResolvedTheme,\n enableSystem,\n themes,\n systemTheme,\n ])\n\n return (\n <ThemeSettingContext.Provider value={contextValue}>\n <ThemeScript\n {...{\n forcedTheme,\n storageKey,\n systemTheme: resolvedTheme,\n attribute,\n value,\n enableSystem,\n defaultTheme,\n attrs,\n }}\n />\n {/* because on SSR we re-run and can avoid whole tree re-render */}\n {useMemo(() => children, [children])}\n </ThemeSettingContext.Provider>\n )\n}\n\nconst ThemeScript = memo(\n ({\n forcedTheme,\n storageKey,\n attribute,\n enableSystem,\n defaultTheme,\n value,\n attrs,\n }: {\n forcedTheme?: string\n storageKey: string\n attribute?: string\n enableSystem?: boolean\n defaultTheme: string\n value?: ValueObject\n attrs: any\n }) => {\n // Code-golfing the amount of characters in the script\n const optimization = (() => {\n if (attribute === 'class') {\n const removeClasses = attrs.map((t: string) => `d.remove('${t}')`).join(';')\n return `var d=document.documentElement.classList;${removeClasses};`\n } else {\n return `var d=document.documentElement;`\n }\n })()\n\n const updateDOM = (name: string, literal?: boolean) => {\n name = value?.[name] || name\n const val = literal ? name : `'${name}'`\n\n if (attribute === 'class') {\n return `d.add(${val})`\n }\n\n return `d.setAttribute('${attribute}', ${val})`\n }\n\n const defaultSystem = defaultTheme === 'system'\n\n return (\n <NextHead>\n {forcedTheme ? (\n <script\n key=\"next-themes-script\"\n dangerouslySetInnerHTML={{\n // These are minified via Terser and then updated by hand, don't recommend\n __html: `!function(){${optimization}${updateDOM(forcedTheme)}}()`,\n }}\n />\n ) : enableSystem ? (\n <script\n key=\"next-themes-script\"\n dangerouslySetInnerHTML={{\n __html: `!function(){try {${optimization}var e=localStorage.getItem('${storageKey}');${\n !defaultSystem ? updateDOM(defaultTheme) + ';' : ''\n }if(\"system\"===e||(!e&&${defaultSystem})){var t=\"${MEDIA}\",m=window.matchMedia(t);m.media!==t||m.matches?${updateDOM(\n 'dark',\n )}:${updateDOM('light')}}else if(e) ${\n value ? `var x=${JSON.stringify(value)};` : ''\n }${updateDOM(value ? 'x[e]' : 'e', true)}}catch(e){}}()`,\n }}\n />\n ) : (\n <script\n key=\"next-themes-script\"\n dangerouslySetInnerHTML={{\n __html: `!function(){try{${optimization}var e=localStorage.getItem(\"${storageKey}\");if(e){${\n value ? `var x=${JSON.stringify(value)};` : ''\n }${updateDOM(value ? 'x[e]' : 'e', true)}}else{${updateDOM(\n defaultTheme,\n )};}}catch(t){}}();`,\n }}\n />\n )}\n </NextHead>\n )\n },\n (prevProps, nextProps) => {\n // Only re-render when forcedTheme changes\n // the rest of the props should be completely stable\n if (prevProps.forcedTheme !== nextProps.forcedTheme) return false\n return true\n },\n)\n\n// Helpers\nconst getTheme = (key: string, fallback?: string) => {\n if (typeof window === 'undefined') return undefined\n let theme\n try {\n theme = localStorage.getItem(key) || undefined\n } catch (e) {\n // Unsupported\n }\n return theme || fallback\n}\n\nconst getSystemTheme = (e?: MediaQueryList) => {\n if (!e) {\n e = window.matchMedia(MEDIA)\n }\n\n const isDark = e.matches\n const systemTheme = isDark ? 'dark' : 'light'\n return systemTheme\n}\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["// https://raw.githubusercontent.com/pacocoursey/next-themes/master/index.tsx\n// forked temporarily due to buggy theme change\n\nimport { useEvent } from '@tamagui/use-event'\nimport * as React from 'react'\nimport {\n createContext,\n memo,\n useContext,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nconst useIsomorphicLayoutEffect =\n typeof window !== 'undefined' ? useLayoutEffect : useEffect\n\nexport interface UseThemeProps {\n /** List of all available theme names */\n themes: string[]\n /** Forced theme name for the current page */\n forcedTheme?: string\n /** Update the theme */\n set: (theme: string) => void\n toggle: () => void\n /** Active theme name - will return \"system\" if not overriden, see \"resolvedTheme\" for getting resolved system value */\n current?: string\n /** @deprecated Use `current` instead (deprecating avoid confusion with useTheme) */\n theme?: string\n /** If `enableSystem` is true and the active theme is \"system\", this returns whether the system preference resolved to \"dark\" or \"light\". Otherwise, identical to `theme` */\n resolvedTheme?: string\n /** If enableSystem is true, returns the System theme preference (\"dark\" or \"light\"), regardless what the active theme is */\n systemTheme?: 'dark' | 'light'\n}\n\nexport interface ThemeProviderProps {\n children?: any\n /** List of all available theme names */\n themes?: string[]\n /** Forced theme name for the current page */\n forcedTheme?: string\n /** Whether to switch between dark and light themes based on prefers-color-scheme */\n enableSystem?: boolean\n systemTheme?: string\n /** Disable all CSS transitions when switching themes */\n disableTransitionOnChange?: boolean\n /** Whether to indicate to browsers which color scheme is used (dark or light) for built-in UI like inputs and buttons */\n enableColorScheme?: boolean\n /** Key used to store theme setting in localStorage */\n storageKey?: string\n /** Default theme name (for v0.0.12 and lower the default was light). If `enableSystem` is false, the default theme is light */\n defaultTheme?: string\n /** HTML attribute modified based on the active theme. Accepts `class` and `data-*` (meaning any data attribute, `data-mode`, `data-color`, etc.) */\n attribute?: string | 'class'\n /** Mapping of theme name to HTML attribute value. Object where key is the theme name and value is the attribute value */\n value?: ValueObject\n onChangeTheme?: (name: string) => void\n nonce?: string\n}\n\nconst ThemeContext = createContext<UseThemeProps | null>(null)\n\n/**\n * @deprecated renamed to `useThemeSetting` to avoid confusion with core `useTheme` hook\n */\nexport const useTheme = () => useContext(ThemeContext)\n\nexport const useThemeSetting = () => useContext(ThemeContext)\n\nconst colorSchemes = ['light', 'dark']\nconst MEDIA = '(prefers-color-scheme: dark)'\n\ninterface ValueObject {\n [themeName: string]: string\n}\n\n// note this only works for light being default for now...\nexport const useRootTheme = () => {\n const [val, setVal] = useState('light')\n\n if (typeof document !== 'undefined') {\n useLayoutEffect(() => {\n // @ts-ignore\n const classes = [...document.documentElement.classList]\n const isDark = classes.includes('t_dark')\n React.startTransition(() => {\n setVal(isDark ? 'dark' : 'light')\n })\n }, [])\n }\n\n return [val, setVal] as const\n}\n\nexport const NextThemeProvider: React.FC<ThemeProviderProps> = (props) => {\n const context = useContext(ThemeContext)\n // Ignore nested context providers, just passthrough children\n if (context) {\n return <>{props.children}</>\n }\n return <Theme {...props} />\n}\n\nexport const Theme: React.FC<ThemeProviderProps> = ({\n nonce,\n forcedTheme,\n disableTransitionOnChange = true,\n enableSystem = true,\n enableColorScheme = true,\n storageKey = 'theme',\n themes = colorSchemes,\n defaultTheme = enableSystem ? 'system' : 'light',\n attribute = 'class',\n onChangeTheme,\n value = {\n dark: 't_dark',\n light: 't_light',\n },\n children,\n}) => {\n const [theme, setThemeState] = useState(() => getTheme(storageKey, defaultTheme))\n const [resolvedTheme, setResolvedTheme] = useState(() => getTheme(storageKey))\n // const resolvedTheme = React.useDeferredValue(resolvedThemeFast)\n const attrs = !value ? themes : Object.values(value)\n\n const handleMediaQuery = useEvent((e?) => {\n const systemTheme = getSystemTheme(e)\n React.startTransition(() => {\n setResolvedTheme(systemTheme)\n })\n if (theme === 'system' && !forcedTheme) {\n handleChangeTheme(systemTheme, false)\n }\n })\n\n // Ref hack to avoid adding handleMediaQuery as a dep\n const mediaListener = useRef(handleMediaQuery)\n mediaListener.current = handleMediaQuery\n\n const handleChangeTheme = useEvent((theme, updateStorage = true, updateDOM = true) => {\n let name = value?.[theme] || theme\n\n if (updateStorage) {\n try {\n localStorage.setItem(storageKey, theme)\n } catch (e) {\n // Unsupported\n }\n }\n\n if (theme === 'system' && enableSystem) {\n const resolved = getSystemTheme()\n name = value?.[resolved] || resolved\n }\n\n onChangeTheme?.(name.replace('t_', ''))\n\n if (updateDOM) {\n const d = document.documentElement\n if (attribute === 'class') {\n d.classList.remove(...attrs)\n d.classList.add(name)\n } else {\n d.setAttribute(attribute, name)\n }\n }\n })\n\n useIsomorphicLayoutEffect(() => {\n const handler = (...args: any) => mediaListener.current(...args)\n // Always listen to System preference\n const media = window.matchMedia(MEDIA)\n // Intentionally use deprecated listener methods to support iOS & old browsers\n media.addListener(handler)\n handler(media)\n return () => {\n media.removeListener(handler)\n }\n }, [])\n\n const set = useEvent((newTheme) => {\n if (forcedTheme) {\n handleChangeTheme(newTheme, true, false)\n } else {\n handleChangeTheme(newTheme)\n }\n setThemeState(newTheme)\n })\n\n // localStorage event handling\n useEffect(() => {\n const handleStorage = (e: StorageEvent) => {\n if (e.key !== storageKey) {\n return\n }\n // If default theme set, use it if localstorage === null (happens on local storage manual deletion)\n const theme = e.newValue || defaultTheme\n set(theme)\n }\n window.addEventListener('storage', handleStorage)\n return () => {\n window.removeEventListener('storage', handleStorage)\n }\n }, [defaultTheme, set, storageKey])\n\n // color-scheme handling\n useIsomorphicLayoutEffect(() => {\n if (!enableColorScheme) return\n\n const colorScheme =\n // If theme is forced to light or dark, use that\n forcedTheme && colorSchemes.includes(forcedTheme)\n ? forcedTheme\n : // If regular theme is light or dark\n theme && colorSchemes.includes(theme)\n ? theme\n : // If theme is system, use the resolved version\n theme === 'system'\n ? resolvedTheme || null\n : null\n\n // color-scheme tells browser how to render built-in elements like forms, scrollbars, etc.\n // if color-scheme is null, this will remove the property\n const userPrefers =\n typeof window !== 'undefined' &&\n window.matchMedia &&\n window.matchMedia('(prefers-color-scheme: dark)').matches\n ? 'dark'\n : 'light'\n\n const wePrefer = colorScheme || 'light'\n\n // avoid running this because it causes full page reflow\n if (userPrefers !== wePrefer) {\n document.documentElement.style.setProperty('color-scheme', colorScheme)\n }\n }, [enableColorScheme, theme, resolvedTheme, forcedTheme])\n\n const toggle = useEvent(() => {\n const order =\n resolvedTheme === 'dark' ? ['system', 'light', 'dark'] : ['system', 'dark', 'light']\n const next = order[(order.indexOf(theme) + 1) % order.length]\n set(next)\n })\n\n const contextResolvedTheme = theme === 'system' ? resolvedTheme : theme\n const systemTheme = (enableSystem ? resolvedTheme : undefined) as\n | 'light'\n | 'dark'\n | undefined\n const contextValue = useMemo(() => {\n const value: UseThemeProps = {\n theme,\n current: theme,\n set,\n toggle,\n forcedTheme,\n resolvedTheme: contextResolvedTheme,\n themes: enableSystem ? [...themes, 'system'] : themes,\n systemTheme,\n } as const\n return value\n }, [\n theme,\n set,\n toggle,\n forcedTheme,\n contextResolvedTheme,\n enableSystem,\n themes,\n systemTheme,\n ])\n\n return (\n <ThemeContext.Provider value={contextValue}>\n <ThemeScript\n {...{\n forcedTheme,\n disableTransitionOnChange,\n storageKey,\n systemTheme: resolvedTheme,\n attribute,\n enableColorScheme,\n value,\n themes,\n enableSystem,\n defaultTheme,\n attrs,\n nonce,\n }}\n />\n {/* because on SSR we re-run and can avoid whole tree re-render */}\n {useMemo(() => children, [children])}\n </ThemeContext.Provider>\n )\n}\n\nconst ThemeScript = memo(\n ({\n forcedTheme,\n storageKey,\n attribute,\n enableSystem,\n enableColorScheme,\n defaultTheme,\n value,\n attrs,\n nonce,\n }: ThemeProviderProps & { attrs: string[]; defaultTheme: string }) => {\n const defaultSystem = defaultTheme === 'system'\n\n // Code-golfing the amount of characters in the script\n const optimization = (() => {\n if (attribute === 'class') {\n const removeClasses = `c.remove(${attrs.map((t: string) => `'${t}'`).join(',')})`\n\n return `var d=document.documentElement,c=d.classList;${removeClasses};`\n } else {\n return `var d=document.documentElement,n='${attribute}',s='setAttribute';`\n }\n })()\n\n const fallbackColorScheme = (() => {\n if (!enableColorScheme) {\n return ''\n }\n\n const fallback = colorSchemes.includes(defaultTheme) ? defaultTheme : null\n\n if (fallback) {\n return `if(e==='light'||e==='dark'||!e)d.style.colorScheme=e||'${defaultTheme}'`\n } else {\n return `if(e==='light'||e==='dark')d.style.colorScheme=e`\n }\n })()\n\n const updateDOM = (name: string, literal: boolean = false, setColorScheme = true) => {\n const resolvedName = value ? value[name] : name\n const val = literal ? name + `|| ''` : `'${resolvedName}'`\n let text = ''\n\n // MUCH faster to set colorScheme alongside HTML attribute/class\n // as it only incurs 1 style recalculation rather than 2\n // This can save over 250ms of work for pages with big DOM\n if (\n enableColorScheme &&\n setColorScheme &&\n !literal &&\n colorSchemes.includes(name)\n ) {\n text += `d.style.colorScheme = '${name}';`\n }\n\n if (attribute === 'class') {\n if (literal || resolvedName) {\n text += `c.add(${val})`\n } else {\n text += `null`\n }\n } else {\n if (resolvedName) {\n text += `d[s](n,${val})`\n }\n }\n\n return text\n }\n\n const scriptSrc = (() => {\n if (forcedTheme) {\n return `!function(){${optimization}${updateDOM(forcedTheme)}}()`\n }\n\n if (enableSystem) {\n return `!function(){try{${optimization}var e=localStorage.getItem('${storageKey}');if('system'===e||(!e&&${defaultSystem})){var t='${MEDIA}',m=window.matchMedia(t);if(m.media!==t||m.matches){${updateDOM(\n 'dark'\n )}}else{${updateDOM('light')}}}else if(e){${\n value ? `var x=${JSON.stringify(value)};` : ''\n }${updateDOM(value ? `x[e]` : 'e', true)}}${\n !defaultSystem ? `else{` + updateDOM(defaultTheme, false, false) + '}' : ''\n }${fallbackColorScheme}}catch(e){}}()`\n }\n\n return `!function(){try{${optimization}var e=localStorage.getItem('${storageKey}');if(e){${\n value ? `var x=${JSON.stringify(value)};` : ''\n }${updateDOM(value ? `x[e]` : 'e', true)}}else{${updateDOM(\n defaultTheme,\n false,\n false\n )};}${fallbackColorScheme}}catch(t){}}();`\n })()\n\n return <script nonce={nonce} dangerouslySetInnerHTML={{ __html: scriptSrc }} />\n },\n // Never re-render this component\n () => true\n)\n\n// Helpers\nconst getTheme = (key: string, fallback?: string) => {\n if (typeof window === 'undefined') return undefined\n let theme\n try {\n theme = localStorage.getItem(key) || undefined\n } catch (e) {\n // Unsupported\n }\n return theme || fallback\n}\n\nconst getSystemTheme = (e?: MediaQueryList) => {\n if (!e) {\n e = window.matchMedia(MEDIA)\n }\n\n const isDark = e.matches\n const systemTheme = isDark ? 'dark' : 'light'\n return systemTheme\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["systemTheme", "theme", "value"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tamagui/next-theme",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.12",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"source": "src/index.ts",
|
|
6
6
|
"types": "./types/index.d.ts",
|
|
@@ -16,13 +16,13 @@
|
|
|
16
16
|
"watch": "tamagui-build --watch"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@tamagui/use-event": "^1.0.
|
|
19
|
+
"@tamagui/use-event": "^1.0.12"
|
|
20
20
|
},
|
|
21
21
|
"peerDependencies": {
|
|
22
22
|
"react": "*"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@tamagui/build": "^1.0.
|
|
25
|
+
"@tamagui/build": "^1.0.12",
|
|
26
26
|
"react": "^18.2.0"
|
|
27
27
|
},
|
|
28
28
|
"publishConfig": {
|
package/src/NextTheme.tsx
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
// forked temporarily due to buggy theme change
|
|
3
3
|
|
|
4
4
|
import { useEvent } from '@tamagui/use-event'
|
|
5
|
-
import NextHead from 'next/head'
|
|
6
5
|
import * as React from 'react'
|
|
7
6
|
import {
|
|
8
7
|
createContext,
|
|
@@ -58,20 +57,17 @@ export interface ThemeProviderProps {
|
|
|
58
57
|
/** Mapping of theme name to HTML attribute value. Object where key is the theme name and value is the attribute value */
|
|
59
58
|
value?: ValueObject
|
|
60
59
|
onChangeTheme?: (name: string) => void
|
|
60
|
+
nonce?: string
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
const
|
|
64
|
-
toggle: () => {},
|
|
65
|
-
set: (_) => {},
|
|
66
|
-
themes: [],
|
|
67
|
-
})
|
|
63
|
+
const ThemeContext = createContext<UseThemeProps | null>(null)
|
|
68
64
|
|
|
69
65
|
/**
|
|
70
66
|
* @deprecated renamed to `useThemeSetting` to avoid confusion with core `useTheme` hook
|
|
71
67
|
*/
|
|
72
|
-
export const useTheme = () => useContext(
|
|
68
|
+
export const useTheme = () => useContext(ThemeContext)
|
|
73
69
|
|
|
74
|
-
export const useThemeSetting = () => useContext(
|
|
70
|
+
export const useThemeSetting = () => useContext(ThemeContext)
|
|
75
71
|
|
|
76
72
|
const colorSchemes = ['light', 'dark']
|
|
77
73
|
const MEDIA = '(prefers-color-scheme: dark)'
|
|
@@ -98,7 +94,17 @@ export const useRootTheme = () => {
|
|
|
98
94
|
return [val, setVal] as const
|
|
99
95
|
}
|
|
100
96
|
|
|
101
|
-
export const NextThemeProvider: React.FC<ThemeProviderProps> = ({
|
|
97
|
+
export const NextThemeProvider: React.FC<ThemeProviderProps> = (props) => {
|
|
98
|
+
const context = useContext(ThemeContext)
|
|
99
|
+
// Ignore nested context providers, just passthrough children
|
|
100
|
+
if (context) {
|
|
101
|
+
return <>{props.children}</>
|
|
102
|
+
}
|
|
103
|
+
return <Theme {...props} />
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export const Theme: React.FC<ThemeProviderProps> = ({
|
|
107
|
+
nonce,
|
|
102
108
|
forcedTheme,
|
|
103
109
|
disableTransitionOnChange = true,
|
|
104
110
|
enableSystem = true,
|
|
@@ -133,36 +139,34 @@ export const NextThemeProvider: React.FC<ThemeProviderProps> = ({
|
|
|
133
139
|
const mediaListener = useRef(handleMediaQuery)
|
|
134
140
|
mediaListener.current = handleMediaQuery
|
|
135
141
|
|
|
136
|
-
const handleChangeTheme = useEvent(
|
|
137
|
-
|
|
138
|
-
let name = value?.[theme] || theme
|
|
142
|
+
const handleChangeTheme = useEvent((theme, updateStorage = true, updateDOM = true) => {
|
|
143
|
+
let name = value?.[theme] || theme
|
|
139
144
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
145
|
+
if (updateStorage) {
|
|
146
|
+
try {
|
|
147
|
+
localStorage.setItem(storageKey, theme)
|
|
148
|
+
} catch (e) {
|
|
149
|
+
// Unsupported
|
|
146
150
|
}
|
|
151
|
+
}
|
|
147
152
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
153
|
+
if (theme === 'system' && enableSystem) {
|
|
154
|
+
const resolved = getSystemTheme()
|
|
155
|
+
name = value?.[resolved] || resolved
|
|
156
|
+
}
|
|
152
157
|
|
|
153
|
-
|
|
158
|
+
onChangeTheme?.(name.replace('t_', ''))
|
|
154
159
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
160
|
+
if (updateDOM) {
|
|
161
|
+
const d = document.documentElement
|
|
162
|
+
if (attribute === 'class') {
|
|
163
|
+
d.classList.remove(...attrs)
|
|
164
|
+
d.classList.add(name)
|
|
165
|
+
} else {
|
|
166
|
+
d.setAttribute(attribute, name)
|
|
163
167
|
}
|
|
164
|
-
}
|
|
165
|
-
)
|
|
168
|
+
}
|
|
169
|
+
})
|
|
166
170
|
|
|
167
171
|
useIsomorphicLayoutEffect(() => {
|
|
168
172
|
const handler = (...args: any) => mediaListener.current(...args)
|
|
@@ -236,9 +240,7 @@ export const NextThemeProvider: React.FC<ThemeProviderProps> = ({
|
|
|
236
240
|
|
|
237
241
|
const toggle = useEvent(() => {
|
|
238
242
|
const order =
|
|
239
|
-
resolvedTheme === 'dark'
|
|
240
|
-
? ['system', 'light', 'dark']
|
|
241
|
-
: ['system', 'dark', 'light']
|
|
243
|
+
resolvedTheme === 'dark' ? ['system', 'light', 'dark'] : ['system', 'dark', 'light']
|
|
242
244
|
const next = order[(order.indexOf(theme) + 1) % order.length]
|
|
243
245
|
set(next)
|
|
244
246
|
})
|
|
@@ -272,22 +274,26 @@ export const NextThemeProvider: React.FC<ThemeProviderProps> = ({
|
|
|
272
274
|
])
|
|
273
275
|
|
|
274
276
|
return (
|
|
275
|
-
<
|
|
277
|
+
<ThemeContext.Provider value={contextValue}>
|
|
276
278
|
<ThemeScript
|
|
277
279
|
{...{
|
|
278
280
|
forcedTheme,
|
|
281
|
+
disableTransitionOnChange,
|
|
279
282
|
storageKey,
|
|
280
283
|
systemTheme: resolvedTheme,
|
|
281
284
|
attribute,
|
|
285
|
+
enableColorScheme,
|
|
282
286
|
value,
|
|
287
|
+
themes,
|
|
283
288
|
enableSystem,
|
|
284
289
|
defaultTheme,
|
|
285
290
|
attrs,
|
|
291
|
+
nonce,
|
|
286
292
|
}}
|
|
287
293
|
/>
|
|
288
294
|
{/* because on SSR we re-run and can avoid whole tree re-render */}
|
|
289
295
|
{useMemo(() => children, [children])}
|
|
290
|
-
</
|
|
296
|
+
</ThemeContext.Provider>
|
|
291
297
|
)
|
|
292
298
|
}
|
|
293
299
|
|
|
@@ -297,85 +303,99 @@ const ThemeScript = memo(
|
|
|
297
303
|
storageKey,
|
|
298
304
|
attribute,
|
|
299
305
|
enableSystem,
|
|
306
|
+
enableColorScheme,
|
|
300
307
|
defaultTheme,
|
|
301
308
|
value,
|
|
302
309
|
attrs,
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
enableSystem?: boolean
|
|
308
|
-
defaultTheme: string
|
|
309
|
-
value?: ValueObject
|
|
310
|
-
attrs: any
|
|
311
|
-
}) => {
|
|
310
|
+
nonce,
|
|
311
|
+
}: ThemeProviderProps & { attrs: string[]; defaultTheme: string }) => {
|
|
312
|
+
const defaultSystem = defaultTheme === 'system'
|
|
313
|
+
|
|
312
314
|
// Code-golfing the amount of characters in the script
|
|
313
315
|
const optimization = (() => {
|
|
314
316
|
if (attribute === 'class') {
|
|
315
|
-
const removeClasses = attrs.map((t: string) => `
|
|
316
|
-
|
|
317
|
+
const removeClasses = `c.remove(${attrs.map((t: string) => `'${t}'`).join(',')})`
|
|
318
|
+
|
|
319
|
+
return `var d=document.documentElement,c=d.classList;${removeClasses};`
|
|
320
|
+
} else {
|
|
321
|
+
return `var d=document.documentElement,n='${attribute}',s='setAttribute';`
|
|
322
|
+
}
|
|
323
|
+
})()
|
|
324
|
+
|
|
325
|
+
const fallbackColorScheme = (() => {
|
|
326
|
+
if (!enableColorScheme) {
|
|
327
|
+
return ''
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const fallback = colorSchemes.includes(defaultTheme) ? defaultTheme : null
|
|
331
|
+
|
|
332
|
+
if (fallback) {
|
|
333
|
+
return `if(e==='light'||e==='dark'||!e)d.style.colorScheme=e||'${defaultTheme}'`
|
|
317
334
|
} else {
|
|
318
|
-
return `
|
|
335
|
+
return `if(e==='light'||e==='dark')d.style.colorScheme=e`
|
|
319
336
|
}
|
|
320
337
|
})()
|
|
321
338
|
|
|
322
|
-
const updateDOM = (name: string, literal
|
|
323
|
-
|
|
324
|
-
const val = literal ? name : `'${
|
|
339
|
+
const updateDOM = (name: string, literal: boolean = false, setColorScheme = true) => {
|
|
340
|
+
const resolvedName = value ? value[name] : name
|
|
341
|
+
const val = literal ? name + `|| ''` : `'${resolvedName}'`
|
|
342
|
+
let text = ''
|
|
343
|
+
|
|
344
|
+
// MUCH faster to set colorScheme alongside HTML attribute/class
|
|
345
|
+
// as it only incurs 1 style recalculation rather than 2
|
|
346
|
+
// This can save over 250ms of work for pages with big DOM
|
|
347
|
+
if (
|
|
348
|
+
enableColorScheme &&
|
|
349
|
+
setColorScheme &&
|
|
350
|
+
!literal &&
|
|
351
|
+
colorSchemes.includes(name)
|
|
352
|
+
) {
|
|
353
|
+
text += `d.style.colorScheme = '${name}';`
|
|
354
|
+
}
|
|
325
355
|
|
|
326
356
|
if (attribute === 'class') {
|
|
327
|
-
|
|
357
|
+
if (literal || resolvedName) {
|
|
358
|
+
text += `c.add(${val})`
|
|
359
|
+
} else {
|
|
360
|
+
text += `null`
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
if (resolvedName) {
|
|
364
|
+
text += `d[s](n,${val})`
|
|
365
|
+
}
|
|
328
366
|
}
|
|
329
367
|
|
|
330
|
-
return
|
|
368
|
+
return text
|
|
331
369
|
}
|
|
332
370
|
|
|
333
|
-
const
|
|
371
|
+
const scriptSrc = (() => {
|
|
372
|
+
if (forcedTheme) {
|
|
373
|
+
return `!function(){${optimization}${updateDOM(forcedTheme)}}()`
|
|
374
|
+
}
|
|
334
375
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
}${updateDOM(value ? 'x[e]' : 'e', true)}}catch(e){}}()`,
|
|
356
|
-
}}
|
|
357
|
-
/>
|
|
358
|
-
) : (
|
|
359
|
-
<script
|
|
360
|
-
key="next-themes-script"
|
|
361
|
-
dangerouslySetInnerHTML={{
|
|
362
|
-
__html: `!function(){try{${optimization}var e=localStorage.getItem("${storageKey}");if(e){${
|
|
363
|
-
value ? `var x=${JSON.stringify(value)};` : ''
|
|
364
|
-
}${updateDOM(value ? 'x[e]' : 'e', true)}}else{${updateDOM(
|
|
365
|
-
defaultTheme,
|
|
366
|
-
)};}}catch(t){}}();`,
|
|
367
|
-
}}
|
|
368
|
-
/>
|
|
369
|
-
)}
|
|
370
|
-
</NextHead>
|
|
371
|
-
)
|
|
372
|
-
},
|
|
373
|
-
(prevProps, nextProps) => {
|
|
374
|
-
// Only re-render when forcedTheme changes
|
|
375
|
-
// the rest of the props should be completely stable
|
|
376
|
-
if (prevProps.forcedTheme !== nextProps.forcedTheme) return false
|
|
377
|
-
return true
|
|
376
|
+
if (enableSystem) {
|
|
377
|
+
return `!function(){try{${optimization}var e=localStorage.getItem('${storageKey}');if('system'===e||(!e&&${defaultSystem})){var t='${MEDIA}',m=window.matchMedia(t);if(m.media!==t||m.matches){${updateDOM(
|
|
378
|
+
'dark'
|
|
379
|
+
)}}else{${updateDOM('light')}}}else if(e){${
|
|
380
|
+
value ? `var x=${JSON.stringify(value)};` : ''
|
|
381
|
+
}${updateDOM(value ? `x[e]` : 'e', true)}}${
|
|
382
|
+
!defaultSystem ? `else{` + updateDOM(defaultTheme, false, false) + '}' : ''
|
|
383
|
+
}${fallbackColorScheme}}catch(e){}}()`
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return `!function(){try{${optimization}var e=localStorage.getItem('${storageKey}');if(e){${
|
|
387
|
+
value ? `var x=${JSON.stringify(value)};` : ''
|
|
388
|
+
}${updateDOM(value ? `x[e]` : 'e', true)}}else{${updateDOM(
|
|
389
|
+
defaultTheme,
|
|
390
|
+
false,
|
|
391
|
+
false
|
|
392
|
+
)};}${fallbackColorScheme}}catch(t){}}();`
|
|
393
|
+
})()
|
|
394
|
+
|
|
395
|
+
return <script nonce={nonce} dangerouslySetInnerHTML={{ __html: scriptSrc }} />
|
|
378
396
|
},
|
|
397
|
+
// Never re-render this component
|
|
398
|
+
() => true
|
|
379
399
|
)
|
|
380
400
|
|
|
381
401
|
// Helpers
|
package/types/NextTheme.d.ts
CHANGED
|
@@ -38,16 +38,18 @@ export interface ThemeProviderProps {
|
|
|
38
38
|
/** Mapping of theme name to HTML attribute value. Object where key is the theme name and value is the attribute value */
|
|
39
39
|
value?: ValueObject;
|
|
40
40
|
onChangeTheme?: (name: string) => void;
|
|
41
|
+
nonce?: string;
|
|
41
42
|
}
|
|
42
43
|
/**
|
|
43
44
|
* @deprecated renamed to `useThemeSetting` to avoid confusion with core `useTheme` hook
|
|
44
45
|
*/
|
|
45
|
-
export declare const useTheme: () => UseThemeProps;
|
|
46
|
-
export declare const useThemeSetting: () => UseThemeProps;
|
|
46
|
+
export declare const useTheme: () => UseThemeProps | null;
|
|
47
|
+
export declare const useThemeSetting: () => UseThemeProps | null;
|
|
47
48
|
interface ValueObject {
|
|
48
49
|
[themeName: string]: string;
|
|
49
50
|
}
|
|
50
51
|
export declare const useRootTheme: () => readonly [string, React.Dispatch<React.SetStateAction<string>>];
|
|
51
52
|
export declare const NextThemeProvider: React.FC<ThemeProviderProps>;
|
|
53
|
+
export declare const Theme: React.FC<ThemeProviderProps>;
|
|
52
54
|
export {};
|
|
53
55
|
//# sourceMappingURL=NextTheme.d.ts.map
|