@ngrok/mantle 0.2.1 → 0.3.0

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.
@@ -1,6 +1,14 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { PropsWithChildren } from 'react';
3
3
 
4
+ /**
5
+ * resolvedThemes is a tuple of valid themes that have been resolved from "system" to a specific theme.
6
+ */
7
+ declare const resolvedThemes: readonly ["light", "dark", "light-high-contrast", "dark-high-contrast"];
8
+ /**
9
+ * ResolvedTheme is a type that represents a theme that has been resolved from "system" to a specific theme.
10
+ */
11
+ type ResolvedTheme = (typeof resolvedThemes)[number];
4
12
  /**
5
13
  * themes is a tuple of valid themes.
6
14
  */
@@ -10,13 +18,21 @@ declare const themes: readonly ["system", "light", "dark", "light-high-contrast"
10
18
  */
11
19
  type Theme = (typeof themes)[number];
12
20
  /**
13
- * theme is a helper which translates the Theme type into a string literal type.
21
+ * $theme is a helper which translates the Theme type into a string literal type.
14
22
  */
15
- declare const theme: <T extends Theme>(value: T) => T;
23
+ declare const $theme: <T extends Theme = "light" | "dark" | "light-high-contrast" | "dark-high-contrast" | "system">(value: T) => T;
16
24
  /**
17
25
  * Type predicate that checks if a value is a valid theme.
18
26
  */
19
27
  declare function isTheme(value: unknown): value is Theme;
28
+ /**
29
+ * $resolvedTheme is a helper which translates the ResolvedTheme type into a string literal type.
30
+ */
31
+ declare const $resolvedTheme: <T extends ResolvedTheme = "light" | "dark" | "light-high-contrast" | "dark-high-contrast">(value: T) => T;
32
+ /**
33
+ * Type predicate that checks if a value is a valid resolved theme.
34
+ */
35
+ declare function isResolvedTheme(value: unknown): value is ResolvedTheme;
20
36
  /**
21
37
  * ThemeProviderState is the shape of the state returned by the ThemeProviderContext.
22
38
  */
@@ -35,6 +51,17 @@ declare function ThemeProvider({ children, defaultTheme, storageKey }: ThemeProv
35
51
  * @note This function will throw an error if used outside of a ThemeProvider context tree.
36
52
  */
37
53
  declare function useTheme(): ThemeProviderState;
54
+ /**
55
+ * Applies the given theme to the `<html>` element.
56
+ */
57
+ declare function applyTheme(theme: Theme): void;
58
+ /**
59
+ * Read the theme and applied theme from the `<html>` element.
60
+ */
61
+ declare function readThemeFromHtmlElement(): {
62
+ appliedTheme: "light" | "dark" | "light-high-contrast" | "dark-high-contrast" | undefined;
63
+ theme: "light" | "dark" | "light-high-contrast" | "dark-high-contrast" | "system" | undefined;
64
+ };
38
65
  /**
39
66
  * If the theme is "system", it will resolve the theme based on the user's media query preferences, otherwise it will return the theme as is.
40
67
  * This will mirror the result that gets applied to the <html> element.
@@ -72,4 +99,4 @@ declare function useInitialHtmlThemeProps(props?: {
72
99
  */
73
100
  declare const PreloadFonts: () => react_jsx_runtime.JSX.Element;
74
101
 
75
- export { MantleThemeHeadContent, PreloadFonts, type Theme, ThemeProvider, type ThemeProviderProps, isTheme, preventWrongThemeFlashScriptContent, theme, useAppliedTheme, useInitialHtmlThemeProps, useTheme };
102
+ export { $resolvedTheme, $theme, MantleThemeHeadContent, PreloadFonts, type Theme, ThemeProvider, type ThemeProviderProps, applyTheme, isResolvedTheme, isTheme, preventWrongThemeFlashScriptContent, readThemeFromHtmlElement, resolvedThemes, themes, useAppliedTheme, useInitialHtmlThemeProps, useTheme };
@@ -1,6 +1,6 @@
1
- import{a as u}from"./chunk-D3XF6J5A.js";import M from"clsx";import{createContext as D,useContext as L,useEffect as v,useMemo as P,useState as F}from"react";import W from"tiny-invariant";import{Fragment as q,jsx as S}from"react/jsx-runtime";var C="https://cdn.ngrok.com/static/fonts",I=["euclid-square/EuclidSquare-Regular-WebS.woff","euclid-square/EuclidSquare-RegularItalic-WebS.woff","euclid-square/EuclidSquare-Medium-WebS.woff","euclid-square/EuclidSquare-Semibold-WebS.woff","euclid-square/EuclidSquare-MediumItalic-WebS.woff","ibm-plex-mono/IBMPlexMono-Text.woff","ibm-plex-mono/IBMPlexMono-TextItalic.woff","ibm-plex-mono/IBMPlexMono-SemiBold.woff","ibm-plex-mono/IBMPlexMono-SemiBoldItalic.woff"],H=e=>[C,e].join("/"),T=()=>S(q,{children:I.map(e=>S("link",{rel:"preload",href:H(e),as:"font",type:"font/woff",crossOrigin:"anonymous"},e))});import{Fragment as R,jsx as p,jsxs as G}from"react/jsx-runtime";var m="(prefers-color-scheme: dark)",a="(prefers-contrast: more)",g=["system","light","dark","light-high-contrast","dark-high-contrast"],B=e=>e;function b(e){return typeof e!="string"?!1:g.includes(e)}var d="mantle-ui-theme",_=["system",()=>null],k=D(_),w=()=>typeof window<"u";function c(e,t="system"){let r=t??"system";if(w()){let n=null;try{n="localStorage"in window?window.localStorage.getItem(e):null}catch{}return b(n)?n:r}return r}function N({children:e,defaultTheme:t="system",storageKey:r=d}){let[n,s]=F(()=>{let o=c(r,t);return l(o),o});v(()=>{let o=c(r,t);s(o),l(o)},[t,r]),v(()=>{let o=window.matchMedia(m),i=window.matchMedia(a),h=()=>{c(r,t)==="system"&&l("system")};return o.addEventListener("change",h),i.addEventListener("change",h),()=>{o.removeEventListener("change",h),i.removeEventListener("change",h)}},[t,r]);let f=P(()=>[n,o=>{try{"localStorage"in window&&window.localStorage.setItem(r,o)}catch{}s(o),l(o)}],[r,n]);return p(k.Provider,{value:f,children:e})}function x(){let e=L(k);return W(e,"useTheme must be used within a ThemeProvider"),e}function l(e){if(!w())return;let t=window.document.documentElement;t.classList.remove(...g);let r=window.matchMedia(m).matches,n=window.matchMedia(a).matches,s=y(e,{prefersDarkMode:r,prefersHighContrast:n});t.classList.add(s),t.dataset.appliedTheme=s,t.dataset.theme=e}function y(e,{prefersDarkMode:t,prefersHighContrast:r}){return e==="system"?A({prefersDarkMode:t,prefersHighContrast:r}):e}function $(){let[e]=x(),t=u(m),r=u(a);return y(e,{prefersDarkMode:t,prefersHighContrast:r})}function A({prefersDarkMode:e,prefersHighContrast:t}){return t?e?"dark-high-contrast":"light-high-contrast":e?"dark":"light"}function E({defaultTheme:e="system",storageKey:t=d}){return`
1
+ import{a as p}from"./chunk-D3XF6J5A.js";import P from"clsx";import{createContext as D,useContext as L,useEffect as b,useMemo as k,useState as $}from"react";import W from"tiny-invariant";import{Fragment as F,jsx as M}from"react/jsx-runtime";var I="https://cdn.ngrok.com/static/fonts",q=["euclid-square/EuclidSquare-Regular-WebS.woff","euclid-square/EuclidSquare-RegularItalic-WebS.woff","euclid-square/EuclidSquare-Medium-WebS.woff","euclid-square/EuclidSquare-Semibold-WebS.woff","euclid-square/EuclidSquare-MediumItalic-WebS.woff","ibm-plex-mono/IBMPlexMono-Text.woff","ibm-plex-mono/IBMPlexMono-TextItalic.woff","ibm-plex-mono/IBMPlexMono-SemiBold.woff","ibm-plex-mono/IBMPlexMono-SemiBoldItalic.woff"],R=e=>[I,e].join("/"),g=()=>M(F,{children:q.map(e=>M("link",{rel:"preload",href:R(e),as:"font",type:"font/woff",crossOrigin:"anonymous"},e))});import{Fragment as Y,jsx as w,jsxs as z}from"react/jsx-runtime";var a="(prefers-color-scheme: dark)",h="(prefers-contrast: more)",v=["light","dark","light-high-contrast","dark-high-contrast"],c=["system",...v],B=e=>e;function y(e){return typeof e!="string"?!1:c.includes(e)}var _=e=>e;function E(e){return typeof e!="string"?!1:v.includes(e)}var T="mantle-ui-theme",N=["system",()=>null],x=D(N),u=()=>typeof window<"u";function d(e,t="system"){let o=t??"system";if(u()){let r=null;try{r="localStorage"in window?window.localStorage.getItem(e):null}catch{}return y(r)?r:o}return o}function A({children:e,defaultTheme:t="system",storageKey:o=T}){let[r,s]=$(()=>{let n=d(o,t);return i(n),n});b(()=>{let n=d(o,t);s(n),i(n)},[t,o]),b(()=>{let n=window.matchMedia(a),m=window.matchMedia(h),l=()=>{d(o,t)==="system"&&i("system")};return n.addEventListener("change",l),m.addEventListener("change",l),()=>{n.removeEventListener("change",l),m.removeEventListener("change",l)}},[t,o]);let f=k(()=>[r,n=>{try{"localStorage"in window&&window.localStorage.setItem(o,n)}catch{}s(n),i(n)}],[o,r]);return w(x.Provider,{value:f,children:e})}function C(){let e=L(x);return W(e,"useTheme must be used within a ThemeProvider"),e}function i(e){if(!u())return;let t=window.document.documentElement;t.classList.remove(...c);let o=window.matchMedia(a).matches,r=window.matchMedia(h).matches,s=S(e,{prefersDarkMode:o,prefersHighContrast:r});t.classList.add(s),t.dataset.appliedTheme=s,t.dataset.theme=e}function O(){if(!u())return{appliedTheme:void 0,theme:void 0};let e=window.document.documentElement,t=y(e.dataset.theme)?e.dataset.theme:void 0;return{appliedTheme:E(e.dataset.appliedTheme)?e.dataset.appliedTheme:void 0,theme:t}}function S(e,{prefersDarkMode:t,prefersHighContrast:o}){return e==="system"?G({prefersDarkMode:t,prefersHighContrast:o}):e}function Q(){let[e]=C(),t=p(a),o=p(h);return S(e,{prefersDarkMode:t,prefersHighContrast:o})}function G({prefersDarkMode:e,prefersHighContrast:t}){return t?e?"dark-high-contrast":"light-high-contrast":e?"dark":"light"}function H({defaultTheme:e="system",storageKey:t=T}){return`
2
2
  (function() {
3
- const themes = ${JSON.stringify(g)};
3
+ const themes = ${JSON.stringify(c)};
4
4
  const isTheme = (value) => typeof value === "string" && themes.includes(value);
5
5
  const fallbackTheme = "${e}" ?? "system";
6
6
  let maybeStoredTheme = null;
@@ -14,8 +14,8 @@ import{a as u}from"./chunk-D3XF6J5A.js";import M from"clsx";import{createContext
14
14
  } catch (_) {}
15
15
  }
16
16
  const themePreference = hasStoredTheme ? maybeStoredTheme : fallbackTheme;
17
- const prefersDarkMode = window.matchMedia("${m}").matches;
18
- const prefersHighContrast = window.matchMedia("${a}").matches;
17
+ const prefersDarkMode = window.matchMedia("${a}").matches;
18
+ const prefersHighContrast = window.matchMedia("${h}").matches;
19
19
  let initialTheme = themePreference;
20
20
  if (initialTheme === "system") {
21
21
  if (prefersHighContrast) {
@@ -30,5 +30,5 @@ import{a as u}from"./chunk-D3XF6J5A.js";import M from"clsx";import{createContext
30
30
  htmlElement.dataset.appliedTheme = initialTheme;
31
31
  htmlElement.dataset.theme = themePreference;
32
32
  })();
33
- `.trim()}var O=({defaultTheme:e="system",storageKey:t=d})=>G(R,{children:[p("script",{dangerouslySetInnerHTML:{__html:E({defaultTheme:e,storageKey:t})}}),p(T,{})]});function Q(e){let{className:t="",defaultTheme:r="system",storageKey:n=d}=e??{};return P(()=>{if(!w())return{className:M(t),"data-applied-theme":"system","data-theme":"system"};let s=window.matchMedia(m).matches,f=window.matchMedia(a).matches,o=c(n,r),i=y(o,{prefersDarkMode:s,prefersHighContrast:f});return{className:M(t,i),"data-applied-theme":i,"data-theme":o}},[t,r,n])}export{O as MantleThemeHeadContent,T as PreloadFonts,N as ThemeProvider,b as isTheme,E as preventWrongThemeFlashScriptContent,B as theme,$ as useAppliedTheme,Q as useInitialHtmlThemeProps,x as useTheme};
33
+ `.trim()}var J=({defaultTheme:e="system",storageKey:t=T})=>z(Y,{children:[w("script",{dangerouslySetInnerHTML:{__html:H({defaultTheme:e,storageKey:t})}}),w(g,{})]});function U(e){let{className:t="",defaultTheme:o="system",storageKey:r=T}=e??{};return k(()=>{if(!u())return{className:P(t),"data-applied-theme":"system","data-theme":"system"};let s=window.matchMedia(a).matches,f=window.matchMedia(h).matches,n=d(r,o),m=S(n,{prefersDarkMode:s,prefersHighContrast:f});return{className:P(t,m),"data-applied-theme":m,"data-theme":n}},[t,o,r])}export{_ as $resolvedTheme,B as $theme,J as MantleThemeHeadContent,g as PreloadFonts,A as ThemeProvider,i as applyTheme,E as isResolvedTheme,y as isTheme,H as preventWrongThemeFlashScriptContent,O as readThemeFromHtmlElement,v as resolvedThemes,c as themes,Q as useAppliedTheme,U as useInitialHtmlThemeProps,C as useTheme};
34
34
  //# sourceMappingURL=theme-provider.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/theme-provider/theme-provider.tsx","../src/components/theme-provider/preload-fonts.tsx"],"sourcesContent":["import clsx from \"clsx\";\nimport type { PropsWithChildren } from \"react\";\nimport { createContext, useContext, useEffect, useMemo, useState } from \"react\";\nimport invariant from \"tiny-invariant\";\nimport { useMatchesMediaQuery } from \"../../hooks/use-matches-media-query\";\nimport { PreloadFonts } from \"./preload-fonts\";\n\n/**\n * prefersDarkModeMediaQuery is the media query used to detect if the user prefers dark mode.\n */\nconst prefersDarkModeMediaQuery = \"(prefers-color-scheme: dark)\";\n\n/**\n * prefersHighContrastMediaQuery is the media query used to detect if the user prefers high contrast mode.\n */\nconst prefersHighContrastMediaQuery = \"(prefers-contrast: more)\";\n\n/**\n * themes is a tuple of valid themes.\n */\nconst themes = [\"system\", \"light\", \"dark\", \"light-high-contrast\", \"dark-high-contrast\"] as const;\n\n/**\n * Theme is a string literal type that represents a valid theme.\n */\ntype Theme = (typeof themes)[number];\n\n/**\n * theme is a helper which translates the Theme type into a string literal type.\n */\nconst theme = <T extends Theme>(value: T) => value;\n\n/**\n * Type predicate that checks if a value is a valid theme.\n */\nfunction isTheme(value: unknown): value is Theme {\n\tif (typeof value !== \"string\") {\n\t\treturn false;\n\t}\n\n\treturn themes.includes(value as Theme);\n}\n\n/**\n * DEFAULT_STORAGE_KEY is the default key used to store the theme in localStorage.\n */\nconst DEFAULT_STORAGE_KEY = \"mantle-ui-theme\";\n\n/**\n * ThemeProviderState is the shape of the state returned by the ThemeProviderContext.\n */\ntype ThemeProviderState = [theme: Theme, setTheme: (theme: Theme) => void];\n\n/**\n * Initial state for the ThemeProviderContext.\n */\nconst initialState: ThemeProviderState = [\"system\", () => null];\n\n/**\n * ThemeProviderContext is a React Context that provides the current theme and a function to set the theme.\n */\nconst ThemeProviderContext = createContext<ThemeProviderState>(initialState);\n\n/**\n * isBrowser returns true if the code is running in a browser environment.\n */\nconst isBrowser = () => typeof window !== \"undefined\";\n\n/**\n * Gets the stored theme from localStorage or returns the default theme if no theme is stored.\n */\nfunction getStoredTheme(storageKey: string, defaultTheme: Theme = \"system\") {\n\tconst fallbackTheme = defaultTheme ?? \"system\";\n\tif (isBrowser()) {\n\t\tlet storedTheme: string | null = null;\n\t\ttry {\n\t\t\tstoredTheme = \"localStorage\" in window ? window.localStorage.getItem(storageKey) : null;\n\t\t} catch (_) {}\n\t\treturn isTheme(storedTheme) ? storedTheme : fallbackTheme;\n\t}\n\treturn fallbackTheme;\n}\n\ntype ThemeProviderProps = PropsWithChildren & {\n\tdefaultTheme?: Theme;\n\tstorageKey?: string;\n};\n\n/**\n * ThemeProvider is a React Context Provider that provides the current theme and a function to set the theme.\n */\nfunction ThemeProvider({ children, defaultTheme = \"system\", storageKey = DEFAULT_STORAGE_KEY }: ThemeProviderProps) {\n\tconst [theme, setTheme] = useState<Theme>(() => {\n\t\tconst initialTheme = getStoredTheme(storageKey, defaultTheme);\n\t\tapplyTheme(initialTheme);\n\t\treturn initialTheme;\n\t});\n\n\tuseEffect(() => {\n\t\tconst storedTheme = getStoredTheme(storageKey, defaultTheme);\n\t\tsetTheme(storedTheme);\n\t\tapplyTheme(storedTheme);\n\t}, [defaultTheme, storageKey]);\n\n\tuseEffect(() => {\n\t\tconst prefersDarkMql = window.matchMedia(prefersDarkModeMediaQuery);\n\t\tconst prefersHighContrastMql = window.matchMedia(prefersHighContrastMediaQuery);\n\n\t\tconst onChange = () => {\n\t\t\tconst storedTheme = getStoredTheme(storageKey, defaultTheme);\n\n\t\t\t// If the stored theme is not \"system\", then the user has explicitly set a theme and we should not\n\t\t\t// automatically change the theme when the user's system preferences change.\n\t\t\tif (storedTheme !== \"system\") {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tapplyTheme(\"system\");\n\t\t};\n\n\t\tprefersDarkMql.addEventListener(\"change\", onChange);\n\t\tprefersHighContrastMql.addEventListener(\"change\", onChange);\n\n\t\treturn () => {\n\t\t\tprefersDarkMql.removeEventListener(\"change\", onChange);\n\t\t\tprefersHighContrastMql.removeEventListener(\"change\", onChange);\n\t\t};\n\t}, [defaultTheme, storageKey]);\n\n\tconst value: ThemeProviderState = useMemo(\n\t\t() => [\n\t\t\ttheme,\n\t\t\t(theme: Theme) => {\n\t\t\t\ttry {\n\t\t\t\t\tif (\"localStorage\" in window) {\n\t\t\t\t\t\twindow.localStorage.setItem(storageKey, theme);\n\t\t\t\t\t}\n\t\t\t\t} catch (_) {}\n\t\t\t\tsetTheme(theme);\n\t\t\t\tapplyTheme(theme);\n\t\t\t},\n\t\t],\n\t\t[storageKey, theme],\n\t);\n\n\treturn <ThemeProviderContext.Provider value={value}>{children}</ThemeProviderContext.Provider>;\n}\n\n/**\n * useTheme returns the current theme and a function to set the theme.\n *\n * @note This function will throw an error if used outside of a ThemeProvider context tree.\n */\nfunction useTheme() {\n\tconst context = useContext(ThemeProviderContext);\n\n\tinvariant(context, \"useTheme must be used within a ThemeProvider\");\n\n\treturn context;\n}\n\n/**\n * Applies the given theme to the <html> element.\n */\nfunction applyTheme(theme: Theme) {\n\tif (!isBrowser()) {\n\t\treturn;\n\t}\n\n\tconst htmlElement = window.document.documentElement;\n\thtmlElement.classList.remove(...themes);\n\tconst prefersDarkMode = window.matchMedia(prefersDarkModeMediaQuery).matches;\n\tconst prefersHighContrast = window.matchMedia(prefersHighContrastMediaQuery).matches;\n\tconst newTheme = resolveTheme(theme, { prefersDarkMode, prefersHighContrast });\n\thtmlElement.classList.add(newTheme);\n\thtmlElement.dataset.appliedTheme = newTheme;\n\thtmlElement.dataset.theme = theme;\n}\n\n/**\n * If the theme is \"system\", it will resolve the theme based on the user's media query preferences, otherwise it will return the theme as is.\n * This will mirror the result that gets applied to the <html> element.\n */\nfunction resolveTheme(\n\ttheme: Theme,\n\t{ prefersDarkMode, prefersHighContrast }: { prefersDarkMode: boolean; prefersHighContrast: boolean },\n) {\n\tif (theme === \"system\") {\n\t\treturn determineThemeFromMediaQuery({ prefersDarkMode, prefersHighContrast });\n\t}\n\n\treturn theme;\n}\n\n/**\n * If the theme is \"system\", it will resolve the theme based on the user's media query preferences, otherwise it will return the theme as is.\n * This will mirror the result that gets applied to the <html> element.\n */\nfunction useAppliedTheme() {\n\tconst [theme] = useTheme();\n\n\tconst prefersDarkMode = useMatchesMediaQuery(prefersDarkModeMediaQuery);\n\tconst prefersHighContrast = useMatchesMediaQuery(prefersHighContrastMediaQuery);\n\n\treturn resolveTheme(theme, { prefersDarkMode, prefersHighContrast });\n}\n\n/**\n * determineThemeFromMediaQuery returns the theme that should be used based on the user's media query preferences.\n * @private\n */\nexport function determineThemeFromMediaQuery({\n\tprefersDarkMode,\n\tprefersHighContrast,\n}: {\n\tprefersDarkMode: boolean;\n\tprefersHighContrast: boolean;\n}) {\n\tif (prefersHighContrast) {\n\t\treturn prefersDarkMode ? \"dark-high-contrast\" : \"light-high-contrast\";\n\t}\n\n\treturn prefersDarkMode ? \"dark\" : \"light\";\n}\n\nfunction preventWrongThemeFlashScriptContent({\n\tdefaultTheme = \"system\",\n\tstorageKey = DEFAULT_STORAGE_KEY,\n}: {\n\tdefaultTheme?: Theme;\n\tstorageKey?: string;\n}) {\n\treturn `\n(function() {\n\tconst themes = ${JSON.stringify(themes)};\n\tconst isTheme = (value) => typeof value === \"string\" && themes.includes(value);\n\tconst fallbackTheme = \"${defaultTheme}\" ?? \"system\";\n\tlet maybeStoredTheme = null;\n\ttry {\n\t\tmaybeStoredTheme = \"localStorage\" in window ? window.localStorage.getItem(\"${storageKey}\") : null;\n\t} catch (_) {}\n\tconst hasStoredTheme = isTheme(maybeStoredTheme);\n\tif (!hasStoredTheme && \"localStorage\" in window) {\n\t\ttry {\n\t\t\twindow.localStorage.setItem(\"${storageKey}\", fallbackTheme);\n\t\t} catch (_) {}\n\t}\n\tconst themePreference = hasStoredTheme ? maybeStoredTheme : fallbackTheme;\n\tconst prefersDarkMode = window.matchMedia(\"${prefersDarkModeMediaQuery}\").matches;\n\tconst prefersHighContrast = window.matchMedia(\"${prefersHighContrastMediaQuery}\").matches;\n\tlet initialTheme = themePreference;\n\tif (initialTheme === \"system\") {\n\t\tif (prefersHighContrast) {\n\t\t\tinitialTheme = prefersDarkMode ? \"dark-high-contrast\" : \"light-high-contrast\";\n\t\t} else {\n\t\t\tinitialTheme = prefersDarkMode ? \"dark\" : \"light\";\n\t\t}\n\t}\n\tconst htmlElement = document.documentElement;\n\thtmlElement.classList.remove(...themes);\n\thtmlElement.classList.add(initialTheme);\n\thtmlElement.dataset.appliedTheme = initialTheme;\n\thtmlElement.dataset.theme = themePreference;\n})();\n`.trim();\n}\n\n/**\n * MantleThemeHeadContent is a React component that prevents the wrong theme from flashing on initial page load.\n * Render as high as possible in the <head> element.\n */\nconst MantleThemeHeadContent = ({\n\tdefaultTheme = \"system\",\n\tstorageKey = DEFAULT_STORAGE_KEY,\n}: {\n\tdefaultTheme?: Theme;\n\tstorageKey?: string;\n}) => (\n\t<>\n\t\t<script\n\t\t\tdangerouslySetInnerHTML={{\n\t\t\t\t__html: preventWrongThemeFlashScriptContent({ defaultTheme, storageKey }),\n\t\t\t}}\n\t\t/>\n\t\t<PreloadFonts />\n\t</>\n);\n\ntype InitialThemeProps = {\n\tclassName: string;\n\t\"data-applied-theme\": Omit<Theme, \"system\">;\n\t\"data-theme\": Theme;\n};\n\n/**\n * useInitialHtmlThemeProps returns the initial props that should be applied to the <html> element to prevent react hydration errors.\n */\nfunction useInitialHtmlThemeProps(props?: {\n\tclassName?: string;\n\tdefaultTheme?: Theme;\n\tstorageKey?: string;\n}): InitialThemeProps {\n\tconst { className = \"\", defaultTheme = \"system\", storageKey = DEFAULT_STORAGE_KEY } = props ?? {};\n\n\treturn useMemo(() => {\n\t\tif (!isBrowser()) {\n\t\t\treturn {\n\t\t\t\tclassName: clsx(className),\n\t\t\t\t\"data-applied-theme\": \"system\",\n\t\t\t\t\"data-theme\": \"system\",\n\t\t\t};\n\t\t}\n\n\t\tconst prefersDarkMode = window.matchMedia(prefersDarkModeMediaQuery).matches;\n\t\tconst prefersHighContrast = window.matchMedia(prefersHighContrastMediaQuery).matches;\n\t\tconst initialTheme = getStoredTheme(storageKey, defaultTheme);\n\t\tconst reolvedTheme = resolveTheme(initialTheme, { prefersDarkMode, prefersHighContrast });\n\n\t\treturn {\n\t\t\tclassName: clsx(className, reolvedTheme),\n\t\t\t\"data-applied-theme\": reolvedTheme,\n\t\t\t\"data-theme\": initialTheme,\n\t\t};\n\t}, [className, defaultTheme, storageKey]);\n}\n\nexport type { Theme, ThemeProviderProps };\nexport {\n\tisTheme,\n\tMantleThemeHeadContent,\n\tpreventWrongThemeFlashScriptContent,\n\ttheme,\n\tThemeProvider,\n\tuseAppliedTheme,\n\tuseInitialHtmlThemeProps,\n\tuseTheme,\n};\n","const cdnBase = \"https://cdn.ngrok.com/static/fonts\";\n\nconst fonts = [\n\t\"euclid-square/EuclidSquare-Regular-WebS.woff\",\n\t\"euclid-square/EuclidSquare-RegularItalic-WebS.woff\",\n\t\"euclid-square/EuclidSquare-Medium-WebS.woff\",\n\t\"euclid-square/EuclidSquare-Semibold-WebS.woff\",\n\t\"euclid-square/EuclidSquare-MediumItalic-WebS.woff\",\n\t\"ibm-plex-mono/IBMPlexMono-Text.woff\",\n\t\"ibm-plex-mono/IBMPlexMono-TextItalic.woff\",\n\t\"ibm-plex-mono/IBMPlexMono-SemiBold.woff\",\n\t\"ibm-plex-mono/IBMPlexMono-SemiBoldItalic.woff\",\n] as const;\n\ntype Font = (typeof fonts)[number];\n\nconst fontHref = <T extends Font>(font: T) => [cdnBase, font].join(\"/\");\n\n/**\n * Preload custom fonts used in the theme. This should be added to the head of the document in your application, preferably as high as possible.\n * Normally you won't use this directly, but instead use the `MantleThemeHeadContent` component which includes this.\n */\nconst PreloadFonts = () => (\n\t<>\n\t\t{fonts.map((font) => (\n\t\t\t<link key={font} rel=\"preload\" href={fontHref(font)} as=\"font\" type=\"font/woff\" crossOrigin=\"anonymous\" />\n\t\t))}\n\t</>\n);\n\nexport { PreloadFonts };\n"],"mappings":"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","names":["clsx","createContext","useContext","useEffect","useMemo","useState","invariant","Fragment","jsx","cdnBase","fonts","fontHref","font","PreloadFonts","Fragment","jsx","jsxs","prefersDarkModeMediaQuery","prefersHighContrastMediaQuery","themes","theme","value","isTheme","DEFAULT_STORAGE_KEY","initialState","ThemeProviderContext","createContext","isBrowser","getStoredTheme","storageKey","defaultTheme","fallbackTheme","storedTheme","ThemeProvider","children","setTheme","useState","initialTheme","applyTheme","useEffect","prefersDarkMql","prefersHighContrastMql","onChange","useMemo","useTheme","context","useContext","invariant","htmlElement","prefersDarkMode","prefersHighContrast","newTheme","resolveTheme","determineThemeFromMediaQuery","useAppliedTheme","useMatchesMediaQuery","preventWrongThemeFlashScriptContent","MantleThemeHeadContent","PreloadFonts","useInitialHtmlThemeProps","props","className","clsx","reolvedTheme"]}
1
+ {"version":3,"sources":["../src/components/theme-provider/theme-provider.tsx","../src/components/theme-provider/preload-fonts.tsx"],"sourcesContent":["import clsx from \"clsx\";\nimport type { PropsWithChildren } from \"react\";\nimport { createContext, useContext, useEffect, useMemo, useState } from \"react\";\nimport invariant from \"tiny-invariant\";\nimport { useMatchesMediaQuery } from \"../../hooks/use-matches-media-query\";\nimport { PreloadFonts } from \"./preload-fonts\";\n\n/**\n * prefersDarkModeMediaQuery is the media query used to detect if the user prefers dark mode.\n */\nconst prefersDarkModeMediaQuery = \"(prefers-color-scheme: dark)\";\n\n/**\n * prefersHighContrastMediaQuery is the media query used to detect if the user prefers high contrast mode.\n */\nconst prefersHighContrastMediaQuery = \"(prefers-contrast: more)\";\n\n/**\n * resolvedThemes is a tuple of valid themes that have been resolved from \"system\" to a specific theme.\n */\nconst resolvedThemes = [\"light\", \"dark\", \"light-high-contrast\", \"dark-high-contrast\"] as const;\n\n/**\n * ResolvedTheme is a type that represents a theme that has been resolved from \"system\" to a specific theme.\n */\ntype ResolvedTheme = (typeof resolvedThemes)[number];\n\n/**\n * themes is a tuple of valid themes.\n */\nconst themes = [\"system\", ...resolvedThemes] as const;\n\n/**\n * Theme is a string literal type that represents a valid theme.\n */\ntype Theme = (typeof themes)[number];\n\n/**\n * $theme is a helper which translates the Theme type into a string literal type.\n */\nconst $theme = <T extends Theme = Theme>(value: T) => value;\n\n/**\n * Type predicate that checks if a value is a valid theme.\n */\nfunction isTheme(value: unknown): value is Theme {\n\tif (typeof value !== \"string\") {\n\t\treturn false;\n\t}\n\n\treturn themes.includes(value as Theme);\n}\n\n/**\n * $resolvedTheme is a helper which translates the ResolvedTheme type into a string literal type.\n */\nconst $resolvedTheme = <T extends ResolvedTheme = ResolvedTheme>(value: T) => value;\n\n/**\n * Type predicate that checks if a value is a valid resolved theme.\n */\nfunction isResolvedTheme(value: unknown): value is ResolvedTheme {\n\tif (typeof value !== \"string\") {\n\t\treturn false;\n\t}\n\n\treturn resolvedThemes.includes(value as ResolvedTheme);\n}\n\n/**\n * DEFAULT_STORAGE_KEY is the default key used to store the theme in localStorage.\n */\nconst DEFAULT_STORAGE_KEY = \"mantle-ui-theme\";\n\n/**\n * ThemeProviderState is the shape of the state returned by the ThemeProviderContext.\n */\ntype ThemeProviderState = [theme: Theme, setTheme: (theme: Theme) => void];\n\n/**\n * Initial state for the ThemeProviderContext.\n */\nconst initialState: ThemeProviderState = [\"system\", () => null];\n\n/**\n * ThemeProviderContext is a React Context that provides the current theme and a function to set the theme.\n */\nconst ThemeProviderContext = createContext<ThemeProviderState>(initialState);\n\n/**\n * isBrowser returns true if the code is running in a browser environment.\n */\nconst isBrowser = () => typeof window !== \"undefined\";\n\n/**\n * Gets the stored theme from localStorage or returns the default theme if no theme is stored.\n */\nfunction getStoredTheme(storageKey: string, defaultTheme: Theme = \"system\") {\n\tconst fallbackTheme = defaultTheme ?? \"system\";\n\tif (isBrowser()) {\n\t\tlet storedTheme: string | null = null;\n\t\ttry {\n\t\t\tstoredTheme = \"localStorage\" in window ? window.localStorage.getItem(storageKey) : null;\n\t\t} catch (_) {}\n\t\treturn isTheme(storedTheme) ? storedTheme : fallbackTheme;\n\t}\n\treturn fallbackTheme;\n}\n\ntype ThemeProviderProps = PropsWithChildren & {\n\tdefaultTheme?: Theme;\n\tstorageKey?: string;\n};\n\n/**\n * ThemeProvider is a React Context Provider that provides the current theme and a function to set the theme.\n */\nfunction ThemeProvider({ children, defaultTheme = \"system\", storageKey = DEFAULT_STORAGE_KEY }: ThemeProviderProps) {\n\tconst [theme, setTheme] = useState<Theme>(() => {\n\t\tconst initialTheme = getStoredTheme(storageKey, defaultTheme);\n\t\tapplyTheme(initialTheme);\n\t\treturn initialTheme;\n\t});\n\n\tuseEffect(() => {\n\t\tconst storedTheme = getStoredTheme(storageKey, defaultTheme);\n\t\tsetTheme(storedTheme);\n\t\tapplyTheme(storedTheme);\n\t}, [defaultTheme, storageKey]);\n\n\tuseEffect(() => {\n\t\tconst prefersDarkMql = window.matchMedia(prefersDarkModeMediaQuery);\n\t\tconst prefersHighContrastMql = window.matchMedia(prefersHighContrastMediaQuery);\n\n\t\tconst onChange = () => {\n\t\t\tconst storedTheme = getStoredTheme(storageKey, defaultTheme);\n\n\t\t\t// If the stored theme is not \"system\", then the user has explicitly set a theme and we should not\n\t\t\t// automatically change the theme when the user's system preferences change.\n\t\t\tif (storedTheme !== \"system\") {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tapplyTheme(\"system\");\n\t\t};\n\n\t\tprefersDarkMql.addEventListener(\"change\", onChange);\n\t\tprefersHighContrastMql.addEventListener(\"change\", onChange);\n\n\t\treturn () => {\n\t\t\tprefersDarkMql.removeEventListener(\"change\", onChange);\n\t\t\tprefersHighContrastMql.removeEventListener(\"change\", onChange);\n\t\t};\n\t}, [defaultTheme, storageKey]);\n\n\tconst value: ThemeProviderState = useMemo(\n\t\t() => [\n\t\t\ttheme,\n\t\t\t(theme: Theme) => {\n\t\t\t\ttry {\n\t\t\t\t\tif (\"localStorage\" in window) {\n\t\t\t\t\t\twindow.localStorage.setItem(storageKey, theme);\n\t\t\t\t\t}\n\t\t\t\t} catch (_) {}\n\t\t\t\tsetTheme(theme);\n\t\t\t\tapplyTheme(theme);\n\t\t\t},\n\t\t],\n\t\t[storageKey, theme],\n\t);\n\n\treturn <ThemeProviderContext.Provider value={value}>{children}</ThemeProviderContext.Provider>;\n}\n\n/**\n * useTheme returns the current theme and a function to set the theme.\n *\n * @note This function will throw an error if used outside of a ThemeProvider context tree.\n */\nfunction useTheme() {\n\tconst context = useContext(ThemeProviderContext);\n\n\tinvariant(context, \"useTheme must be used within a ThemeProvider\");\n\n\treturn context;\n}\n\n/**\n * Applies the given theme to the `<html>` element.\n */\nfunction applyTheme(theme: Theme) {\n\tif (!isBrowser()) {\n\t\treturn;\n\t}\n\n\tconst htmlElement = window.document.documentElement;\n\thtmlElement.classList.remove(...themes);\n\tconst prefersDarkMode = window.matchMedia(prefersDarkModeMediaQuery).matches;\n\tconst prefersHighContrast = window.matchMedia(prefersHighContrastMediaQuery).matches;\n\tconst newTheme = resolveTheme(theme, { prefersDarkMode, prefersHighContrast });\n\thtmlElement.classList.add(newTheme);\n\thtmlElement.dataset.appliedTheme = newTheme;\n\thtmlElement.dataset.theme = theme;\n}\n\n/**\n * Read the theme and applied theme from the `<html>` element.\n */\nfunction readThemeFromHtmlElement() {\n\tif (!isBrowser()) {\n\t\treturn {\n\t\t\tappliedTheme: undefined,\n\t\t\ttheme: undefined,\n\t\t};\n\t}\n\n\tconst htmlElement = window.document.documentElement;\n\tconst theme = isTheme(htmlElement.dataset.theme) ? htmlElement.dataset.theme : undefined;\n\tconst appliedTheme = isResolvedTheme(htmlElement.dataset.appliedTheme) ? htmlElement.dataset.appliedTheme : undefined;\n\n\treturn {\n\t\tappliedTheme,\n\t\ttheme,\n\t};\n}\n\n/**\n * If the theme is \"system\", it will resolve the theme based on the user's media query preferences, otherwise it will return the theme as is.\n * This will mirror the result that gets applied to the <html> element.\n */\nfunction resolveTheme(\n\ttheme: Theme,\n\t{ prefersDarkMode, prefersHighContrast }: { prefersDarkMode: boolean; prefersHighContrast: boolean },\n) {\n\tif (theme === \"system\") {\n\t\treturn determineThemeFromMediaQuery({ prefersDarkMode, prefersHighContrast });\n\t}\n\n\treturn theme;\n}\n\n/**\n * If the theme is \"system\", it will resolve the theme based on the user's media query preferences, otherwise it will return the theme as is.\n * This will mirror the result that gets applied to the <html> element.\n */\nfunction useAppliedTheme() {\n\tconst [theme] = useTheme();\n\n\tconst prefersDarkMode = useMatchesMediaQuery(prefersDarkModeMediaQuery);\n\tconst prefersHighContrast = useMatchesMediaQuery(prefersHighContrastMediaQuery);\n\n\treturn resolveTheme(theme, { prefersDarkMode, prefersHighContrast });\n}\n\n/**\n * determineThemeFromMediaQuery returns the theme that should be used based on the user's media query preferences.\n * @private\n */\nexport function determineThemeFromMediaQuery({\n\tprefersDarkMode,\n\tprefersHighContrast,\n}: {\n\tprefersDarkMode: boolean;\n\tprefersHighContrast: boolean;\n}): ResolvedTheme {\n\tif (prefersHighContrast) {\n\t\treturn prefersDarkMode ? \"dark-high-contrast\" : \"light-high-contrast\";\n\t}\n\n\treturn prefersDarkMode ? \"dark\" : \"light\";\n}\n\nfunction preventWrongThemeFlashScriptContent({\n\tdefaultTheme = \"system\",\n\tstorageKey = DEFAULT_STORAGE_KEY,\n}: {\n\tdefaultTheme?: Theme;\n\tstorageKey?: string;\n}) {\n\treturn `\n(function() {\n\tconst themes = ${JSON.stringify(themes)};\n\tconst isTheme = (value) => typeof value === \"string\" && themes.includes(value);\n\tconst fallbackTheme = \"${defaultTheme}\" ?? \"system\";\n\tlet maybeStoredTheme = null;\n\ttry {\n\t\tmaybeStoredTheme = \"localStorage\" in window ? window.localStorage.getItem(\"${storageKey}\") : null;\n\t} catch (_) {}\n\tconst hasStoredTheme = isTheme(maybeStoredTheme);\n\tif (!hasStoredTheme && \"localStorage\" in window) {\n\t\ttry {\n\t\t\twindow.localStorage.setItem(\"${storageKey}\", fallbackTheme);\n\t\t} catch (_) {}\n\t}\n\tconst themePreference = hasStoredTheme ? maybeStoredTheme : fallbackTheme;\n\tconst prefersDarkMode = window.matchMedia(\"${prefersDarkModeMediaQuery}\").matches;\n\tconst prefersHighContrast = window.matchMedia(\"${prefersHighContrastMediaQuery}\").matches;\n\tlet initialTheme = themePreference;\n\tif (initialTheme === \"system\") {\n\t\tif (prefersHighContrast) {\n\t\t\tinitialTheme = prefersDarkMode ? \"dark-high-contrast\" : \"light-high-contrast\";\n\t\t} else {\n\t\t\tinitialTheme = prefersDarkMode ? \"dark\" : \"light\";\n\t\t}\n\t}\n\tconst htmlElement = document.documentElement;\n\thtmlElement.classList.remove(...themes);\n\thtmlElement.classList.add(initialTheme);\n\thtmlElement.dataset.appliedTheme = initialTheme;\n\thtmlElement.dataset.theme = themePreference;\n})();\n`.trim();\n}\n\n/**\n * MantleThemeHeadContent is a React component that prevents the wrong theme from flashing on initial page load.\n * Render as high as possible in the <head> element.\n */\nconst MantleThemeHeadContent = ({\n\tdefaultTheme = \"system\",\n\tstorageKey = DEFAULT_STORAGE_KEY,\n}: {\n\tdefaultTheme?: Theme;\n\tstorageKey?: string;\n}) => (\n\t<>\n\t\t<script\n\t\t\tdangerouslySetInnerHTML={{\n\t\t\t\t__html: preventWrongThemeFlashScriptContent({ defaultTheme, storageKey }),\n\t\t\t}}\n\t\t/>\n\t\t<PreloadFonts />\n\t</>\n);\n\ntype InitialThemeProps = {\n\tclassName: string;\n\t\"data-applied-theme\": Omit<Theme, \"system\">;\n\t\"data-theme\": Theme;\n};\n\n/**\n * useInitialHtmlThemeProps returns the initial props that should be applied to the <html> element to prevent react hydration errors.\n */\nfunction useInitialHtmlThemeProps(props?: {\n\tclassName?: string;\n\tdefaultTheme?: Theme;\n\tstorageKey?: string;\n}): InitialThemeProps {\n\tconst { className = \"\", defaultTheme = \"system\", storageKey = DEFAULT_STORAGE_KEY } = props ?? {};\n\n\treturn useMemo(() => {\n\t\tif (!isBrowser()) {\n\t\t\treturn {\n\t\t\t\tclassName: clsx(className),\n\t\t\t\t\"data-applied-theme\": \"system\",\n\t\t\t\t\"data-theme\": \"system\",\n\t\t\t};\n\t\t}\n\n\t\tconst prefersDarkMode = window.matchMedia(prefersDarkModeMediaQuery).matches;\n\t\tconst prefersHighContrast = window.matchMedia(prefersHighContrastMediaQuery).matches;\n\t\tconst initialTheme = getStoredTheme(storageKey, defaultTheme);\n\t\tconst reolvedTheme = resolveTheme(initialTheme, { prefersDarkMode, prefersHighContrast });\n\n\t\treturn {\n\t\t\tclassName: clsx(className, reolvedTheme),\n\t\t\t\"data-applied-theme\": reolvedTheme,\n\t\t\t\"data-theme\": initialTheme,\n\t\t};\n\t}, [className, defaultTheme, storageKey]);\n}\n\nexport {\n\t//,\n\t$resolvedTheme,\n\t$theme,\n\tapplyTheme,\n\tisResolvedTheme,\n\tisTheme,\n\tMantleThemeHeadContent,\n\tpreventWrongThemeFlashScriptContent,\n\treadThemeFromHtmlElement,\n\tresolvedThemes,\n\tThemeProvider,\n\tthemes,\n\tuseAppliedTheme,\n\tuseInitialHtmlThemeProps,\n\tuseTheme,\n};\n\nexport type {\n\t//,\n\tResolvedTheme,\n\tTheme,\n\tThemeProviderProps,\n};\n","const cdnBase = \"https://cdn.ngrok.com/static/fonts\";\n\nconst fonts = [\n\t\"euclid-square/EuclidSquare-Regular-WebS.woff\",\n\t\"euclid-square/EuclidSquare-RegularItalic-WebS.woff\",\n\t\"euclid-square/EuclidSquare-Medium-WebS.woff\",\n\t\"euclid-square/EuclidSquare-Semibold-WebS.woff\",\n\t\"euclid-square/EuclidSquare-MediumItalic-WebS.woff\",\n\t\"ibm-plex-mono/IBMPlexMono-Text.woff\",\n\t\"ibm-plex-mono/IBMPlexMono-TextItalic.woff\",\n\t\"ibm-plex-mono/IBMPlexMono-SemiBold.woff\",\n\t\"ibm-plex-mono/IBMPlexMono-SemiBoldItalic.woff\",\n] as const;\n\ntype Font = (typeof fonts)[number];\n\nconst fontHref = <T extends Font>(font: T) => [cdnBase, font].join(\"/\");\n\n/**\n * Preload custom fonts used in the theme. This should be added to the head of the document in your application, preferably as high as possible.\n * Normally you won't use this directly, but instead use the `MantleThemeHeadContent` component which includes this.\n */\nconst PreloadFonts = () => (\n\t<>\n\t\t{fonts.map((font) => (\n\t\t\t<link key={font} rel=\"preload\" href={fontHref(font)} as=\"font\" type=\"font/woff\" crossOrigin=\"anonymous\" />\n\t\t))}\n\t</>\n);\n\nexport { PreloadFonts };\n"],"mappings":"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","names":["clsx","createContext","useContext","useEffect","useMemo","useState","invariant","Fragment","jsx","cdnBase","fonts","fontHref","font","PreloadFonts","Fragment","jsx","jsxs","prefersDarkModeMediaQuery","prefersHighContrastMediaQuery","resolvedThemes","themes","$theme","value","isTheme","$resolvedTheme","isResolvedTheme","DEFAULT_STORAGE_KEY","initialState","ThemeProviderContext","createContext","isBrowser","getStoredTheme","storageKey","defaultTheme","fallbackTheme","storedTheme","ThemeProvider","children","theme","setTheme","useState","initialTheme","applyTheme","useEffect","prefersDarkMql","prefersHighContrastMql","onChange","useMemo","useTheme","context","useContext","invariant","htmlElement","prefersDarkMode","prefersHighContrast","newTheme","resolveTheme","readThemeFromHtmlElement","determineThemeFromMediaQuery","useAppliedTheme","useMatchesMediaQuery","preventWrongThemeFlashScriptContent","MantleThemeHeadContent","PreloadFonts","useInitialHtmlThemeProps","props","className","clsx","reolvedTheme"]}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "mantle is ngrok's UI library and design system.",
4
4
  "author": "ngrok",
5
5
  "license": "MIT",
6
- "version": "0.2.1",
6
+ "version": "0.3.0",
7
7
  "homepage": "https://mantle.ngrok.com",
8
8
  "repository": {
9
9
  "type": "git",
@@ -25,7 +25,7 @@
25
25
  "node": "^20.0.0"
26
26
  },
27
27
  "dependencies": {
28
- "@headlessui/react": "2.1.5",
28
+ "@headlessui/react": "2.1.8",
29
29
  "@radix-ui/react-dialog": "1.1.1",
30
30
  "@radix-ui/react-dropdown-menu": "2.1.1",
31
31
  "@radix-ui/react-popover": "1.1.1",
@@ -49,24 +49,24 @@
49
49
  "@testing-library/dom": "10.4.0",
50
50
  "@testing-library/react": "16.0.1",
51
51
  "@testing-library/user-event": "14.5.2",
52
- "@types/node": "20.16.5",
52
+ "@types/node": "20.16.6",
53
53
  "@types/prismjs": "1.26.4",
54
- "@types/react": "18.3.5",
54
+ "@types/react": "18.3.9",
55
55
  "@types/react-dom": "18.3.0",
56
56
  "@vitejs/plugin-react": "4.3.1",
57
- "@vitest/ui": "2.0.5",
57
+ "@vitest/ui": "2.1.1",
58
58
  "autoprefixer": "10.4.20",
59
59
  "browserslist": "4.23.3",
60
- "date-fns": "3.6.0",
61
- "jsdom": "25.0.0",
62
- "postcss": "8.4.45",
60
+ "date-fns": "4.1.0",
61
+ "jsdom": "25.0.1",
62
+ "postcss": "8.4.47",
63
63
  "react": "18.3.1",
64
64
  "react-dom": "18.3.1",
65
- "react-router-dom": "6.26.1",
66
- "tailwindcss": "3.4.10",
65
+ "react-router-dom": "6.26.2",
66
+ "tailwindcss": "3.4.13",
67
67
  "tsup": "8.3.0",
68
- "typescript": "5.5.4",
69
- "vite": "5.4.6",
68
+ "typescript": "5.6.2",
69
+ "vite": "5.4.7",
70
70
  "vitest": "2.0.5",
71
71
  "vitest-dom": "0.1.1",
72
72
  "zod": "3.23.8",
@@ -74,11 +74,11 @@
74
74
  },
75
75
  "peerDependencies": {
76
76
  "@phosphor-icons/react": "2.1.7",
77
- "date-fns": "^3.6.0",
78
- "postcss": "^8.4.45",
77
+ "date-fns": "^4.1.0",
78
+ "postcss": "^8.4.47",
79
79
  "react": "^18.3.1",
80
80
  "react-dom": "^18.3.1",
81
- "tailwindcss": "^3.4.10",
81
+ "tailwindcss": "^3.4.13",
82
82
  "zod": "^3.23.8"
83
83
  },
84
84
  "exports": {