@tamagui/next-theme 1.0.15 → 1.0.17
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 +52 -76
- package/dist/cjs/NextTheme.js.map +3 -3
- package/dist/esm/NextTheme.js +52 -75
- package/dist/esm/NextTheme.js.map +2 -2
- package/package.json +3 -3
- package/src/NextTheme.tsx +84 -96
- package/types/NextTheme.d.ts +3 -4
package/dist/cjs/NextTheme.js
CHANGED
|
@@ -25,7 +25,6 @@ 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,
|
|
29
28
|
useRootTheme: () => useRootTheme,
|
|
30
29
|
useTheme: () => useTheme,
|
|
31
30
|
useThemeSetting: () => useThemeSetting
|
|
@@ -33,12 +32,19 @@ __export(NextTheme_exports, {
|
|
|
33
32
|
module.exports = __toCommonJS(NextTheme_exports);
|
|
34
33
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
35
34
|
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
|
-
|
|
39
|
+
const ThemeSettingContext = (0, import_react.createContext)({
|
|
40
|
+
toggle: () => {
|
|
41
|
+
},
|
|
42
|
+
set: (_) => {
|
|
43
|
+
},
|
|
44
|
+
themes: []
|
|
45
|
+
});
|
|
46
|
+
const useTheme = () => (0, import_react.useContext)(ThemeSettingContext);
|
|
47
|
+
const useThemeSetting = () => (0, import_react.useContext)(ThemeSettingContext);
|
|
42
48
|
const colorSchemes = ["light", "dark"];
|
|
43
49
|
const MEDIA = "(prefers-color-scheme: dark)";
|
|
44
50
|
const useRootTheme = () => {
|
|
@@ -54,19 +60,7 @@ const useRootTheme = () => {
|
|
|
54
60
|
}
|
|
55
61
|
return [val, setVal];
|
|
56
62
|
};
|
|
57
|
-
const NextThemeProvider = (
|
|
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,
|
|
63
|
+
const NextThemeProvider = ({
|
|
70
64
|
forcedTheme,
|
|
71
65
|
disableTransitionOnChange = true,
|
|
72
66
|
enableSystem = true,
|
|
@@ -75,6 +69,7 @@ const Theme = ({
|
|
|
75
69
|
themes = colorSchemes,
|
|
76
70
|
defaultTheme = enableSystem ? "system" : "light",
|
|
77
71
|
attribute = "class",
|
|
72
|
+
skipNextHead,
|
|
78
73
|
onChangeTheme,
|
|
79
74
|
value = {
|
|
80
75
|
dark: "t_dark",
|
|
@@ -188,23 +183,20 @@ const Theme = ({
|
|
|
188
183
|
themes,
|
|
189
184
|
systemTheme
|
|
190
185
|
]);
|
|
191
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
186
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ThemeSettingContext.Provider, {
|
|
192
187
|
value: contextValue,
|
|
193
188
|
children: [
|
|
194
189
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemeScript, {
|
|
195
190
|
...{
|
|
196
191
|
forcedTheme,
|
|
197
|
-
disableTransitionOnChange,
|
|
198
192
|
storageKey,
|
|
199
193
|
systemTheme: resolvedTheme,
|
|
200
194
|
attribute,
|
|
201
|
-
enableColorScheme,
|
|
202
195
|
value,
|
|
203
|
-
themes,
|
|
204
196
|
enableSystem,
|
|
205
197
|
defaultTheme,
|
|
206
198
|
attrs,
|
|
207
|
-
|
|
199
|
+
skipNextHead
|
|
208
200
|
}
|
|
209
201
|
}),
|
|
210
202
|
(0, import_react.useMemo)(() => children, [children])
|
|
@@ -217,73 +209,58 @@ const ThemeScript = (0, import_react.memo)(
|
|
|
217
209
|
storageKey,
|
|
218
210
|
attribute,
|
|
219
211
|
enableSystem,
|
|
220
|
-
enableColorScheme,
|
|
221
212
|
defaultTheme,
|
|
222
213
|
value,
|
|
223
214
|
attrs,
|
|
224
|
-
|
|
215
|
+
skipNextHead
|
|
225
216
|
}) => {
|
|
226
|
-
const defaultSystem = defaultTheme === "system";
|
|
227
217
|
const optimization = (() => {
|
|
228
218
|
if (attribute === "class") {
|
|
229
|
-
const removeClasses =
|
|
230
|
-
return `var d=document.documentElement
|
|
231
|
-
} else {
|
|
232
|
-
return `var d=document.documentElement,n='${attribute}',s='setAttribute';`;
|
|
233
|
-
}
|
|
234
|
-
})();
|
|
235
|
-
const fallbackColorScheme = (() => {
|
|
236
|
-
if (!enableColorScheme) {
|
|
237
|
-
return "";
|
|
238
|
-
}
|
|
239
|
-
const fallback = colorSchemes.includes(defaultTheme) ? defaultTheme : null;
|
|
240
|
-
if (fallback) {
|
|
241
|
-
return `if(e==='light'||e==='dark'||!e)d.style.colorScheme=e||'${defaultTheme}'`;
|
|
219
|
+
const removeClasses = attrs.map((t) => `d.remove('${t}')`).join(";");
|
|
220
|
+
return `var d=document.documentElement.classList;${removeClasses};`;
|
|
242
221
|
} else {
|
|
243
|
-
return `
|
|
222
|
+
return `var d=document.documentElement;`;
|
|
244
223
|
}
|
|
245
224
|
})();
|
|
246
|
-
const updateDOM = (name, literal
|
|
247
|
-
|
|
248
|
-
const val = literal ? name
|
|
249
|
-
let text = "";
|
|
250
|
-
if (enableColorScheme && setColorScheme && !literal && colorSchemes.includes(name)) {
|
|
251
|
-
text += `d.style.colorScheme = '${name}';`;
|
|
252
|
-
}
|
|
225
|
+
const updateDOM = (name, literal) => {
|
|
226
|
+
name = (value == null ? void 0 : value[name]) || name;
|
|
227
|
+
const val = literal ? name : `'${name}'`;
|
|
253
228
|
if (attribute === "class") {
|
|
254
|
-
|
|
255
|
-
text += `c.add(${val})`;
|
|
256
|
-
} else {
|
|
257
|
-
text += `null`;
|
|
258
|
-
}
|
|
259
|
-
} else {
|
|
260
|
-
if (resolvedName) {
|
|
261
|
-
text += `d[s](n,${val})`;
|
|
262
|
-
}
|
|
229
|
+
return `d.add(${val})`;
|
|
263
230
|
}
|
|
264
|
-
return
|
|
231
|
+
return `d.setAttribute('${attribute}', ${val})`;
|
|
265
232
|
};
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
233
|
+
const defaultSystem = defaultTheme === "system";
|
|
234
|
+
const contents = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, {
|
|
235
|
+
children: forcedTheme ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("script", {
|
|
236
|
+
dangerouslySetInnerHTML: {
|
|
237
|
+
__html: `!function(){${optimization}${updateDOM(forcedTheme)}}()`
|
|
238
|
+
}
|
|
239
|
+
}, "next-themes-script") : enableSystem ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("script", {
|
|
240
|
+
dangerouslySetInnerHTML: {
|
|
241
|
+
__html: `!function(){try {${optimization}var e=localStorage.getItem('${storageKey}');${!defaultSystem ? updateDOM(defaultTheme) + ";" : ""}if("system"===e||(!e&&${defaultSystem})){var t="${MEDIA}",m=window.matchMedia(t);m.media!==t||m.matches?${updateDOM(
|
|
242
|
+
"dark"
|
|
243
|
+
)}:${updateDOM("light")}}else if(e) ${value ? `var x=${JSON.stringify(value)};` : ""}${updateDOM(value ? "x[e]" : "e", true)}}catch(e){}}()`
|
|
244
|
+
}
|
|
245
|
+
}, "next-themes-script") : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("script", {
|
|
246
|
+
dangerouslySetInnerHTML: {
|
|
247
|
+
__html: `!function(){try{${optimization}var e=localStorage.getItem("${storageKey}");if(e){${value ? `var x=${JSON.stringify(value)};` : ""}${updateDOM(value ? "x[e]" : "e", true)}}else{${updateDOM(
|
|
248
|
+
defaultTheme
|
|
249
|
+
)};}}catch(t){}}();`
|
|
250
|
+
}
|
|
251
|
+
}, "next-themes-script")
|
|
252
|
+
});
|
|
253
|
+
if (skipNextHead)
|
|
254
|
+
return contents;
|
|
255
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_head.default, {
|
|
256
|
+
children: contents
|
|
284
257
|
});
|
|
285
258
|
},
|
|
286
|
-
() =>
|
|
259
|
+
(prevProps, nextProps) => {
|
|
260
|
+
if (prevProps.forcedTheme !== nextProps.forcedTheme)
|
|
261
|
+
return false;
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
287
264
|
);
|
|
288
265
|
const getTheme = (key, fallback) => {
|
|
289
266
|
if (typeof window === "undefined")
|
|
@@ -306,7 +283,6 @@ const getSystemTheme = (e) => {
|
|
|
306
283
|
// Annotate the CommonJS export names for ESM import in node:
|
|
307
284
|
0 && (module.exports = {
|
|
308
285
|
NextThemeProvider,
|
|
309
|
-
Theme,
|
|
310
286
|
useRootTheme,
|
|
311
287
|
useTheme,
|
|
312
288
|
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 * 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": ";;;;;;;;;;;;;;;;;;;;;;;;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 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 // avoids warning\n\n skipNextHead?: boolean\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 skipNextHead,\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 <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 skipNextHead,\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 skipNextHead,\n }: {\n forcedTheme?: string\n storageKey: string\n attribute?: string\n enableSystem?: boolean\n defaultTheme: string\n value?: ValueObject\n attrs: any\n skipNextHead\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 const contents = (\n <>\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 </>\n )\n\n if (skipNextHead) return contents\n\n return <NextHead>{contents}</NextHead>\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": "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",
|
|
6
|
+
"names": ["systemTheme", "theme", "value", "NextHead"]
|
|
7
7
|
}
|
package/dist/esm/NextTheme.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEvent } from "@tamagui/use-event";
|
|
3
|
+
import NextHead from "next/head";
|
|
3
4
|
import * as React from "react";
|
|
4
5
|
import {
|
|
5
6
|
createContext,
|
|
@@ -12,9 +13,15 @@ import {
|
|
|
12
13
|
useState
|
|
13
14
|
} from "react";
|
|
14
15
|
const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
const ThemeSettingContext = createContext({
|
|
17
|
+
toggle: () => {
|
|
18
|
+
},
|
|
19
|
+
set: (_) => {
|
|
20
|
+
},
|
|
21
|
+
themes: []
|
|
22
|
+
});
|
|
23
|
+
const useTheme = () => useContext(ThemeSettingContext);
|
|
24
|
+
const useThemeSetting = () => useContext(ThemeSettingContext);
|
|
18
25
|
const colorSchemes = ["light", "dark"];
|
|
19
26
|
const MEDIA = "(prefers-color-scheme: dark)";
|
|
20
27
|
const useRootTheme = () => {
|
|
@@ -30,19 +37,7 @@ const useRootTheme = () => {
|
|
|
30
37
|
}
|
|
31
38
|
return [val, setVal];
|
|
32
39
|
};
|
|
33
|
-
const NextThemeProvider = (
|
|
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,
|
|
40
|
+
const NextThemeProvider = ({
|
|
46
41
|
forcedTheme,
|
|
47
42
|
disableTransitionOnChange = true,
|
|
48
43
|
enableSystem = true,
|
|
@@ -51,6 +46,7 @@ const Theme = ({
|
|
|
51
46
|
themes = colorSchemes,
|
|
52
47
|
defaultTheme = enableSystem ? "system" : "light",
|
|
53
48
|
attribute = "class",
|
|
49
|
+
skipNextHead,
|
|
54
50
|
onChangeTheme,
|
|
55
51
|
value = {
|
|
56
52
|
dark: "t_dark",
|
|
@@ -164,23 +160,20 @@ const Theme = ({
|
|
|
164
160
|
themes,
|
|
165
161
|
systemTheme
|
|
166
162
|
]);
|
|
167
|
-
return /* @__PURE__ */ jsxs(
|
|
163
|
+
return /* @__PURE__ */ jsxs(ThemeSettingContext.Provider, {
|
|
168
164
|
value: contextValue,
|
|
169
165
|
children: [
|
|
170
166
|
/* @__PURE__ */ jsx(ThemeScript, {
|
|
171
167
|
...{
|
|
172
168
|
forcedTheme,
|
|
173
|
-
disableTransitionOnChange,
|
|
174
169
|
storageKey,
|
|
175
170
|
systemTheme: resolvedTheme,
|
|
176
171
|
attribute,
|
|
177
|
-
enableColorScheme,
|
|
178
172
|
value,
|
|
179
|
-
themes,
|
|
180
173
|
enableSystem,
|
|
181
174
|
defaultTheme,
|
|
182
175
|
attrs,
|
|
183
|
-
|
|
176
|
+
skipNextHead
|
|
184
177
|
}
|
|
185
178
|
}),
|
|
186
179
|
useMemo(() => children, [children])
|
|
@@ -193,73 +186,58 @@ const ThemeScript = memo(
|
|
|
193
186
|
storageKey,
|
|
194
187
|
attribute,
|
|
195
188
|
enableSystem,
|
|
196
|
-
enableColorScheme,
|
|
197
189
|
defaultTheme,
|
|
198
190
|
value,
|
|
199
191
|
attrs,
|
|
200
|
-
|
|
192
|
+
skipNextHead
|
|
201
193
|
}) => {
|
|
202
|
-
const defaultSystem = defaultTheme === "system";
|
|
203
194
|
const optimization = (() => {
|
|
204
195
|
if (attribute === "class") {
|
|
205
|
-
const removeClasses =
|
|
206
|
-
return `var d=document.documentElement
|
|
207
|
-
} else {
|
|
208
|
-
return `var d=document.documentElement,n='${attribute}',s='setAttribute';`;
|
|
209
|
-
}
|
|
210
|
-
})();
|
|
211
|
-
const fallbackColorScheme = (() => {
|
|
212
|
-
if (!enableColorScheme) {
|
|
213
|
-
return "";
|
|
214
|
-
}
|
|
215
|
-
const fallback = colorSchemes.includes(defaultTheme) ? defaultTheme : null;
|
|
216
|
-
if (fallback) {
|
|
217
|
-
return `if(e==='light'||e==='dark'||!e)d.style.colorScheme=e||'${defaultTheme}'`;
|
|
196
|
+
const removeClasses = attrs.map((t) => `d.remove('${t}')`).join(";");
|
|
197
|
+
return `var d=document.documentElement.classList;${removeClasses};`;
|
|
218
198
|
} else {
|
|
219
|
-
return `
|
|
199
|
+
return `var d=document.documentElement;`;
|
|
220
200
|
}
|
|
221
201
|
})();
|
|
222
|
-
const updateDOM = (name, literal
|
|
223
|
-
|
|
224
|
-
const val = literal ? name
|
|
225
|
-
let text = "";
|
|
226
|
-
if (enableColorScheme && setColorScheme && !literal && colorSchemes.includes(name)) {
|
|
227
|
-
text += `d.style.colorScheme = '${name}';`;
|
|
228
|
-
}
|
|
202
|
+
const updateDOM = (name, literal) => {
|
|
203
|
+
name = (value == null ? void 0 : value[name]) || name;
|
|
204
|
+
const val = literal ? name : `'${name}'`;
|
|
229
205
|
if (attribute === "class") {
|
|
230
|
-
|
|
231
|
-
text += `c.add(${val})`;
|
|
232
|
-
} else {
|
|
233
|
-
text += `null`;
|
|
234
|
-
}
|
|
235
|
-
} else {
|
|
236
|
-
if (resolvedName) {
|
|
237
|
-
text += `d[s](n,${val})`;
|
|
238
|
-
}
|
|
206
|
+
return `d.add(${val})`;
|
|
239
207
|
}
|
|
240
|
-
return
|
|
208
|
+
return `d.setAttribute('${attribute}', ${val})`;
|
|
241
209
|
};
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
210
|
+
const defaultSystem = defaultTheme === "system";
|
|
211
|
+
const contents = /* @__PURE__ */ jsx(Fragment, {
|
|
212
|
+
children: forcedTheme ? /* @__PURE__ */ jsx("script", {
|
|
213
|
+
dangerouslySetInnerHTML: {
|
|
214
|
+
__html: `!function(){${optimization}${updateDOM(forcedTheme)}}()`
|
|
215
|
+
}
|
|
216
|
+
}, "next-themes-script") : enableSystem ? /* @__PURE__ */ jsx("script", {
|
|
217
|
+
dangerouslySetInnerHTML: {
|
|
218
|
+
__html: `!function(){try {${optimization}var e=localStorage.getItem('${storageKey}');${!defaultSystem ? updateDOM(defaultTheme) + ";" : ""}if("system"===e||(!e&&${defaultSystem})){var t="${MEDIA}",m=window.matchMedia(t);m.media!==t||m.matches?${updateDOM(
|
|
219
|
+
"dark"
|
|
220
|
+
)}:${updateDOM("light")}}else if(e) ${value ? `var x=${JSON.stringify(value)};` : ""}${updateDOM(value ? "x[e]" : "e", true)}}catch(e){}}()`
|
|
221
|
+
}
|
|
222
|
+
}, "next-themes-script") : /* @__PURE__ */ jsx("script", {
|
|
223
|
+
dangerouslySetInnerHTML: {
|
|
224
|
+
__html: `!function(){try{${optimization}var e=localStorage.getItem("${storageKey}");if(e){${value ? `var x=${JSON.stringify(value)};` : ""}${updateDOM(value ? "x[e]" : "e", true)}}else{${updateDOM(
|
|
225
|
+
defaultTheme
|
|
226
|
+
)};}}catch(t){}}();`
|
|
227
|
+
}
|
|
228
|
+
}, "next-themes-script")
|
|
229
|
+
});
|
|
230
|
+
if (skipNextHead)
|
|
231
|
+
return contents;
|
|
232
|
+
return /* @__PURE__ */ jsx(NextHead, {
|
|
233
|
+
children: contents
|
|
260
234
|
});
|
|
261
235
|
},
|
|
262
|
-
() =>
|
|
236
|
+
(prevProps, nextProps) => {
|
|
237
|
+
if (prevProps.forcedTheme !== nextProps.forcedTheme)
|
|
238
|
+
return false;
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
263
241
|
);
|
|
264
242
|
const getTheme = (key, fallback) => {
|
|
265
243
|
if (typeof window === "undefined")
|
|
@@ -281,7 +259,6 @@ const getSystemTheme = (e) => {
|
|
|
281
259
|
};
|
|
282
260
|
export {
|
|
283
261
|
NextThemeProvider,
|
|
284
|
-
Theme,
|
|
285
262
|
useRootTheme,
|
|
286
263
|
useTheme,
|
|
287
264
|
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 * 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": "
|
|
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 // avoids warning\n\n skipNextHead?: boolean\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 skipNextHead,\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 <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 skipNextHead,\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 skipNextHead,\n }: {\n forcedTheme?: string\n storageKey: string\n attribute?: string\n enableSystem?: boolean\n defaultTheme: string\n value?: ValueObject\n attrs: any\n skipNextHead\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 const contents = (\n <>\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 </>\n )\n\n if (skipNextHead) return contents\n\n return <NextHead>{contents}</NextHead>\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": "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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.17",
|
|
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.17"
|
|
20
20
|
},
|
|
21
21
|
"peerDependencies": {
|
|
22
22
|
"react": "*"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@tamagui/build": "^1.0.
|
|
25
|
+
"@tamagui/build": "^1.0.17",
|
|
26
26
|
"react": "^18.2.0"
|
|
27
27
|
},
|
|
28
28
|
"publishConfig": {
|
package/src/NextTheme.tsx
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
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'
|
|
5
6
|
import * as React from 'react'
|
|
6
7
|
import {
|
|
7
8
|
createContext,
|
|
@@ -57,17 +58,24 @@ export interface ThemeProviderProps {
|
|
|
57
58
|
/** Mapping of theme name to HTML attribute value. Object where key is the theme name and value is the attribute value */
|
|
58
59
|
value?: ValueObject
|
|
59
60
|
onChangeTheme?: (name: string) => void
|
|
60
|
-
|
|
61
|
+
|
|
62
|
+
// avoids warning
|
|
63
|
+
|
|
64
|
+
skipNextHead?: boolean
|
|
61
65
|
}
|
|
62
66
|
|
|
63
|
-
const
|
|
67
|
+
const ThemeSettingContext = createContext<UseThemeProps>({
|
|
68
|
+
toggle: () => {},
|
|
69
|
+
set: (_) => {},
|
|
70
|
+
themes: [],
|
|
71
|
+
})
|
|
64
72
|
|
|
65
73
|
/**
|
|
66
74
|
* @deprecated renamed to `useThemeSetting` to avoid confusion with core `useTheme` hook
|
|
67
75
|
*/
|
|
68
|
-
export const useTheme = () => useContext(
|
|
76
|
+
export const useTheme = () => useContext(ThemeSettingContext)
|
|
69
77
|
|
|
70
|
-
export const useThemeSetting = () => useContext(
|
|
78
|
+
export const useThemeSetting = () => useContext(ThemeSettingContext)
|
|
71
79
|
|
|
72
80
|
const colorSchemes = ['light', 'dark']
|
|
73
81
|
const MEDIA = '(prefers-color-scheme: dark)'
|
|
@@ -94,17 +102,7 @@ export const useRootTheme = () => {
|
|
|
94
102
|
return [val, setVal] as const
|
|
95
103
|
}
|
|
96
104
|
|
|
97
|
-
export const NextThemeProvider: React.FC<ThemeProviderProps> = (
|
|
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,
|
|
105
|
+
export const NextThemeProvider: React.FC<ThemeProviderProps> = ({
|
|
108
106
|
forcedTheme,
|
|
109
107
|
disableTransitionOnChange = true,
|
|
110
108
|
enableSystem = true,
|
|
@@ -113,6 +111,7 @@ export const Theme: React.FC<ThemeProviderProps> = ({
|
|
|
113
111
|
themes = colorSchemes,
|
|
114
112
|
defaultTheme = enableSystem ? 'system' : 'light',
|
|
115
113
|
attribute = 'class',
|
|
114
|
+
skipNextHead,
|
|
116
115
|
onChangeTheme,
|
|
117
116
|
value = {
|
|
118
117
|
dark: 't_dark',
|
|
@@ -274,26 +273,23 @@ export const Theme: React.FC<ThemeProviderProps> = ({
|
|
|
274
273
|
])
|
|
275
274
|
|
|
276
275
|
return (
|
|
277
|
-
<
|
|
276
|
+
<ThemeSettingContext.Provider value={contextValue}>
|
|
278
277
|
<ThemeScript
|
|
279
278
|
{...{
|
|
280
279
|
forcedTheme,
|
|
281
|
-
disableTransitionOnChange,
|
|
282
280
|
storageKey,
|
|
283
281
|
systemTheme: resolvedTheme,
|
|
284
282
|
attribute,
|
|
285
|
-
enableColorScheme,
|
|
286
283
|
value,
|
|
287
|
-
themes,
|
|
288
284
|
enableSystem,
|
|
289
285
|
defaultTheme,
|
|
290
286
|
attrs,
|
|
291
|
-
|
|
287
|
+
skipNextHead,
|
|
292
288
|
}}
|
|
293
289
|
/>
|
|
294
290
|
{/* because on SSR we re-run and can avoid whole tree re-render */}
|
|
295
291
|
{useMemo(() => children, [children])}
|
|
296
|
-
</
|
|
292
|
+
</ThemeSettingContext.Provider>
|
|
297
293
|
)
|
|
298
294
|
}
|
|
299
295
|
|
|
@@ -303,99 +299,91 @@ const ThemeScript = memo(
|
|
|
303
299
|
storageKey,
|
|
304
300
|
attribute,
|
|
305
301
|
enableSystem,
|
|
306
|
-
enableColorScheme,
|
|
307
302
|
defaultTheme,
|
|
308
303
|
value,
|
|
309
304
|
attrs,
|
|
310
|
-
|
|
311
|
-
}:
|
|
312
|
-
|
|
313
|
-
|
|
305
|
+
skipNextHead,
|
|
306
|
+
}: {
|
|
307
|
+
forcedTheme?: string
|
|
308
|
+
storageKey: string
|
|
309
|
+
attribute?: string
|
|
310
|
+
enableSystem?: boolean
|
|
311
|
+
defaultTheme: string
|
|
312
|
+
value?: ValueObject
|
|
313
|
+
attrs: any
|
|
314
|
+
skipNextHead
|
|
315
|
+
}) => {
|
|
314
316
|
// Code-golfing the amount of characters in the script
|
|
315
317
|
const optimization = (() => {
|
|
316
318
|
if (attribute === 'class') {
|
|
317
|
-
const removeClasses =
|
|
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}'`
|
|
319
|
+
const removeClasses = attrs.map((t: string) => `d.remove('${t}')`).join(';')
|
|
320
|
+
return `var d=document.documentElement.classList;${removeClasses};`
|
|
334
321
|
} else {
|
|
335
|
-
return `
|
|
322
|
+
return `var d=document.documentElement;`
|
|
336
323
|
}
|
|
337
324
|
})()
|
|
338
325
|
|
|
339
|
-
const updateDOM = (name: string, literal
|
|
340
|
-
|
|
341
|
-
const val = literal ? name
|
|
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
|
-
}
|
|
326
|
+
const updateDOM = (name: string, literal?: boolean) => {
|
|
327
|
+
name = value?.[name] || name
|
|
328
|
+
const val = literal ? name : `'${name}'`
|
|
355
329
|
|
|
356
330
|
if (attribute === 'class') {
|
|
357
|
-
|
|
358
|
-
text += `c.add(${val})`
|
|
359
|
-
} else {
|
|
360
|
-
text += `null`
|
|
361
|
-
}
|
|
362
|
-
} else {
|
|
363
|
-
if (resolvedName) {
|
|
364
|
-
text += `d[s](n,${val})`
|
|
365
|
-
}
|
|
331
|
+
return `d.add(${val})`
|
|
366
332
|
}
|
|
367
333
|
|
|
368
|
-
return
|
|
334
|
+
return `d.setAttribute('${attribute}', ${val})`
|
|
369
335
|
}
|
|
370
336
|
|
|
371
|
-
const
|
|
372
|
-
if (forcedTheme) {
|
|
373
|
-
return `!function(){${optimization}${updateDOM(forcedTheme)}}()`
|
|
374
|
-
}
|
|
375
|
-
|
|
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
|
-
})()
|
|
337
|
+
const defaultSystem = defaultTheme === 'system'
|
|
394
338
|
|
|
395
|
-
|
|
339
|
+
const contents = (
|
|
340
|
+
<>
|
|
341
|
+
{forcedTheme ? (
|
|
342
|
+
<script
|
|
343
|
+
key="next-themes-script"
|
|
344
|
+
dangerouslySetInnerHTML={{
|
|
345
|
+
// These are minified via Terser and then updated by hand, don't recommend
|
|
346
|
+
__html: `!function(){${optimization}${updateDOM(forcedTheme)}}()`,
|
|
347
|
+
}}
|
|
348
|
+
/>
|
|
349
|
+
) : enableSystem ? (
|
|
350
|
+
<script
|
|
351
|
+
key="next-themes-script"
|
|
352
|
+
dangerouslySetInnerHTML={{
|
|
353
|
+
__html: `!function(){try {${optimization}var e=localStorage.getItem('${storageKey}');${
|
|
354
|
+
!defaultSystem ? updateDOM(defaultTheme) + ';' : ''
|
|
355
|
+
}if("system"===e||(!e&&${defaultSystem})){var t="${MEDIA}",m=window.matchMedia(t);m.media!==t||m.matches?${updateDOM(
|
|
356
|
+
'dark'
|
|
357
|
+
)}:${updateDOM('light')}}else if(e) ${
|
|
358
|
+
value ? `var x=${JSON.stringify(value)};` : ''
|
|
359
|
+
}${updateDOM(value ? 'x[e]' : 'e', true)}}catch(e){}}()`,
|
|
360
|
+
}}
|
|
361
|
+
/>
|
|
362
|
+
) : (
|
|
363
|
+
<script
|
|
364
|
+
key="next-themes-script"
|
|
365
|
+
dangerouslySetInnerHTML={{
|
|
366
|
+
__html: `!function(){try{${optimization}var e=localStorage.getItem("${storageKey}");if(e){${
|
|
367
|
+
value ? `var x=${JSON.stringify(value)};` : ''
|
|
368
|
+
}${updateDOM(value ? 'x[e]' : 'e', true)}}else{${updateDOM(
|
|
369
|
+
defaultTheme
|
|
370
|
+
)};}}catch(t){}}();`,
|
|
371
|
+
}}
|
|
372
|
+
/>
|
|
373
|
+
)}
|
|
374
|
+
</>
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
if (skipNextHead) return contents
|
|
378
|
+
|
|
379
|
+
return <NextHead>{contents}</NextHead>
|
|
396
380
|
},
|
|
397
|
-
|
|
398
|
-
|
|
381
|
+
(prevProps, nextProps) => {
|
|
382
|
+
// Only re-render when forcedTheme changes
|
|
383
|
+
// the rest of the props should be completely stable
|
|
384
|
+
if (prevProps.forcedTheme !== nextProps.forcedTheme) return false
|
|
385
|
+
return true
|
|
386
|
+
}
|
|
399
387
|
)
|
|
400
388
|
|
|
401
389
|
// Helpers
|
package/types/NextTheme.d.ts
CHANGED
|
@@ -38,18 +38,17 @@ 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
|
-
|
|
41
|
+
skipNextHead?: boolean;
|
|
42
42
|
}
|
|
43
43
|
/**
|
|
44
44
|
* @deprecated renamed to `useThemeSetting` to avoid confusion with core `useTheme` hook
|
|
45
45
|
*/
|
|
46
|
-
export declare const useTheme: () => UseThemeProps
|
|
47
|
-
export declare const useThemeSetting: () => UseThemeProps
|
|
46
|
+
export declare const useTheme: () => UseThemeProps;
|
|
47
|
+
export declare const useThemeSetting: () => UseThemeProps;
|
|
48
48
|
interface ValueObject {
|
|
49
49
|
[themeName: string]: string;
|
|
50
50
|
}
|
|
51
51
|
export declare const useRootTheme: () => readonly [string, React.Dispatch<React.SetStateAction<string>>];
|
|
52
52
|
export declare const NextThemeProvider: React.FC<ThemeProviderProps>;
|
|
53
|
-
export declare const Theme: React.FC<ThemeProviderProps>;
|
|
54
53
|
export {};
|
|
55
54
|
//# sourceMappingURL=NextTheme.d.ts.map
|