@tamagui/next-theme 1.0.1-beta.166 → 1.0.1-beta.168
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 +38 -38
- package/dist/cjs/NextTheme.js.map +3 -3
- package/dist/esm/NextTheme.js +36 -38
- package/dist/esm/NextTheme.js.map +3 -3
- package/package.json +5 -2
- package/src/NextTheme.tsx +45 -40
- package/types/NextTheme.d.ts +23 -2
package/dist/cjs/NextTheme.js
CHANGED
|
@@ -26,22 +26,25 @@ var NextTheme_exports = {};
|
|
|
26
26
|
__export(NextTheme_exports, {
|
|
27
27
|
NextThemeProvider: () => NextThemeProvider,
|
|
28
28
|
useRootTheme: () => useRootTheme,
|
|
29
|
-
useTheme: () => useTheme
|
|
29
|
+
useTheme: () => useTheme,
|
|
30
|
+
useThemeSetting: () => useThemeSetting
|
|
30
31
|
});
|
|
31
32
|
module.exports = __toCommonJS(NextTheme_exports);
|
|
32
33
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
34
|
+
var import_use_event = require("@tamagui/use-event");
|
|
33
35
|
var import_head = __toESM(require("next/head"));
|
|
34
36
|
var React = __toESM(require("react"));
|
|
35
37
|
var import_react = require("react");
|
|
36
38
|
const useIsomorphicLayoutEffect = typeof window !== "undefined" ? import_react.useLayoutEffect : import_react.useEffect;
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
+
const ThemeSettingContext = (0, import_react.createContext)({
|
|
40
|
+
toggle: () => {
|
|
39
41
|
},
|
|
40
|
-
|
|
42
|
+
set: (_) => {
|
|
41
43
|
},
|
|
42
44
|
themes: []
|
|
43
45
|
});
|
|
44
|
-
const useTheme = () => (0, import_react.useContext)(
|
|
46
|
+
const useTheme = () => (0, import_react.useContext)(ThemeSettingContext);
|
|
47
|
+
const useThemeSetting = () => (0, import_react.useContext)(ThemeSettingContext);
|
|
45
48
|
const colorSchemes = ["light", "dark"];
|
|
46
49
|
const MEDIA = "(prefers-color-scheme: dark)";
|
|
47
50
|
const useRootTheme = () => {
|
|
@@ -69,20 +72,17 @@ const NextThemeProvider = ({
|
|
|
69
72
|
const [theme, setThemeState] = (0, import_react.useState)(() => getTheme(storageKey, defaultTheme));
|
|
70
73
|
const [resolvedTheme, setResolvedTheme] = (0, import_react.useState)(() => getTheme(storageKey));
|
|
71
74
|
const attrs = !value ? themes : Object.values(value);
|
|
72
|
-
const handleMediaQuery = (0,
|
|
73
|
-
(e)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
},
|
|
81
|
-
[theme, forcedTheme]
|
|
82
|
-
);
|
|
75
|
+
const handleMediaQuery = (0, import_use_event.useEvent)((e) => {
|
|
76
|
+
const systemTheme = getSystemTheme(e);
|
|
77
|
+
React.startTransition(() => {
|
|
78
|
+
setResolvedTheme(systemTheme);
|
|
79
|
+
});
|
|
80
|
+
if (theme === "system" && !forcedTheme)
|
|
81
|
+
handleChangeTheme(systemTheme, false);
|
|
82
|
+
});
|
|
83
83
|
const mediaListener = (0, import_react.useRef)(handleMediaQuery);
|
|
84
84
|
mediaListener.current = handleMediaQuery;
|
|
85
|
-
const handleChangeTheme = (0,
|
|
85
|
+
const handleChangeTheme = (0, import_use_event.useEvent)((theme2, updateStorage = true, updateDOM = true) => {
|
|
86
86
|
let name = (value == null ? void 0 : value[theme2]) || theme2;
|
|
87
87
|
if (updateStorage) {
|
|
88
88
|
try {
|
|
@@ -104,7 +104,7 @@ const NextThemeProvider = ({
|
|
|
104
104
|
d.setAttribute(attribute, name);
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
|
-
}
|
|
107
|
+
});
|
|
108
108
|
useIsomorphicLayoutEffect(() => {
|
|
109
109
|
const handler = (...args) => mediaListener.current(...args);
|
|
110
110
|
const media = window.matchMedia(MEDIA);
|
|
@@ -114,30 +114,27 @@ const NextThemeProvider = ({
|
|
|
114
114
|
media.removeListener(handler);
|
|
115
115
|
};
|
|
116
116
|
}, []);
|
|
117
|
-
const
|
|
118
|
-
(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
},
|
|
126
|
-
[forcedTheme]
|
|
127
|
-
);
|
|
117
|
+
const set = (0, import_use_event.useEvent)((newTheme) => {
|
|
118
|
+
if (forcedTheme) {
|
|
119
|
+
handleChangeTheme(newTheme, true, false);
|
|
120
|
+
} else {
|
|
121
|
+
handleChangeTheme(newTheme);
|
|
122
|
+
}
|
|
123
|
+
setThemeState(newTheme);
|
|
124
|
+
});
|
|
128
125
|
(0, import_react.useEffect)(() => {
|
|
129
126
|
const handleStorage = (e) => {
|
|
130
127
|
if (e.key !== storageKey) {
|
|
131
128
|
return;
|
|
132
129
|
}
|
|
133
130
|
const theme2 = e.newValue || defaultTheme;
|
|
134
|
-
|
|
131
|
+
set(theme2);
|
|
135
132
|
};
|
|
136
133
|
window.addEventListener("storage", handleStorage);
|
|
137
134
|
return () => {
|
|
138
135
|
window.removeEventListener("storage", handleStorage);
|
|
139
136
|
};
|
|
140
|
-
}, []);
|
|
137
|
+
}, [defaultTheme, set, storageKey]);
|
|
141
138
|
useIsomorphicLayoutEffect(() => {
|
|
142
139
|
if (!enableColorScheme)
|
|
143
140
|
return;
|
|
@@ -149,21 +146,23 @@ const NextThemeProvider = ({
|
|
|
149
146
|
}
|
|
150
147
|
}, [enableColorScheme, theme, resolvedTheme, forcedTheme]);
|
|
151
148
|
const contextValue = (0, import_react.useMemo)(() => {
|
|
152
|
-
|
|
149
|
+
const value2 = {
|
|
153
150
|
theme,
|
|
154
|
-
|
|
155
|
-
|
|
151
|
+
current: theme,
|
|
152
|
+
set,
|
|
153
|
+
toggle() {
|
|
156
154
|
const order = resolvedTheme === "dark" ? ["system", "light", "dark"] : ["system", "dark", "light"];
|
|
157
155
|
const next = order[(order.indexOf(theme) + 1) % order.length];
|
|
158
|
-
|
|
156
|
+
set(next);
|
|
159
157
|
},
|
|
160
158
|
forcedTheme,
|
|
161
159
|
resolvedTheme: theme === "system" ? resolvedTheme : theme,
|
|
162
160
|
themes: enableSystem ? [...themes, "system"] : themes,
|
|
163
161
|
systemTheme: enableSystem ? resolvedTheme : void 0
|
|
164
162
|
};
|
|
165
|
-
|
|
166
|
-
|
|
163
|
+
return value2;
|
|
164
|
+
}, [theme, set, forcedTheme, resolvedTheme, enableSystem, themes]);
|
|
165
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ThemeSettingContext.Provider, {
|
|
167
166
|
value: contextValue,
|
|
168
167
|
children: [
|
|
169
168
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemeScript, {
|
|
@@ -253,6 +252,7 @@ const getSystemTheme = (e) => {
|
|
|
253
252
|
0 && (module.exports = {
|
|
254
253
|
NextThemeProvider,
|
|
255
254
|
useRootTheme,
|
|
256
|
-
useTheme
|
|
255
|
+
useTheme,
|
|
256
|
+
useThemeSetting
|
|
257
257
|
});
|
|
258
258
|
//# sourceMappingURL=NextTheme.js.map
|
|
@@ -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 NextHead from 'next/head'\nimport * as React from 'react'\nimport {\n createContext,\n memo,\n useCallback,\n useContext,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nconst useIsomorphicLayoutEffect = 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 setTheme: (theme: string) => void\n toggleTheme: () => void\n /** Active theme name */\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 ThemeContext = createContext<UseThemeProps>({\n toggleTheme: () => {},\n setTheme: (_) => {},\n themes: [],\n})\nexport const useTheme = () => useContext(ThemeContext)\n\nconst colorSchemes = ['light', 'dark']\nconst MEDIA = '(prefers-color-scheme: dark)'\n\ninterface ValueObject {\n [themeName: string]: string\n}\n\nexport const useRootTheme = () => {\n const isClient = typeof document !== 'undefined'\n // @ts-ignore\n const classes = isClient ? [...document.documentElement.classList] : []\n const isDark = classes.includes('t_dark')\n return useState(isDark ? 'dark' : 'light')\n}\n\nexport const NextThemeProvider: React.FC<ThemeProviderProps> = ({\n forcedTheme,\n disableTransitionOnChange = true,\n enableSystem = true,\n enableColorScheme = true,\n storageKey = 'theme',\n themes = ['light', 'dark'],\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 = useCallback(\n (e?) => {\n const systemTheme = getSystemTheme(e)\n React.startTransition(() => {\n setResolvedTheme(systemTheme)\n })\n if (theme === 'system' && !forcedTheme) handleChangeTheme(systemTheme, false)\n },\n [theme, forcedTheme]\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 = useCallback((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 setTheme = useCallback(\n (newTheme) => {\n if (forcedTheme) {\n handleChangeTheme(newTheme, true, false)\n } else {\n handleChangeTheme(newTheme)\n }\n setThemeState(newTheme)\n },\n [forcedTheme]\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 setTheme(theme)\n }\n window.addEventListener('storage', handleStorage)\n return () => {\n window.removeEventListener('storage', handleStorage)\n }\n }, [])\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 contextValue = useMemo(() => {\n return {\n theme,\n setTheme,\n toggleTheme() {\n const order =\n resolvedTheme === 'dark' ? ['system', 'light', 'dark'] : ['system', 'dark', 'light']\n const next = order[(order.indexOf(theme) + 1) % order.length]\n setTheme(next)\n },\n forcedTheme,\n resolvedTheme: theme === 'system' ? resolvedTheme : theme,\n themes: enableSystem ? [...themes, 'system'] : themes,\n systemTheme: (enableSystem ? resolvedTheme : undefined) as 'light' | 'dark' | undefined,\n } as const\n }, [theme, forcedTheme, resolvedTheme, enableSystem])\n\n return (\n <ThemeContext.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 {children}\n </ThemeContext.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 // prettier-ignore\n __html: `!function(){${optimization}${updateDOM(forcedTheme)}}()`,\n }}\n />\n ) : enableSystem ? (\n <script\n key=\"next-themes-script\"\n dangerouslySetInnerHTML={{\n // prettier-ignore\n __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('dark')}:${updateDOM('light')}}else if(e) ${value ? `var x=${JSON.stringify(value)};` : ''}${updateDOM(value ? 'x[e]' : 'e', true)}}catch(e){}}()`,\n }}\n />\n ) : (\n <script\n key=\"next-themes-script\"\n dangerouslySetInnerHTML={{\n // prettier-ignore\n __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(defaultTheme)};}}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;
|
|
6
|
-
"names": ["theme", "NextHead"]
|
|
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 = 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\nexport const useRootTheme = () => {\n const isClient = typeof document !== 'undefined'\n // @ts-ignore\n const classes = isClient ? [...document.documentElement.classList] : []\n const isDark = classes.includes('t_dark')\n return useState(isDark ? 'dark' : 'light')\n}\n\nexport const NextThemeProvider: React.FC<ThemeProviderProps> = ({\n forcedTheme,\n disableTransitionOnChange = true,\n enableSystem = true,\n enableColorScheme = true,\n storageKey = 'theme',\n themes = ['light', 'dark'],\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) handleChangeTheme(systemTheme, false)\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 contextValue = useMemo(() => {\n const value: UseThemeProps = {\n theme,\n current: theme,\n set,\n toggle() {\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 forcedTheme,\n resolvedTheme: theme === 'system' ? resolvedTheme : theme,\n themes: enableSystem ? [...themes, 'system'] : themes,\n systemTheme: (enableSystem ? resolvedTheme : undefined) as 'light' | 'dark' | undefined,\n } as const\n\n return value\n }, [theme, set, forcedTheme, resolvedTheme, enableSystem, themes])\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 {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 // prettier-ignore\n __html: `!function(){${optimization}${updateDOM(forcedTheme)}}()`,\n }}\n />\n ) : enableSystem ? (\n <script\n key=\"next-themes-script\"\n dangerouslySetInnerHTML={{\n // prettier-ignore\n __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('dark')}:${updateDOM('light')}}else if(e) ${value ? `var x=${JSON.stringify(value)};` : ''}${updateDOM(value ? 'x[e]' : 'e', true)}}catch(e){}}()`,\n }}\n />\n ) : (\n <script\n key=\"next-themes-script\"\n dangerouslySetInnerHTML={{\n // prettier-ignore\n __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(defaultTheme)};}}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": "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",
|
|
6
|
+
"names": ["theme", "value", "NextHead"]
|
|
7
7
|
}
|
package/dist/esm/NextTheme.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEvent } from "@tamagui/use-event";
|
|
2
3
|
import NextHead from "next/head";
|
|
3
4
|
import * as React from "react";
|
|
4
5
|
import {
|
|
5
6
|
createContext,
|
|
6
7
|
memo,
|
|
7
|
-
useCallback,
|
|
8
8
|
useContext,
|
|
9
9
|
useEffect,
|
|
10
10
|
useLayoutEffect,
|
|
@@ -13,14 +13,15 @@ import {
|
|
|
13
13
|
useState
|
|
14
14
|
} from "react";
|
|
15
15
|
const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
|
|
16
|
-
const
|
|
17
|
-
|
|
16
|
+
const ThemeSettingContext = createContext({
|
|
17
|
+
toggle: () => {
|
|
18
18
|
},
|
|
19
|
-
|
|
19
|
+
set: (_) => {
|
|
20
20
|
},
|
|
21
21
|
themes: []
|
|
22
22
|
});
|
|
23
|
-
const useTheme = () => useContext(
|
|
23
|
+
const useTheme = () => useContext(ThemeSettingContext);
|
|
24
|
+
const useThemeSetting = () => useContext(ThemeSettingContext);
|
|
24
25
|
const colorSchemes = ["light", "dark"];
|
|
25
26
|
const MEDIA = "(prefers-color-scheme: dark)";
|
|
26
27
|
const useRootTheme = () => {
|
|
@@ -48,20 +49,17 @@ const NextThemeProvider = ({
|
|
|
48
49
|
const [theme, setThemeState] = useState(() => getTheme(storageKey, defaultTheme));
|
|
49
50
|
const [resolvedTheme, setResolvedTheme] = useState(() => getTheme(storageKey));
|
|
50
51
|
const attrs = !value ? themes : Object.values(value);
|
|
51
|
-
const handleMediaQuery =
|
|
52
|
-
(e)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
},
|
|
60
|
-
[theme, forcedTheme]
|
|
61
|
-
);
|
|
52
|
+
const handleMediaQuery = useEvent((e) => {
|
|
53
|
+
const systemTheme = getSystemTheme(e);
|
|
54
|
+
React.startTransition(() => {
|
|
55
|
+
setResolvedTheme(systemTheme);
|
|
56
|
+
});
|
|
57
|
+
if (theme === "system" && !forcedTheme)
|
|
58
|
+
handleChangeTheme(systemTheme, false);
|
|
59
|
+
});
|
|
62
60
|
const mediaListener = useRef(handleMediaQuery);
|
|
63
61
|
mediaListener.current = handleMediaQuery;
|
|
64
|
-
const handleChangeTheme =
|
|
62
|
+
const handleChangeTheme = useEvent((theme2, updateStorage = true, updateDOM = true) => {
|
|
65
63
|
let name = (value == null ? void 0 : value[theme2]) || theme2;
|
|
66
64
|
if (updateStorage) {
|
|
67
65
|
try {
|
|
@@ -83,7 +81,7 @@ const NextThemeProvider = ({
|
|
|
83
81
|
d.setAttribute(attribute, name);
|
|
84
82
|
}
|
|
85
83
|
}
|
|
86
|
-
}
|
|
84
|
+
});
|
|
87
85
|
useIsomorphicLayoutEffect(() => {
|
|
88
86
|
const handler = (...args) => mediaListener.current(...args);
|
|
89
87
|
const media = window.matchMedia(MEDIA);
|
|
@@ -93,30 +91,27 @@ const NextThemeProvider = ({
|
|
|
93
91
|
media.removeListener(handler);
|
|
94
92
|
};
|
|
95
93
|
}, []);
|
|
96
|
-
const
|
|
97
|
-
(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
},
|
|
105
|
-
[forcedTheme]
|
|
106
|
-
);
|
|
94
|
+
const set = useEvent((newTheme) => {
|
|
95
|
+
if (forcedTheme) {
|
|
96
|
+
handleChangeTheme(newTheme, true, false);
|
|
97
|
+
} else {
|
|
98
|
+
handleChangeTheme(newTheme);
|
|
99
|
+
}
|
|
100
|
+
setThemeState(newTheme);
|
|
101
|
+
});
|
|
107
102
|
useEffect(() => {
|
|
108
103
|
const handleStorage = (e) => {
|
|
109
104
|
if (e.key !== storageKey) {
|
|
110
105
|
return;
|
|
111
106
|
}
|
|
112
107
|
const theme2 = e.newValue || defaultTheme;
|
|
113
|
-
|
|
108
|
+
set(theme2);
|
|
114
109
|
};
|
|
115
110
|
window.addEventListener("storage", handleStorage);
|
|
116
111
|
return () => {
|
|
117
112
|
window.removeEventListener("storage", handleStorage);
|
|
118
113
|
};
|
|
119
|
-
}, []);
|
|
114
|
+
}, [defaultTheme, set, storageKey]);
|
|
120
115
|
useIsomorphicLayoutEffect(() => {
|
|
121
116
|
if (!enableColorScheme)
|
|
122
117
|
return;
|
|
@@ -128,21 +123,23 @@ const NextThemeProvider = ({
|
|
|
128
123
|
}
|
|
129
124
|
}, [enableColorScheme, theme, resolvedTheme, forcedTheme]);
|
|
130
125
|
const contextValue = useMemo(() => {
|
|
131
|
-
|
|
126
|
+
const value2 = {
|
|
132
127
|
theme,
|
|
133
|
-
|
|
134
|
-
|
|
128
|
+
current: theme,
|
|
129
|
+
set,
|
|
130
|
+
toggle() {
|
|
135
131
|
const order = resolvedTheme === "dark" ? ["system", "light", "dark"] : ["system", "dark", "light"];
|
|
136
132
|
const next = order[(order.indexOf(theme) + 1) % order.length];
|
|
137
|
-
|
|
133
|
+
set(next);
|
|
138
134
|
},
|
|
139
135
|
forcedTheme,
|
|
140
136
|
resolvedTheme: theme === "system" ? resolvedTheme : theme,
|
|
141
137
|
themes: enableSystem ? [...themes, "system"] : themes,
|
|
142
138
|
systemTheme: enableSystem ? resolvedTheme : void 0
|
|
143
139
|
};
|
|
144
|
-
|
|
145
|
-
|
|
140
|
+
return value2;
|
|
141
|
+
}, [theme, set, forcedTheme, resolvedTheme, enableSystem, themes]);
|
|
142
|
+
return /* @__PURE__ */ jsxs(ThemeSettingContext.Provider, {
|
|
146
143
|
value: contextValue,
|
|
147
144
|
children: [
|
|
148
145
|
/* @__PURE__ */ jsx(ThemeScript, {
|
|
@@ -231,6 +228,7 @@ const getSystemTheme = (e) => {
|
|
|
231
228
|
export {
|
|
232
229
|
NextThemeProvider,
|
|
233
230
|
useRootTheme,
|
|
234
|
-
useTheme
|
|
231
|
+
useTheme,
|
|
232
|
+
useThemeSetting
|
|
235
233
|
};
|
|
236
234
|
//# sourceMappingURL=NextTheme.js.map
|
|
@@ -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 NextHead from 'next/head'\nimport * as React from 'react'\nimport {\n createContext,\n memo,\n useCallback,\n useContext,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nconst useIsomorphicLayoutEffect = 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 setTheme: (theme: string) => void\n toggleTheme: () => void\n /** Active theme name */\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 ThemeContext = createContext<UseThemeProps>({\n toggleTheme: () => {},\n setTheme: (_) => {},\n themes: [],\n})\nexport const useTheme = () => useContext(ThemeContext)\n\nconst colorSchemes = ['light', 'dark']\nconst MEDIA = '(prefers-color-scheme: dark)'\n\ninterface ValueObject {\n [themeName: string]: string\n}\n\nexport const useRootTheme = () => {\n const isClient = typeof document !== 'undefined'\n // @ts-ignore\n const classes = isClient ? [...document.documentElement.classList] : []\n const isDark = classes.includes('t_dark')\n return useState(isDark ? 'dark' : 'light')\n}\n\nexport const NextThemeProvider: React.FC<ThemeProviderProps> = ({\n forcedTheme,\n disableTransitionOnChange = true,\n enableSystem = true,\n enableColorScheme = true,\n storageKey = 'theme',\n themes = ['light', 'dark'],\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 = useCallback(\n (e?) => {\n const systemTheme = getSystemTheme(e)\n React.startTransition(() => {\n setResolvedTheme(systemTheme)\n })\n if (theme === 'system' && !forcedTheme) handleChangeTheme(systemTheme, false)\n },\n [theme, forcedTheme]\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 = useCallback((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 setTheme = useCallback(\n (newTheme) => {\n if (forcedTheme) {\n handleChangeTheme(newTheme, true, false)\n } else {\n handleChangeTheme(newTheme)\n }\n setThemeState(newTheme)\n },\n [forcedTheme]\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 setTheme(theme)\n }\n window.addEventListener('storage', handleStorage)\n return () => {\n window.removeEventListener('storage', handleStorage)\n }\n }, [])\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 contextValue = useMemo(() => {\n return {\n theme,\n setTheme,\n toggleTheme() {\n const order =\n resolvedTheme === 'dark' ? ['system', 'light', 'dark'] : ['system', 'dark', 'light']\n const next = order[(order.indexOf(theme) + 1) % order.length]\n setTheme(next)\n },\n forcedTheme,\n resolvedTheme: theme === 'system' ? resolvedTheme : theme,\n themes: enableSystem ? [...themes, 'system'] : themes,\n systemTheme: (enableSystem ? resolvedTheme : undefined) as 'light' | 'dark' | undefined,\n } as const\n }, [theme, forcedTheme, resolvedTheme, enableSystem])\n\n return (\n <ThemeContext.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 {children}\n </ThemeContext.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 // prettier-ignore\n __html: `!function(){${optimization}${updateDOM(forcedTheme)}}()`,\n }}\n />\n ) : enableSystem ? (\n <script\n key=\"next-themes-script\"\n dangerouslySetInnerHTML={{\n // prettier-ignore\n __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('dark')}:${updateDOM('light')}}else if(e) ${value ? `var x=${JSON.stringify(value)};` : ''}${updateDOM(value ? 'x[e]' : 'e', true)}}catch(e){}}()`,\n }}\n />\n ) : (\n <script\n key=\"next-themes-script\"\n dangerouslySetInnerHTML={{\n // prettier-ignore\n __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(defaultTheme)};}}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": "
|
|
6
|
-
"names": ["theme"]
|
|
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 = 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\nexport const useRootTheme = () => {\n const isClient = typeof document !== 'undefined'\n // @ts-ignore\n const classes = isClient ? [...document.documentElement.classList] : []\n const isDark = classes.includes('t_dark')\n return useState(isDark ? 'dark' : 'light')\n}\n\nexport const NextThemeProvider: React.FC<ThemeProviderProps> = ({\n forcedTheme,\n disableTransitionOnChange = true,\n enableSystem = true,\n enableColorScheme = true,\n storageKey = 'theme',\n themes = ['light', 'dark'],\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) handleChangeTheme(systemTheme, false)\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 contextValue = useMemo(() => {\n const value: UseThemeProps = {\n theme,\n current: theme,\n set,\n toggle() {\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 forcedTheme,\n resolvedTheme: theme === 'system' ? resolvedTheme : theme,\n themes: enableSystem ? [...themes, 'system'] : themes,\n systemTheme: (enableSystem ? resolvedTheme : undefined) as 'light' | 'dark' | undefined,\n } as const\n\n return value\n }, [theme, set, forcedTheme, resolvedTheme, enableSystem, themes])\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 {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 // prettier-ignore\n __html: `!function(){${optimization}${updateDOM(forcedTheme)}}()`,\n }}\n />\n ) : enableSystem ? (\n <script\n key=\"next-themes-script\"\n dangerouslySetInnerHTML={{\n // prettier-ignore\n __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('dark')}:${updateDOM('light')}}else if(e) ${value ? `var x=${JSON.stringify(value)};` : ''}${updateDOM(value ? 'x[e]' : 'e', true)}}catch(e){}}()`,\n }}\n />\n ) : (\n <script\n key=\"next-themes-script\"\n dangerouslySetInnerHTML={{\n // prettier-ignore\n __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(defaultTheme)};}}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": "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",
|
|
6
|
+
"names": ["theme", "value"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tamagui/next-theme",
|
|
3
|
-
"version": "1.0.1-beta.
|
|
3
|
+
"version": "1.0.1-beta.168",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"source": "src/index.ts",
|
|
6
6
|
"types": "./types/index.d.ts",
|
|
@@ -15,11 +15,14 @@
|
|
|
15
15
|
"build": "tamagui-build",
|
|
16
16
|
"watch": "tamagui-build --watch"
|
|
17
17
|
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@tamagui/use-event": "^1.0.1-beta.168"
|
|
20
|
+
},
|
|
18
21
|
"peerDependencies": {
|
|
19
22
|
"react": "*"
|
|
20
23
|
},
|
|
21
24
|
"devDependencies": {
|
|
22
|
-
"@tamagui/build": "^1.0.1-beta.
|
|
25
|
+
"@tamagui/build": "^1.0.1-beta.168",
|
|
23
26
|
"react": "*"
|
|
24
27
|
},
|
|
25
28
|
"publishConfig": {
|
package/src/NextTheme.tsx
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// https://raw.githubusercontent.com/pacocoursey/next-themes/master/index.tsx
|
|
2
2
|
// forked temporarily due to buggy theme change
|
|
3
3
|
|
|
4
|
+
import { useEvent } from '@tamagui/use-event'
|
|
4
5
|
import NextHead from 'next/head'
|
|
5
6
|
import * as React from 'react'
|
|
6
7
|
import {
|
|
7
8
|
createContext,
|
|
8
9
|
memo,
|
|
9
|
-
useCallback,
|
|
10
10
|
useContext,
|
|
11
11
|
useEffect,
|
|
12
12
|
useLayoutEffect,
|
|
@@ -23,9 +23,11 @@ export interface UseThemeProps {
|
|
|
23
23
|
/** Forced theme name for the current page */
|
|
24
24
|
forcedTheme?: string
|
|
25
25
|
/** Update the theme */
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
/** Active theme name */
|
|
26
|
+
set: (theme: string) => void
|
|
27
|
+
toggle: () => void
|
|
28
|
+
/** Active theme name - will return "system" if not overriden, see "resolvedTheme" for getting resolved system value */
|
|
29
|
+
current?: string
|
|
30
|
+
/** @deprecated Use `current` instead (deprecating avoid confusion with useTheme) */
|
|
29
31
|
theme?: string
|
|
30
32
|
/** 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` */
|
|
31
33
|
resolvedTheme?: string
|
|
@@ -57,12 +59,18 @@ export interface ThemeProviderProps {
|
|
|
57
59
|
onChangeTheme?: (name: string) => void
|
|
58
60
|
}
|
|
59
61
|
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
const ThemeSettingContext = createContext<UseThemeProps>({
|
|
63
|
+
toggle: () => {},
|
|
64
|
+
set: (_) => {},
|
|
63
65
|
themes: [],
|
|
64
66
|
})
|
|
65
|
-
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @deprecated renamed to `useThemeSetting` to avoid confusion with core `useTheme` hook
|
|
70
|
+
*/
|
|
71
|
+
export const useTheme = () => useContext(ThemeSettingContext)
|
|
72
|
+
|
|
73
|
+
export const useThemeSetting = () => useContext(ThemeSettingContext)
|
|
66
74
|
|
|
67
75
|
const colorSchemes = ['light', 'dark']
|
|
68
76
|
const MEDIA = '(prefers-color-scheme: dark)'
|
|
@@ -100,22 +108,19 @@ export const NextThemeProvider: React.FC<ThemeProviderProps> = ({
|
|
|
100
108
|
// const resolvedTheme = React.useDeferredValue(resolvedThemeFast)
|
|
101
109
|
const attrs = !value ? themes : Object.values(value)
|
|
102
110
|
|
|
103
|
-
const handleMediaQuery =
|
|
104
|
-
(e
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
},
|
|
111
|
-
[theme, forcedTheme]
|
|
112
|
-
)
|
|
111
|
+
const handleMediaQuery = useEvent((e?) => {
|
|
112
|
+
const systemTheme = getSystemTheme(e)
|
|
113
|
+
React.startTransition(() => {
|
|
114
|
+
setResolvedTheme(systemTheme)
|
|
115
|
+
})
|
|
116
|
+
if (theme === 'system' && !forcedTheme) handleChangeTheme(systemTheme, false)
|
|
117
|
+
})
|
|
113
118
|
|
|
114
119
|
// Ref hack to avoid adding handleMediaQuery as a dep
|
|
115
120
|
const mediaListener = useRef(handleMediaQuery)
|
|
116
121
|
mediaListener.current = handleMediaQuery
|
|
117
122
|
|
|
118
|
-
const handleChangeTheme =
|
|
123
|
+
const handleChangeTheme = useEvent((theme, updateStorage = true, updateDOM = true) => {
|
|
119
124
|
let name = value?.[theme] || theme
|
|
120
125
|
|
|
121
126
|
if (updateStorage) {
|
|
@@ -142,7 +147,7 @@ export const NextThemeProvider: React.FC<ThemeProviderProps> = ({
|
|
|
142
147
|
d.setAttribute(attribute, name)
|
|
143
148
|
}
|
|
144
149
|
}
|
|
145
|
-
}
|
|
150
|
+
})
|
|
146
151
|
|
|
147
152
|
useIsomorphicLayoutEffect(() => {
|
|
148
153
|
const handler = (...args: any) => mediaListener.current(...args)
|
|
@@ -156,17 +161,14 @@ export const NextThemeProvider: React.FC<ThemeProviderProps> = ({
|
|
|
156
161
|
}
|
|
157
162
|
}, [])
|
|
158
163
|
|
|
159
|
-
const
|
|
160
|
-
(
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
},
|
|
168
|
-
[forcedTheme]
|
|
169
|
-
)
|
|
164
|
+
const set = useEvent((newTheme) => {
|
|
165
|
+
if (forcedTheme) {
|
|
166
|
+
handleChangeTheme(newTheme, true, false)
|
|
167
|
+
} else {
|
|
168
|
+
handleChangeTheme(newTheme)
|
|
169
|
+
}
|
|
170
|
+
setThemeState(newTheme)
|
|
171
|
+
})
|
|
170
172
|
|
|
171
173
|
// localStorage event handling
|
|
172
174
|
useEffect(() => {
|
|
@@ -176,13 +178,13 @@ export const NextThemeProvider: React.FC<ThemeProviderProps> = ({
|
|
|
176
178
|
}
|
|
177
179
|
// If default theme set, use it if localstorage === null (happens on local storage manual deletion)
|
|
178
180
|
const theme = e.newValue || defaultTheme
|
|
179
|
-
|
|
181
|
+
set(theme)
|
|
180
182
|
}
|
|
181
183
|
window.addEventListener('storage', handleStorage)
|
|
182
184
|
return () => {
|
|
183
185
|
window.removeEventListener('storage', handleStorage)
|
|
184
186
|
}
|
|
185
|
-
}, [])
|
|
187
|
+
}, [defaultTheme, set, storageKey])
|
|
186
188
|
|
|
187
189
|
// color-scheme handling
|
|
188
190
|
useIsomorphicLayoutEffect(() => {
|
|
@@ -218,24 +220,27 @@ export const NextThemeProvider: React.FC<ThemeProviderProps> = ({
|
|
|
218
220
|
}, [enableColorScheme, theme, resolvedTheme, forcedTheme])
|
|
219
221
|
|
|
220
222
|
const contextValue = useMemo(() => {
|
|
221
|
-
|
|
223
|
+
const value: UseThemeProps = {
|
|
222
224
|
theme,
|
|
223
|
-
|
|
224
|
-
|
|
225
|
+
current: theme,
|
|
226
|
+
set,
|
|
227
|
+
toggle() {
|
|
225
228
|
const order =
|
|
226
229
|
resolvedTheme === 'dark' ? ['system', 'light', 'dark'] : ['system', 'dark', 'light']
|
|
227
230
|
const next = order[(order.indexOf(theme) + 1) % order.length]
|
|
228
|
-
|
|
231
|
+
set(next)
|
|
229
232
|
},
|
|
230
233
|
forcedTheme,
|
|
231
234
|
resolvedTheme: theme === 'system' ? resolvedTheme : theme,
|
|
232
235
|
themes: enableSystem ? [...themes, 'system'] : themes,
|
|
233
236
|
systemTheme: (enableSystem ? resolvedTheme : undefined) as 'light' | 'dark' | undefined,
|
|
234
237
|
} as const
|
|
235
|
-
|
|
238
|
+
|
|
239
|
+
return value
|
|
240
|
+
}, [theme, set, forcedTheme, resolvedTheme, enableSystem, themes])
|
|
236
241
|
|
|
237
242
|
return (
|
|
238
|
-
<
|
|
243
|
+
<ThemeSettingContext.Provider value={contextValue}>
|
|
239
244
|
<ThemeScript
|
|
240
245
|
{...{
|
|
241
246
|
forcedTheme,
|
|
@@ -249,7 +254,7 @@ export const NextThemeProvider: React.FC<ThemeProviderProps> = ({
|
|
|
249
254
|
}}
|
|
250
255
|
/>
|
|
251
256
|
{children}
|
|
252
|
-
</
|
|
257
|
+
</ThemeSettingContext.Provider>
|
|
253
258
|
)
|
|
254
259
|
}
|
|
255
260
|
|
package/types/NextTheme.d.ts
CHANGED
|
@@ -1,28 +1,49 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
export interface UseThemeProps {
|
|
3
|
+
/** List of all available theme names */
|
|
3
4
|
themes: string[];
|
|
5
|
+
/** Forced theme name for the current page */
|
|
4
6
|
forcedTheme?: string;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
+
/** Update the theme */
|
|
8
|
+
set: (theme: string) => void;
|
|
9
|
+
toggle: () => void;
|
|
10
|
+
/** Active theme name - will return "system" if not overriden, see "resolvedTheme" for getting resolved system value */
|
|
11
|
+
current?: string;
|
|
12
|
+
/** @deprecated Use `current` instead (deprecating avoid confusion with useTheme) */
|
|
7
13
|
theme?: string;
|
|
14
|
+
/** 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` */
|
|
8
15
|
resolvedTheme?: string;
|
|
16
|
+
/** If enableSystem is true, returns the System theme preference ("dark" or "light"), regardless what the active theme is */
|
|
9
17
|
systemTheme?: 'dark' | 'light';
|
|
10
18
|
}
|
|
11
19
|
export interface ThemeProviderProps {
|
|
12
20
|
children?: any;
|
|
21
|
+
/** List of all available theme names */
|
|
13
22
|
themes?: string[];
|
|
23
|
+
/** Forced theme name for the current page */
|
|
14
24
|
forcedTheme?: string;
|
|
25
|
+
/** Whether to switch between dark and light themes based on prefers-color-scheme */
|
|
15
26
|
enableSystem?: boolean;
|
|
16
27
|
systemTheme?: string;
|
|
28
|
+
/** Disable all CSS transitions when switching themes */
|
|
17
29
|
disableTransitionOnChange?: boolean;
|
|
30
|
+
/** Whether to indicate to browsers which color scheme is used (dark or light) for built-in UI like inputs and buttons */
|
|
18
31
|
enableColorScheme?: boolean;
|
|
32
|
+
/** Key used to store theme setting in localStorage */
|
|
19
33
|
storageKey?: string;
|
|
34
|
+
/** Default theme name (for v0.0.12 and lower the default was light). If `enableSystem` is false, the default theme is light */
|
|
20
35
|
defaultTheme?: string;
|
|
36
|
+
/** HTML attribute modified based on the active theme. Accepts `class` and `data-*` (meaning any data attribute, `data-mode`, `data-color`, etc.) */
|
|
21
37
|
attribute?: string | 'class';
|
|
38
|
+
/** Mapping of theme name to HTML attribute value. Object where key is the theme name and value is the attribute value */
|
|
22
39
|
value?: ValueObject;
|
|
23
40
|
onChangeTheme?: (name: string) => void;
|
|
24
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* @deprecated renamed to `useThemeSetting` to avoid confusion with core `useTheme` hook
|
|
44
|
+
*/
|
|
25
45
|
export declare const useTheme: () => UseThemeProps;
|
|
46
|
+
export declare const useThemeSetting: () => UseThemeProps;
|
|
26
47
|
interface ValueObject {
|
|
27
48
|
[themeName: string]: string;
|
|
28
49
|
}
|