@dbcdk/react-components 0.0.172 → 0.0.174

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.
Files changed (67) hide show
  1. package/dist/client.cjs +14 -0
  2. package/dist/client.d.ts +2 -0
  3. package/dist/client.js +2 -0
  4. package/dist/components/app-header/AppHeader.module.css +2 -2
  5. package/dist/components/button/Button.d.ts +3 -1
  6. package/dist/components/button/Button.module.css +26 -0
  7. package/dist/components/color-scheme-button/ColorSchemeButton.cjs +78 -0
  8. package/dist/components/color-scheme-button/ColorSchemeButton.d.ts +8 -0
  9. package/dist/components/color-scheme-button/ColorSchemeButton.js +75 -0
  10. package/dist/components/forms/form-select/FormSelect.d.ts +1 -1
  11. package/dist/components/forms/form-select/FormSelect.module.css +19 -0
  12. package/dist/components/forms/input/Input.cjs +1 -1
  13. package/dist/components/forms/input/Input.d.ts +1 -1
  14. package/dist/components/forms/input/Input.js +1 -1
  15. package/dist/components/forms/input/Input.module.css +32 -0
  16. package/dist/components/forms/typeahead/Typeahead.cjs +1 -0
  17. package/dist/components/forms/typeahead/Typeahead.js +1 -0
  18. package/dist/components/grid/Grid.cjs +4 -3
  19. package/dist/components/grid/Grid.d.ts +3 -3
  20. package/dist/components/grid/Grid.js +4 -3
  21. package/dist/components/grid/Grid.module.css +1 -1
  22. package/dist/components/hyperlink/Hyperlink.cjs +2 -1
  23. package/dist/components/hyperlink/Hyperlink.d.ts +2 -1
  24. package/dist/components/hyperlink/Hyperlink.js +2 -1
  25. package/dist/components/hyperlink/Hyperlink.module.css +16 -0
  26. package/dist/components/metric-tile/MetricTile.cjs +77 -8
  27. package/dist/components/metric-tile/MetricTile.d.ts +17 -4
  28. package/dist/components/metric-tile/MetricTile.js +78 -9
  29. package/dist/components/metric-tile/MetricTile.module.css +122 -19
  30. package/dist/components/nav-bar/NavBar.cjs +1 -0
  31. package/dist/components/nav-bar/NavBar.js +1 -0
  32. package/dist/components/nav-bar/NavBar.module.css +10 -10
  33. package/dist/components/page-layout/PageLayout.module.css +1 -1
  34. package/dist/components/popover/Popover.cjs +4 -2
  35. package/dist/components/popover/Popover.d.ts +2 -0
  36. package/dist/components/popover/Popover.js +4 -2
  37. package/dist/components/popover/Popover.module.css +5 -0
  38. package/dist/components/theme-button/ThemeButton.cjs +30 -23
  39. package/dist/components/theme-button/ThemeButton.d.ts +9 -3
  40. package/dist/components/theme-button/ThemeButton.js +31 -24
  41. package/dist/components/theme-script/ThemeScript.cjs +50 -12
  42. package/dist/components/theme-script/ThemeScript.d.ts +35 -12
  43. package/dist/components/theme-script/ThemeScript.js +51 -13
  44. package/dist/hooks/useColorScheme.cjs +74 -0
  45. package/dist/hooks/useColorScheme.d.ts +6 -0
  46. package/dist/hooks/useColorScheme.js +72 -0
  47. package/dist/hooks/useTheme.cjs +116 -50
  48. package/dist/hooks/useTheme.d.ts +17 -5
  49. package/dist/hooks/useTheme.js +117 -51
  50. package/dist/index.cjs +14 -0
  51. package/dist/index.d.ts +2 -0
  52. package/dist/index.js +2 -0
  53. package/dist/styles/themes/dbc/colors.css +13 -2
  54. package/dist/styles/themes/filmstriben/theme.css +55 -0
  55. package/dist/styles/themes/types.cjs +8 -0
  56. package/dist/styles/themes/types.d.ts +24 -16
  57. package/dist/styles/themes/types.js +6 -0
  58. package/dist/themes/filmstriben.css +4 -0
  59. package/dist/utils/color-scheme.constants.cjs +11 -0
  60. package/dist/utils/color-scheme.constants.d.ts +4 -0
  61. package/dist/utils/color-scheme.constants.js +7 -0
  62. package/dist/utils/theme.constants.cjs +3 -7
  63. package/dist/utils/theme.constants.d.ts +2 -4
  64. package/dist/utils/theme.constants.js +3 -6
  65. package/package.json +1 -1
  66. package/dist/styles/themes/filmstriben-budget/theme.css +0 -22
  67. package/dist/themes/filmstriben-budget.css +0 -3
@@ -1,40 +1,47 @@
1
1
  'use client';
2
2
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
3
- import { Palette, Monitor, Sun, Moon } from 'lucide-react';
3
+ import { Check, Palette } from 'lucide-react';
4
4
  import { Button } from '../../components/button/Button';
5
5
  import { Menu } from '../../components/menu/Menu';
6
6
  import { Popover } from '../../components/popover/Popover';
7
7
  import { useTheme } from '../../hooks/useTheme';
8
+ import { THEME_LABELS } from '../../styles/themes/types';
8
9
 
9
- const THEME_OPTIONS = [
10
- { value: "system", label: "System", icon: /* @__PURE__ */ jsx(Monitor, { size: 16 }) },
11
- { value: "light", label: "Lyst", icon: /* @__PURE__ */ jsx(Sun, { size: 16 }) },
12
- { value: "dark", label: "M\xF8rkt", icon: /* @__PURE__ */ jsx(Moon, { size: 16 }) }
13
- ];
14
- function ThemeMenuSection() {
15
- const { theme, switchTheme } = useTheme();
10
+ function ThemeMenuSection({ themes, hrefs }) {
11
+ const { theme, switchTheme } = useTheme({
12
+ defaultTheme: themes[0],
13
+ hrefs
14
+ });
16
15
  return /* @__PURE__ */ jsxs(Fragment, { children: [
17
- /* @__PURE__ */ jsx(Menu.Header, { children: "Udseende" }),
18
- THEME_OPTIONS.map((option) => /* @__PURE__ */ jsx(
16
+ /* @__PURE__ */ jsx(Menu.Header, { children: "Tema" }),
17
+ themes.map((id) => /* @__PURE__ */ jsx(
19
18
  Menu.RadioItem,
20
19
  {
21
20
  name: "theme",
22
- value: option.value,
23
- checked: theme === option.value,
24
- label: option.label,
25
- onValueChange: (value) => switchTheme(value)
21
+ value: id,
22
+ checked: theme === id,
23
+ label: THEME_LABELS[id],
24
+ onValueChange: () => switchTheme(id)
26
25
  },
27
- option.value
26
+ id
28
27
  ))
29
28
  ] });
30
29
  }
31
- function ThemeButton({ size, variant = "outlined" }) {
32
- const { theme, switchTheme } = useTheme();
30
+ function ThemeButton({
31
+ themes,
32
+ hrefs,
33
+ size,
34
+ variant = "outlined"
35
+ }) {
36
+ const { theme, switchTheme } = useTheme({
37
+ defaultTheme: themes[0],
38
+ hrefs
39
+ });
33
40
  return /* @__PURE__ */ jsx(
34
41
  Popover,
35
42
  {
36
43
  matchTriggerWidth: false,
37
- minWidth: "140px",
44
+ minWidth: "160px",
38
45
  trigger: (handleClick, chevron) => /* @__PURE__ */ jsxs(
39
46
  Button,
40
47
  {
@@ -50,20 +57,20 @@ function ThemeButton({ size, variant = "outlined" }) {
50
57
  }
51
58
  ),
52
59
  children: (close) => /* @__PURE__ */ jsxs(Menu, { children: [
53
- /* @__PURE__ */ jsx(Menu.Header, { children: "Udseende" }),
54
- THEME_OPTIONS.map((option) => /* @__PURE__ */ jsx(Menu.Item, { active: theme === option.value, children: /* @__PURE__ */ jsxs(
60
+ /* @__PURE__ */ jsx(Menu.Header, { children: "Tema" }),
61
+ themes.map((id) => /* @__PURE__ */ jsx(Menu.Item, { active: theme === id, children: /* @__PURE__ */ jsxs(
55
62
  "button",
56
63
  {
57
64
  onClick: () => {
58
- switchTheme(option.value);
65
+ switchTheme(id);
59
66
  close();
60
67
  },
61
68
  children: [
62
- option.icon,
63
- option.label
69
+ THEME_LABELS[id],
70
+ theme === id && /* @__PURE__ */ jsx(Check, { size: 16 })
64
71
  ]
65
72
  }
66
- ) }, option.value))
73
+ ) }, id))
67
74
  ] })
68
75
  }
69
76
  );
@@ -1,22 +1,60 @@
1
1
  'use strict';
2
2
 
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
+ var colorScheme_constants = require('../../utils/color-scheme.constants');
4
5
  var theme_constants = require('../../utils/theme.constants');
5
6
 
6
- function buildThemeInitScript(defaultTheme) {
7
- return `(function(){try{
8
- var KEY=${JSON.stringify(theme_constants.THEME_STORAGE_KEY)};
9
- var VARIANTS=${JSON.stringify(theme_constants.THEME_VARIANTS)};
10
- function isVariant(x){return !!x && VARIANTS.indexOf(x)!==-1}
7
+ function buildInitScript(defaultColorScheme, hrefs, defaultTheme, linkId, storageKey) {
8
+ const hrefById = {};
9
+ for (const [id, href] of Object.entries(hrefs)) {
10
+ if (id !== defaultTheme && href) hrefById[id] = href;
11
+ }
12
+ const themeBlock = defaultTheme === void 0 ? "" : `try{
13
+ var THEME_KEY=${JSON.stringify(storageKey)};
14
+ var THEME_DEFAULT=${JSON.stringify(defaultTheme)};
15
+ var HREFS=${JSON.stringify(hrefById)};
16
+ var LINK_ID=${JSON.stringify(linkId)};
17
+ var resolvedTheme=getCookie(THEME_KEY);
18
+ if(!resolvedTheme){try{resolvedTheme=localStorage.getItem(THEME_KEY)}catch(e){}}
19
+ if(!resolvedTheme)resolvedTheme=THEME_DEFAULT;
20
+ if(resolvedTheme!==THEME_DEFAULT&&HREFS[resolvedTheme]){
21
+ var link=document.createElement('link');
22
+ link.id=LINK_ID;
23
+ link.rel='stylesheet';
24
+ link.href=HREFS[resolvedTheme];
25
+ document.head.appendChild(link);
26
+ }
27
+ document.documentElement.dataset.productTheme=resolvedTheme;
28
+ }catch(e){}`;
29
+ return `(function(){
11
30
  function getCookie(name){var m=document.cookie.match(new RegExp('(?:^|; )'+name+'=([^;]*)'));return m?decodeURIComponent(m[1]):null}
12
- var resolved=${JSON.stringify(defaultTheme)};
13
- var fromCookie=getCookie(KEY);
14
- if(isVariant(fromCookie)){resolved=fromCookie}else{try{var fromStorage=localStorage.getItem(KEY);if(isVariant(fromStorage))resolved=fromStorage}catch(e){}}
15
- document.documentElement.dataset.theme=resolved;
16
- }catch(e){}})();`;
31
+ try{
32
+ var SCHEME_KEY=${JSON.stringify(colorScheme_constants.COLOR_SCHEME_STORAGE_KEY)};
33
+ var SCHEMES=${JSON.stringify(colorScheme_constants.COLOR_SCHEMES)};
34
+ function isScheme(x){return !!x && SCHEMES.indexOf(x)!==-1}
35
+ var resolvedScheme=${JSON.stringify(defaultColorScheme)};
36
+ var fromCookie=getCookie(SCHEME_KEY);
37
+ if(isScheme(fromCookie)){resolvedScheme=fromCookie}else{try{var fromStorage=localStorage.getItem(SCHEME_KEY);if(isScheme(fromStorage))resolvedScheme=fromStorage}catch(e){}}
38
+ document.documentElement.dataset.theme=resolvedScheme;
39
+ }catch(e){}
40
+ ${themeBlock}
41
+ })();`;
17
42
  }
18
- function ThemeScript({ defaultTheme = "system" }) {
19
- return /* @__PURE__ */ jsxRuntime.jsx("script", { dangerouslySetInnerHTML: { __html: buildThemeInitScript(defaultTheme) } });
43
+ function ThemeScript({
44
+ defaultColorScheme = "system",
45
+ defaultTheme,
46
+ hrefs = {},
47
+ linkId = theme_constants.THEME_LINK_ID,
48
+ storageKey = theme_constants.THEME_STORAGE_KEY
49
+ }) {
50
+ return /* @__PURE__ */ jsxRuntime.jsx(
51
+ "script",
52
+ {
53
+ dangerouslySetInnerHTML: {
54
+ __html: buildInitScript(defaultColorScheme, hrefs, defaultTheme, linkId, storageKey)
55
+ }
56
+ }
57
+ );
20
58
  }
21
59
 
22
60
  exports.ThemeScript = ThemeScript;
@@ -1,25 +1,48 @@
1
- import type { ThemeVariant } from '../../utils/theme.constants';
1
+ import type { ThemeHrefs, ThemeId } from '../../styles/themes/types';
2
+ import type { ColorScheme } from '../../utils/color-scheme.constants';
2
3
  export interface ThemeScriptProps {
3
- /** Theme applied when nothing is stored yet. Defaults to 'system'. */
4
- defaultTheme?: ThemeVariant;
4
+ /** Color scheme applied when nothing is stored yet. Defaults to 'system'. */
5
+ defaultColorScheme?: ColorScheme;
6
+ /**
7
+ * The product theme the app already loads via a normal static import. Omit
8
+ * entirely if the app only ever ships one theme (e.g. just dbc) — no
9
+ * `data-product-theme` handling happens at all in that case.
10
+ */
11
+ defaultTheme?: ThemeId;
12
+ /** Per-theme CSS URL, for any theme not already loaded some other way. */
13
+ hrefs?: ThemeHrefs;
14
+ /** DOM id for the `<link>` this script (and useTheme) manage. */
15
+ linkId?: string;
16
+ storageKey?: string;
5
17
  }
6
18
  /**
7
- * Renders a blocking inline <script> that reads the persisted theme (cookie,
8
- * falling back to localStorage) and sets `data-theme` on <html> synchronously
9
- * — before the page paints and before React hydrates.
19
+ * Renders a blocking inline <script> that restores both the persisted color
20
+ * scheme (light/dark/system) and, if `defaultTheme` is given, the persisted
21
+ * product theme synchronously, before the page paints and before React
22
+ * hydrates. One tag covers both axes, so most apps only ever need this one
23
+ * component in `<head>`.
10
24
  *
11
- * `useTheme()` alone cannot prevent a flash of the wrong theme on load: its
12
- * resolution runs inside a `useEffect`, which by definition only fires after
13
- * the first render has already been committed (and, server-side, there is no
14
- * `document` to read a cookie from at all). Closing that gap requires code
15
- * that runs outside React's render lifecycle — this component is that code.
25
+ * Neither `useColorScheme()` nor `useTheme()` alone can prevent a flash of
26
+ * the wrong value on load: their resolution runs inside a `useEffect`, which
27
+ * only fires after the first render has already been committed (and,
28
+ * server-side, there is no `document` to read a cookie from at all). Closing
29
+ * that gap requires code that runs outside React's render lifecycle — this
30
+ * component is that code.
16
31
  *
17
32
  * Place it as early as possible in <head>, e.g. in Next.js's `_document.js`:
18
33
  *
19
34
  * ```tsx
20
35
  * <Head>
36
+ * <ThemeScript defaultTheme="filmstriben" />
37
+ * </Head>
38
+ * ```
39
+ *
40
+ * Or, for an app with only one product theme (no `defaultTheme` needed):
41
+ *
42
+ * ```tsx
43
+ * <Head>
21
44
  * <ThemeScript />
22
45
  * </Head>
23
46
  * ```
24
47
  */
25
- export declare function ThemeScript({ defaultTheme }: ThemeScriptProps): import("react/jsx-runtime").JSX.Element;
48
+ export declare function ThemeScript({ defaultColorScheme, defaultTheme, hrefs, linkId, storageKey, }: ThemeScriptProps): import("react/jsx-runtime").JSX.Element;
@@ -1,20 +1,58 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { THEME_STORAGE_KEY, THEME_VARIANTS } from '../../utils/theme.constants';
2
+ import { COLOR_SCHEME_STORAGE_KEY, COLOR_SCHEMES } from '../../utils/color-scheme.constants';
3
+ import { THEME_LINK_ID, THEME_STORAGE_KEY } from '../../utils/theme.constants';
3
4
 
4
- function buildThemeInitScript(defaultTheme) {
5
- return `(function(){try{
6
- var KEY=${JSON.stringify(THEME_STORAGE_KEY)};
7
- var VARIANTS=${JSON.stringify(THEME_VARIANTS)};
8
- function isVariant(x){return !!x && VARIANTS.indexOf(x)!==-1}
5
+ function buildInitScript(defaultColorScheme, hrefs, defaultTheme, linkId, storageKey) {
6
+ const hrefById = {};
7
+ for (const [id, href] of Object.entries(hrefs)) {
8
+ if (id !== defaultTheme && href) hrefById[id] = href;
9
+ }
10
+ const themeBlock = defaultTheme === void 0 ? "" : `try{
11
+ var THEME_KEY=${JSON.stringify(storageKey)};
12
+ var THEME_DEFAULT=${JSON.stringify(defaultTheme)};
13
+ var HREFS=${JSON.stringify(hrefById)};
14
+ var LINK_ID=${JSON.stringify(linkId)};
15
+ var resolvedTheme=getCookie(THEME_KEY);
16
+ if(!resolvedTheme){try{resolvedTheme=localStorage.getItem(THEME_KEY)}catch(e){}}
17
+ if(!resolvedTheme)resolvedTheme=THEME_DEFAULT;
18
+ if(resolvedTheme!==THEME_DEFAULT&&HREFS[resolvedTheme]){
19
+ var link=document.createElement('link');
20
+ link.id=LINK_ID;
21
+ link.rel='stylesheet';
22
+ link.href=HREFS[resolvedTheme];
23
+ document.head.appendChild(link);
24
+ }
25
+ document.documentElement.dataset.productTheme=resolvedTheme;
26
+ }catch(e){}`;
27
+ return `(function(){
9
28
  function getCookie(name){var m=document.cookie.match(new RegExp('(?:^|; )'+name+'=([^;]*)'));return m?decodeURIComponent(m[1]):null}
10
- var resolved=${JSON.stringify(defaultTheme)};
11
- var fromCookie=getCookie(KEY);
12
- if(isVariant(fromCookie)){resolved=fromCookie}else{try{var fromStorage=localStorage.getItem(KEY);if(isVariant(fromStorage))resolved=fromStorage}catch(e){}}
13
- document.documentElement.dataset.theme=resolved;
14
- }catch(e){}})();`;
29
+ try{
30
+ var SCHEME_KEY=${JSON.stringify(COLOR_SCHEME_STORAGE_KEY)};
31
+ var SCHEMES=${JSON.stringify(COLOR_SCHEMES)};
32
+ function isScheme(x){return !!x && SCHEMES.indexOf(x)!==-1}
33
+ var resolvedScheme=${JSON.stringify(defaultColorScheme)};
34
+ var fromCookie=getCookie(SCHEME_KEY);
35
+ if(isScheme(fromCookie)){resolvedScheme=fromCookie}else{try{var fromStorage=localStorage.getItem(SCHEME_KEY);if(isScheme(fromStorage))resolvedScheme=fromStorage}catch(e){}}
36
+ document.documentElement.dataset.theme=resolvedScheme;
37
+ }catch(e){}
38
+ ${themeBlock}
39
+ })();`;
15
40
  }
16
- function ThemeScript({ defaultTheme = "system" }) {
17
- return /* @__PURE__ */ jsx("script", { dangerouslySetInnerHTML: { __html: buildThemeInitScript(defaultTheme) } });
41
+ function ThemeScript({
42
+ defaultColorScheme = "system",
43
+ defaultTheme,
44
+ hrefs = {},
45
+ linkId = THEME_LINK_ID,
46
+ storageKey = THEME_STORAGE_KEY
47
+ }) {
48
+ return /* @__PURE__ */ jsx(
49
+ "script",
50
+ {
51
+ dangerouslySetInnerHTML: {
52
+ __html: buildInitScript(defaultColorScheme, hrefs, defaultTheme, linkId, storageKey)
53
+ }
54
+ }
55
+ );
18
56
  }
19
57
 
20
58
  export { ThemeScript };
@@ -0,0 +1,74 @@
1
+ 'use client';
2
+ 'use strict';
3
+
4
+ var react = require('react');
5
+ var colorScheme_constants = require('../utils/color-scheme.constants');
6
+ var cookie_utils = require('../utils/cookie.utils');
7
+ var localStorage_utils = require('../utils/localStorage.utils');
8
+
9
+ function persistColorScheme(id) {
10
+ localStorage_utils.writeLocalStorage(colorScheme_constants.COLOR_SCHEME_STORAGE_KEY, id);
11
+ cookie_utils.writeCookie(colorScheme_constants.COLOR_SCHEME_STORAGE_KEY, id);
12
+ }
13
+ function getDomColorScheme() {
14
+ return document.documentElement.dataset.theme;
15
+ }
16
+ function applyColorScheme(id) {
17
+ document.documentElement.dataset.theme = id;
18
+ }
19
+ let currentColorScheme = null;
20
+ let resolved = false;
21
+ const listeners = /* @__PURE__ */ new Set();
22
+ function notify() {
23
+ listeners.forEach((listener) => listener());
24
+ }
25
+ function subscribe(listener) {
26
+ listeners.add(listener);
27
+ return () => {
28
+ listeners.delete(listener);
29
+ };
30
+ }
31
+ function getSnapshot() {
32
+ return currentColorScheme;
33
+ }
34
+ function getServerSnapshot() {
35
+ return null;
36
+ }
37
+ function resolveInitialColorScheme(initialColorScheme) {
38
+ const fromDomAttribute = getDomColorScheme();
39
+ let resolvedColorScheme = colorScheme_constants.isColorScheme(fromDomAttribute) ? fromDomAttribute : initialColorScheme;
40
+ const fromCookie = cookie_utils.readCookie(colorScheme_constants.COLOR_SCHEME_STORAGE_KEY);
41
+ if (colorScheme_constants.isColorScheme(fromCookie)) {
42
+ resolvedColorScheme = fromCookie;
43
+ } else {
44
+ const fromStorage = localStorage_utils.readLocalStorage(colorScheme_constants.COLOR_SCHEME_STORAGE_KEY);
45
+ if (colorScheme_constants.isColorScheme(fromStorage)) resolvedColorScheme = fromStorage;
46
+ }
47
+ return resolvedColorScheme;
48
+ }
49
+ function ensureResolved(initialColorScheme) {
50
+ if (resolved) return;
51
+ resolved = true;
52
+ const value = resolveInitialColorScheme(initialColorScheme);
53
+ applyColorScheme(value);
54
+ persistColorScheme(value);
55
+ currentColorScheme = value;
56
+ notify();
57
+ }
58
+ function switchColorScheme(id) {
59
+ applyColorScheme(id);
60
+ persistColorScheme(id);
61
+ currentColorScheme = id;
62
+ resolved = true;
63
+ notify();
64
+ return id;
65
+ }
66
+ function useColorScheme(initialColorScheme = "system") {
67
+ react.useEffect(() => {
68
+ ensureResolved(initialColorScheme);
69
+ }, [initialColorScheme]);
70
+ const colorScheme = react.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
71
+ return { colorScheme, switchColorScheme };
72
+ }
73
+
74
+ exports.useColorScheme = useColorScheme;
@@ -0,0 +1,6 @@
1
+ import type { ColorScheme } from '../utils/color-scheme.constants';
2
+ export type { ColorScheme };
3
+ export declare function useColorScheme(initialColorScheme?: ColorScheme): {
4
+ colorScheme: ColorScheme | null;
5
+ switchColorScheme: (id: ColorScheme) => ColorScheme;
6
+ };
@@ -0,0 +1,72 @@
1
+ 'use client';
2
+ import { useEffect, useSyncExternalStore } from 'react';
3
+ import { isColorScheme, COLOR_SCHEME_STORAGE_KEY } from '../utils/color-scheme.constants';
4
+ import { readCookie, writeCookie } from '../utils/cookie.utils';
5
+ import { readLocalStorage, writeLocalStorage } from '../utils/localStorage.utils';
6
+
7
+ function persistColorScheme(id) {
8
+ writeLocalStorage(COLOR_SCHEME_STORAGE_KEY, id);
9
+ writeCookie(COLOR_SCHEME_STORAGE_KEY, id);
10
+ }
11
+ function getDomColorScheme() {
12
+ return document.documentElement.dataset.theme;
13
+ }
14
+ function applyColorScheme(id) {
15
+ document.documentElement.dataset.theme = id;
16
+ }
17
+ let currentColorScheme = null;
18
+ let resolved = false;
19
+ const listeners = /* @__PURE__ */ new Set();
20
+ function notify() {
21
+ listeners.forEach((listener) => listener());
22
+ }
23
+ function subscribe(listener) {
24
+ listeners.add(listener);
25
+ return () => {
26
+ listeners.delete(listener);
27
+ };
28
+ }
29
+ function getSnapshot() {
30
+ return currentColorScheme;
31
+ }
32
+ function getServerSnapshot() {
33
+ return null;
34
+ }
35
+ function resolveInitialColorScheme(initialColorScheme) {
36
+ const fromDomAttribute = getDomColorScheme();
37
+ let resolvedColorScheme = isColorScheme(fromDomAttribute) ? fromDomAttribute : initialColorScheme;
38
+ const fromCookie = readCookie(COLOR_SCHEME_STORAGE_KEY);
39
+ if (isColorScheme(fromCookie)) {
40
+ resolvedColorScheme = fromCookie;
41
+ } else {
42
+ const fromStorage = readLocalStorage(COLOR_SCHEME_STORAGE_KEY);
43
+ if (isColorScheme(fromStorage)) resolvedColorScheme = fromStorage;
44
+ }
45
+ return resolvedColorScheme;
46
+ }
47
+ function ensureResolved(initialColorScheme) {
48
+ if (resolved) return;
49
+ resolved = true;
50
+ const value = resolveInitialColorScheme(initialColorScheme);
51
+ applyColorScheme(value);
52
+ persistColorScheme(value);
53
+ currentColorScheme = value;
54
+ notify();
55
+ }
56
+ function switchColorScheme(id) {
57
+ applyColorScheme(id);
58
+ persistColorScheme(id);
59
+ currentColorScheme = id;
60
+ resolved = true;
61
+ notify();
62
+ return id;
63
+ }
64
+ function useColorScheme(initialColorScheme = "system") {
65
+ useEffect(() => {
66
+ ensureResolved(initialColorScheme);
67
+ }, [initialColorScheme]);
68
+ const colorScheme = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
69
+ return { colorScheme, switchColorScheme };
70
+ }
71
+
72
+ export { useColorScheme };
@@ -2,34 +2,59 @@
2
2
  'use strict';
3
3
 
4
4
  var react = require('react');
5
+ var cookie_utils = require('../utils/cookie.utils');
6
+ var localStorage_utils = require('../utils/localStorage.utils');
5
7
  var theme_constants = require('../utils/theme.constants');
6
8
 
7
- function getCookie(name) {
8
- const match = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`));
9
- return match ? decodeURIComponent(match[1]) : null;
10
- }
11
- function persistTheme(id) {
12
- try {
13
- localStorage.setItem(theme_constants.THEME_STORAGE_KEY, id);
14
- } catch {
15
- console.error("Failed to access localStorage");
16
- }
17
- try {
18
- document.cookie = `${theme_constants.THEME_STORAGE_KEY}=${encodeURIComponent(
19
- id
20
- )}; Path=/; Max-Age=${60 * 60 * 24 * 365}`;
21
- } catch {
22
- console.error("Failed to set theme cookie");
23
- }
9
+ const PRELOAD_ID_PREFIX = "dbc-theme-preload-";
10
+ function persistTheme(storageKey, id) {
11
+ localStorage_utils.writeLocalStorage(storageKey, id);
12
+ cookie_utils.writeCookie(storageKey, id);
24
13
  }
25
14
  function getDomTheme() {
26
- return document.documentElement.dataset.theme;
15
+ return document.documentElement.dataset.productTheme;
27
16
  }
28
17
  function applyTheme(id) {
29
- document.documentElement.dataset.theme = id;
18
+ document.documentElement.dataset.productTheme = id;
19
+ }
20
+ function findLink(linkId) {
21
+ return document.getElementById(linkId);
22
+ }
23
+ function findPreloadLink(id) {
24
+ return document.getElementById(`${PRELOAD_ID_PREFIX}${id}`);
25
+ }
26
+ function ensureStylesheet(id, href, linkId, onReady) {
27
+ var _a;
28
+ if (!href) {
29
+ onReady();
30
+ return;
31
+ }
32
+ const active = findLink(linkId);
33
+ if (active && active.getAttribute("href") === href && active.rel === "stylesheet") {
34
+ onReady();
35
+ return;
36
+ }
37
+ const preload = findPreloadLink(id);
38
+ const link = (_a = active != null ? active : preload) != null ? _a : document.createElement("link");
39
+ const wasConnected = link.isConnected;
40
+ link.id = linkId;
41
+ link.rel = "stylesheet";
42
+ link.removeAttribute("as");
43
+ link.href = href;
44
+ if (preload && preload !== link) preload.remove();
45
+ let settled = false;
46
+ const finish = () => {
47
+ if (settled) return;
48
+ settled = true;
49
+ onReady();
50
+ };
51
+ link.addEventListener("load", finish, { once: true });
52
+ link.addEventListener("error", finish, { once: true });
53
+ if (!wasConnected) document.head.appendChild(link);
30
54
  }
31
55
  let currentTheme = null;
32
56
  let resolved = false;
57
+ let preloadScheduled = false;
33
58
  const listeners = /* @__PURE__ */ new Set();
34
59
  function notify() {
35
60
  listeners.forEach((listener) => listener());
@@ -46,44 +71,85 @@ function getSnapshot() {
46
71
  function getServerSnapshot() {
47
72
  return null;
48
73
  }
49
- function resolveInitialTheme(initialTheme) {
50
- const themeFromDataAttributes = getDomTheme();
51
- let resolvedTheme = theme_constants.isThemeVariant(themeFromDataAttributes) ? themeFromDataAttributes : initialTheme;
52
- const fromCookie = getCookie(theme_constants.THEME_STORAGE_KEY);
53
- if (theme_constants.isThemeVariant(fromCookie)) {
54
- resolvedTheme = fromCookie;
55
- } else {
56
- try {
57
- const fromStorage = localStorage.getItem(theme_constants.THEME_STORAGE_KEY);
58
- if (theme_constants.isThemeVariant(fromStorage)) resolvedTheme = fromStorage;
59
- } catch {
60
- console.error("Failed to access localStorage");
61
- }
62
- }
63
- return resolvedTheme;
74
+ function resolveInitialTheme(storageKey, defaultTheme) {
75
+ const fromDom = getDomTheme();
76
+ if (fromDom) return fromDom;
77
+ const fromCookie = cookie_utils.readCookie(storageKey);
78
+ if (fromCookie) return fromCookie;
79
+ const fromStorage = localStorage_utils.readLocalStorage(storageKey);
80
+ if (fromStorage) return fromStorage;
81
+ return defaultTheme;
64
82
  }
65
- function ensureResolved(initialTheme) {
83
+ function ensureResolved(hrefs, defaultTheme, storageKey, linkId) {
66
84
  if (resolved) return;
67
85
  resolved = true;
68
- const value = resolveInitialTheme(initialTheme);
69
- applyTheme(value);
70
- persistTheme(value);
71
- currentTheme = value;
72
- notify();
86
+ const value = resolveInitialTheme(storageKey, defaultTheme);
87
+ const finish = () => {
88
+ applyTheme(value);
89
+ persistTheme(storageKey, value);
90
+ currentTheme = value;
91
+ notify();
92
+ };
93
+ if (value === defaultTheme) {
94
+ finish();
95
+ return;
96
+ }
97
+ ensureStylesheet(value, hrefs[value], linkId, finish);
73
98
  }
74
- function switchTheme(id) {
75
- applyTheme(id);
76
- persistTheme(id);
77
- currentTheme = id;
78
- resolved = true;
79
- notify();
80
- return id;
99
+ function schedulePreload(hrefs, defaultTheme) {
100
+ if (preloadScheduled) return;
101
+ preloadScheduled = true;
102
+ const run = () => {
103
+ var _a;
104
+ for (const [id, href] of Object.entries(hrefs)) {
105
+ if (id === defaultTheme || !href) continue;
106
+ if (((_a = findLink(theme_constants.THEME_LINK_ID)) == null ? void 0 : _a.getAttribute("href")) === href) continue;
107
+ if (findPreloadLink(id)) continue;
108
+ const link = document.createElement("link");
109
+ link.id = `${PRELOAD_ID_PREFIX}${id}`;
110
+ link.rel = "preload";
111
+ link.as = "style";
112
+ link.href = href;
113
+ document.head.appendChild(link);
114
+ }
115
+ };
116
+ const requestIdle = window.requestIdleCallback;
117
+ if (typeof requestIdle === "function") {
118
+ requestIdle(run);
119
+ } else {
120
+ setTimeout(run, 1);
121
+ }
81
122
  }
82
- function useTheme(initialTheme = "system") {
123
+ function switchThemeInternal(hrefs, defaultTheme, storageKey, linkId, id) {
124
+ const finish = () => {
125
+ applyTheme(id);
126
+ persistTheme(storageKey, id);
127
+ currentTheme = id;
128
+ resolved = true;
129
+ notify();
130
+ };
131
+ if (id === defaultTheme) {
132
+ finish();
133
+ return;
134
+ }
135
+ ensureStylesheet(id, hrefs[id], linkId, finish);
136
+ }
137
+ function useTheme(options) {
138
+ const {
139
+ defaultTheme,
140
+ hrefs = {},
141
+ linkId = theme_constants.THEME_LINK_ID,
142
+ preloadOthers = true,
143
+ storageKey = theme_constants.THEME_STORAGE_KEY
144
+ } = options;
83
145
  react.useEffect(() => {
84
- ensureResolved(initialTheme);
85
- }, [initialTheme]);
146
+ ensureResolved(hrefs, defaultTheme, storageKey, linkId);
147
+ if (preloadOthers) schedulePreload(hrefs, defaultTheme);
148
+ }, []);
86
149
  const theme = react.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
150
+ function switchTheme(id) {
151
+ switchThemeInternal(hrefs, defaultTheme, storageKey, linkId, id);
152
+ }
87
153
  return { theme, switchTheme };
88
154
  }
89
155