@dbcdk/react-components 0.0.174 → 0.0.176

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 (40) hide show
  1. package/README.md +17 -18
  2. package/dist/components/filter-field/FilterField.module.css +2 -2
  3. package/dist/components/forms/file-upload/FileUpload.module.css +3 -3
  4. package/dist/components/forms/form-select/FormSelect.module.css +1 -1
  5. package/dist/components/forms/input/Input.module.css +8 -8
  6. package/dist/components/forms/text-area/Textarea.module.css +2 -1
  7. package/dist/components/nav-bar/NavBar.module.css +1 -1
  8. package/dist/components/theme-button/ThemeButton.cjs +4 -15
  9. package/dist/components/theme-button/ThemeButton.d.ts +3 -4
  10. package/dist/components/theme-button/ThemeButton.js +4 -15
  11. package/dist/components/theme-script/ThemeScript.cjs +23 -16
  12. package/dist/components/theme-script/ThemeScript.d.ts +2 -6
  13. package/dist/components/theme-script/ThemeScript.js +24 -17
  14. package/dist/hooks/usePersistentState.cjs +6 -1
  15. package/dist/hooks/usePersistentState.d.ts +1 -1
  16. package/dist/hooks/usePersistentState.js +6 -1
  17. package/dist/hooks/useTheme.cjs +33 -96
  18. package/dist/hooks/useTheme.d.ts +3 -10
  19. package/dist/hooks/useTheme.js +34 -97
  20. package/dist/styles/themes/apply-product-theme.cjs +20 -0
  21. package/dist/styles/themes/apply-product-theme.d.ts +3 -0
  22. package/dist/styles/themes/apply-product-theme.js +18 -0
  23. package/dist/styles/themes/dbc/colors.css +2 -1
  24. package/dist/styles/themes/product-theme-tokens.cjs +39 -0
  25. package/dist/styles/themes/product-theme-tokens.d.ts +3 -0
  26. package/dist/styles/themes/product-theme-tokens.js +37 -0
  27. package/dist/styles/themes/product-themes.cjs +143 -0
  28. package/dist/styles/themes/product-themes.d.ts +11 -0
  29. package/dist/styles/themes/product-themes.js +140 -0
  30. package/dist/styles/themes/types.cjs +3 -2
  31. package/dist/styles/themes/types.d.ts +1 -13
  32. package/dist/styles/themes/types.js +3 -2
  33. package/dist/utils/sessionStorage.utils.cjs +53 -0
  34. package/dist/utils/sessionStorage.utils.d.ts +19 -0
  35. package/dist/utils/sessionStorage.utils.js +49 -0
  36. package/package.json +1 -1
  37. package/dist/styles/themes/filmstriben/theme.css +0 -55
  38. package/dist/themes/filmstriben.css +0 -4
  39. package/dist/types/assets.d.cjs +0 -2
  40. package/dist/types/assets.d.js +0 -1
package/README.md CHANGED
@@ -44,35 +44,30 @@ import '@dbcdk/react-components/styles.css'
44
44
 
45
45
  ---
46
46
 
47
- ### 3) Add the theme `<link>` in your root layout (Next.js example)
47
+ ### 3) Add `ThemeScript` in your root layout (Next.js example)
48
48
 
49
- The library uses theme CSS files that are dynamically loaded via a `<link>` tag in `<head>`.
50
- You **must** use the exported `LINK_ID` so the `useTheme()` hook can update the active theme at runtime.
49
+ The library's shared CSS foundation is still imported normally, but
50
+ product-theme switching is handled by `ThemeScript` + `useTheme()`.
51
+ `ThemeScript` restores the persisted color scheme and product theme before
52
+ paint, so the app avoids a flash of the wrong theme on first load.
51
53
 
52
54
  ```tsx
53
55
  import { ReactNode } from 'react'
54
- import { cookies } from 'next/headers'
55
56
 
56
- import { LINK_ID } from '@dbcdk/react-components'
57
+ import { ThemeScript } from '@dbcdk/react-components'
57
58
  import '@dbcdk/react-components/styles.css'
59
+ import '@dbcdk/react-components/themes/dbc.css'
58
60
 
59
- export default async function RootLayout({ children }: Readonly<{ children: ReactNode }>) {
60
- const cookieStore = await cookies()
61
- const themeId = cookieStore.get('dbc_theme')?.value || 'light'
62
-
61
+ export default function RootLayout({ children }: Readonly<{ children: ReactNode }>) {
63
62
  return (
64
63
  <html lang="da">
65
- <head>
66
- <link id={LINK_ID} rel="stylesheet" href={`/themes/${themeId}.css`} />
67
- </head>
64
+ <head><ThemeScript defaultTheme="dbc" /></head>
68
65
  <body>{children}</body>
69
66
  </html>
70
67
  )
71
68
  }
72
69
  ```
73
70
 
74
- > Theme files are expected to be served from `/themes/<theme>.css`.
75
-
76
71
  ---
77
72
 
78
73
  ### 4) Switching theme in your application
@@ -86,19 +81,23 @@ import { AppHeader, Button, useTheme } from '@dbcdk/react-components'
86
81
  import { Moon, Sun } from 'lucide-react'
87
82
 
88
83
  export default function Header() {
89
- const { theme, switchTheme } = useTheme()
84
+ const { theme, switchTheme } = useTheme({ defaultTheme: 'dbc' })
90
85
 
91
86
  return (
92
87
  <AppHeader>
93
- <Button variant="outlined" onClick={() => switchTheme(theme === 'light' ? 'dark' : 'light')}>
94
- {theme === 'light' ? <Moon /> : <Sun />}
88
+ <Button
89
+ variant="outlined"
90
+ onClick={() => switchTheme(theme === 'dbc' ? 'filmstriben' : 'dbc')}
91
+ >
92
+ {theme === 'dbc' ? <Moon /> : <Sun />}
95
93
  </Button>
96
94
  </AppHeader>
97
95
  )
98
96
  }
99
97
  ```
100
98
 
101
- The hook updates the `<link>` tag automatically and persists the selected theme.
99
+ The hook updates `data-product-theme`, applies the selected product theme's
100
+ token overrides, and persists the selection.
102
101
 
103
102
  ---
104
103
 
@@ -167,7 +167,7 @@
167
167
  z-index: 1;
168
168
  color: var(--color-fg-default);
169
169
  background: color-mix(in srgb, var(--color-bg-hover-subtle) 55%, transparent);
170
- box-shadow: inset 0 0 0 1px var(--color-border-selected);
170
+ box-shadow: inset 0 0 0 1px var(--color-field-focus);
171
171
  outline: none;
172
172
  }
173
173
 
@@ -198,7 +198,7 @@
198
198
  }
199
199
 
200
200
  .valueWrapper:focus-within {
201
- box-shadow: inset 0 0 0 1px var(--color-border-selected);
201
+ box-shadow: inset 0 0 0 1px var(--color-field-focus);
202
202
  }
203
203
 
204
204
  .invalid .valueWrapper:focus-within {
@@ -26,15 +26,15 @@
26
26
 
27
27
  .dropzone:hover:not(.disabled) {
28
28
  background-color: var(--color-bg-selected);
29
- border-color: var(--color-border-selected);
29
+ border-color: var(--color-field-focus);
30
30
  color: var(--color-fg-default);
31
31
  }
32
32
 
33
33
  .dragging {
34
34
  background-color: var(--color-bg-selected);
35
- border-color: var(--color-border-selected);
35
+ border-color: var(--color-field-focus);
36
36
  color: var(--color-fg-default);
37
- box-shadow: inset 0 0 0 1px var(--color-border-selected);
37
+ box-shadow: inset 0 0 0 1px var(--color-field-focus);
38
38
  }
39
39
 
40
40
  .hasError {
@@ -30,7 +30,7 @@
30
30
  ========================================= */
31
31
 
32
32
  .container:focus-within {
33
- box-shadow: 0 0 0 2px var(--color-border-selected);
33
+ box-shadow: 0 0 0 2px var(--color-field-focus);
34
34
  z-index: 1;
35
35
  }
36
36
 
@@ -106,8 +106,8 @@
106
106
  }
107
107
 
108
108
  .outlined:focus-within:not([aria-disabled='true']) {
109
- border-color: var(--color-border-selected);
110
- box-shadow: inset 0 0 0 1px var(--color-border-selected);
109
+ border-color: var(--color-field-focus);
110
+ box-shadow: inset 0 0 0 1px var(--color-field-focus);
111
111
  }
112
112
 
113
113
  .surface {
@@ -122,8 +122,8 @@
122
122
  }
123
123
 
124
124
  .surface:focus-within:not([aria-disabled='true']) {
125
- border-color: var(--color-border-selected);
126
- box-shadow: inset 0 0 0 1px var(--color-border-selected);
125
+ border-color: var(--color-field-focus);
126
+ box-shadow: inset 0 0 0 1px var(--color-field-focus);
127
127
  }
128
128
 
129
129
  /* Old filled/tinted behavior, now renamed subtle */
@@ -138,8 +138,8 @@
138
138
  }
139
139
 
140
140
  .subtle:focus-within:not([aria-disabled='true']) {
141
- border-color: var(--color-border-selected);
142
- box-shadow: inset 0 0 0 1px var(--color-border-selected);
141
+ border-color: var(--color-field-focus);
142
+ box-shadow: inset 0 0 0 1px var(--color-field-focus);
143
143
  }
144
144
 
145
145
  .embedded {
@@ -176,11 +176,11 @@
176
176
  }
177
177
 
178
178
  .standalone:focus-within:not([aria-disabled='true']) {
179
- border-color: var(--color-border-selected);
179
+ border-color: var(--color-field-focus);
180
180
  box-shadow:
181
181
  var(--shadow-xs),
182
182
  var(--shadow-md),
183
- inset 0 0 0 1px var(--color-border-selected);
183
+ inset 0 0 0 1px var(--color-field-focus);
184
184
  }
185
185
 
186
186
  /* Ghost field for use inside/near AppHeader — only meaningful there */
@@ -66,5 +66,6 @@
66
66
 
67
67
  .container textarea:focus-visible {
68
68
  outline: none;
69
- border-color: var(--color-border-selected);
69
+ border-color: var(--color-field-focus);
70
+ box-shadow: inset 0 0 0 1px var(--color-field-focus);
70
71
  }
@@ -206,7 +206,7 @@
206
206
  inset-block-end: 0;
207
207
  inline-size: 0;
208
208
  block-size: 2px;
209
- background-color: var(--color-navbar-fg);
209
+ background-color: var(--color-navbar-brand);
210
210
  transition: inline-size var(--transition-fast) var(--ease-standard);
211
211
  }
212
212
 
@@ -9,11 +9,8 @@ var Popover = require('../../components/popover/Popover');
9
9
  var useTheme = require('../../hooks/useTheme');
10
10
  var types = require('../../styles/themes/types');
11
11
 
12
- function ThemeMenuSection({ themes, hrefs }) {
13
- const { theme, switchTheme } = useTheme.useTheme({
14
- defaultTheme: themes[0],
15
- hrefs
16
- });
12
+ function ThemeMenuSection({ themes }) {
13
+ const { theme, switchTheme } = useTheme.useTheme({ defaultTheme: themes[0] });
17
14
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
18
15
  /* @__PURE__ */ jsxRuntime.jsx(Menu.Menu.Header, { children: "Tema" }),
19
16
  themes.map((id) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -29,16 +26,8 @@ function ThemeMenuSection({ themes, hrefs }) {
29
26
  ))
30
27
  ] });
31
28
  }
32
- function ThemeButton({
33
- themes,
34
- hrefs,
35
- size,
36
- variant = "outlined"
37
- }) {
38
- const { theme, switchTheme } = useTheme.useTheme({
39
- defaultTheme: themes[0],
40
- hrefs
41
- });
29
+ function ThemeButton({ themes, size, variant = "outlined" }) {
30
+ const { theme, switchTheme } = useTheme.useTheme({ defaultTheme: themes[0] });
42
31
  return /* @__PURE__ */ jsxRuntime.jsx(
43
32
  Popover.Popover,
44
33
  {
@@ -1,14 +1,13 @@
1
1
  import type { JSX } from 'react';
2
2
  import type { ButtonSize, ButtonVariant } from '../../components/button/Button';
3
- import type { ThemeHrefs, ThemeId } from '../../hooks/useTheme';
3
+ import type { ThemeId } from '../../hooks/useTheme';
4
4
  export interface ThemeMenuSectionProps {
5
5
  /** Every theme to offer as an option. The first entry is treated as the default. */
6
6
  themes: readonly ThemeId[];
7
- hrefs?: ThemeHrefs;
8
7
  }
9
8
  export interface ThemeButtonProps extends ThemeMenuSectionProps {
10
9
  size?: ButtonSize;
11
10
  variant?: ButtonVariant;
12
11
  }
13
- export declare function ThemeMenuSection({ themes, hrefs }: ThemeMenuSectionProps): JSX.Element;
14
- export declare function ThemeButton({ themes, hrefs, size, variant, }: ThemeButtonProps): JSX.Element;
12
+ export declare function ThemeMenuSection({ themes }: ThemeMenuSectionProps): JSX.Element;
13
+ export declare function ThemeButton({ themes, size, variant }: ThemeButtonProps): JSX.Element;
@@ -7,11 +7,8 @@ import { Popover } from '../../components/popover/Popover';
7
7
  import { useTheme } from '../../hooks/useTheme';
8
8
  import { THEME_LABELS } from '../../styles/themes/types';
9
9
 
10
- function ThemeMenuSection({ themes, hrefs }) {
11
- const { theme, switchTheme } = useTheme({
12
- defaultTheme: themes[0],
13
- hrefs
14
- });
10
+ function ThemeMenuSection({ themes }) {
11
+ const { theme, switchTheme } = useTheme({ defaultTheme: themes[0] });
15
12
  return /* @__PURE__ */ jsxs(Fragment, { children: [
16
13
  /* @__PURE__ */ jsx(Menu.Header, { children: "Tema" }),
17
14
  themes.map((id) => /* @__PURE__ */ jsx(
@@ -27,16 +24,8 @@ function ThemeMenuSection({ themes, hrefs }) {
27
24
  ))
28
25
  ] });
29
26
  }
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
- });
27
+ function ThemeButton({ themes, size, variant = "outlined" }) {
28
+ const { theme, switchTheme } = useTheme({ defaultTheme: themes[0] });
40
29
  return /* @__PURE__ */ jsx(
41
30
  Popover,
42
31
  {
@@ -1,30 +1,39 @@
1
1
  'use strict';
2
2
 
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
+ var productThemes = require('../../styles/themes/product-themes');
4
5
  var colorScheme_constants = require('../../utils/color-scheme.constants');
5
6
  var theme_constants = require('../../utils/theme.constants');
6
7
 
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
- }
8
+ function buildInitScript(defaultColorScheme, defaultTheme, storageKey) {
9
+ const productThemes$1 = JSON.stringify(productThemes.PRODUCT_THEMES);
12
10
  const themeBlock = defaultTheme === void 0 ? "" : `try{
13
11
  var THEME_KEY=${JSON.stringify(storageKey)};
14
12
  var THEME_DEFAULT=${JSON.stringify(defaultTheme)};
15
- var HREFS=${JSON.stringify(hrefById)};
16
- var LINK_ID=${JSON.stringify(linkId)};
13
+ var PRODUCT_THEMES=${productThemes$1};
17
14
  var resolvedTheme=getCookie(THEME_KEY);
18
15
  if(!resolvedTheme){try{resolvedTheme=localStorage.getItem(THEME_KEY)}catch(e){}}
19
16
  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);
17
+ function applyProductTheme(themeId, scheme){
18
+ var target=document.documentElement;
19
+ var definition=PRODUCT_THEMES[themeId]||{};
20
+ var tokens={};
21
+ var source=scheme==='dark'?definition.dark:definition.light;
22
+ if(definition.base){for(var baseKey in definition.base){tokens[baseKey]=definition.base[baseKey];}}
23
+ if(source){for(var sourceKey in source){tokens[sourceKey]=source[sourceKey];}}
24
+ var knownThemeIds=Object.keys(PRODUCT_THEMES);
25
+ for(var i=0;i<knownThemeIds.length;i++){
26
+ var knownDefinition=PRODUCT_THEMES[knownThemeIds[i]]||{};
27
+ for(var sectionIndex=0;sectionIndex<3;sectionIndex++){
28
+ var section=sectionIndex===0?knownDefinition.base:sectionIndex===1?knownDefinition.light:knownDefinition.dark;
29
+ if(!section)continue;
30
+ for(var clearKey in section){target.style.removeProperty(clearKey);}
31
+ }
32
+ }
33
+ for(var token in tokens){target.style.setProperty(token,tokens[token]);}
26
34
  }
27
35
  document.documentElement.dataset.productTheme=resolvedTheme;
36
+ applyProductTheme(resolvedTheme, document.documentElement.dataset.theme);
28
37
  }catch(e){}`;
29
38
  return `(function(){
30
39
  function getCookie(name){var m=document.cookie.match(new RegExp('(?:^|; )'+name+'=([^;]*)'));return m?decodeURIComponent(m[1]):null}
@@ -43,15 +52,13 @@ function buildInitScript(defaultColorScheme, hrefs, defaultTheme, linkId, storag
43
52
  function ThemeScript({
44
53
  defaultColorScheme = "system",
45
54
  defaultTheme,
46
- hrefs = {},
47
- linkId = theme_constants.THEME_LINK_ID,
48
55
  storageKey = theme_constants.THEME_STORAGE_KEY
49
56
  }) {
50
57
  return /* @__PURE__ */ jsxRuntime.jsx(
51
58
  "script",
52
59
  {
53
60
  dangerouslySetInnerHTML: {
54
- __html: buildInitScript(defaultColorScheme, hrefs, defaultTheme, linkId, storageKey)
61
+ __html: buildInitScript(defaultColorScheme, defaultTheme, storageKey)
55
62
  }
56
63
  }
57
64
  );
@@ -1,4 +1,4 @@
1
- import type { ThemeHrefs, ThemeId } from '../../styles/themes/types';
1
+ import type { ThemeId } from '../../styles/themes/types';
2
2
  import type { ColorScheme } from '../../utils/color-scheme.constants';
3
3
  export interface ThemeScriptProps {
4
4
  /** Color scheme applied when nothing is stored yet. Defaults to 'system'. */
@@ -9,10 +9,6 @@ export interface ThemeScriptProps {
9
9
  * `data-product-theme` handling happens at all in that case.
10
10
  */
11
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
12
  storageKey?: string;
17
13
  }
18
14
  /**
@@ -45,4 +41,4 @@ export interface ThemeScriptProps {
45
41
  * </Head>
46
42
  * ```
47
43
  */
48
- export declare function ThemeScript({ defaultColorScheme, defaultTheme, hrefs, linkId, storageKey, }: ThemeScriptProps): import("react/jsx-runtime").JSX.Element;
44
+ export declare function ThemeScript({ defaultColorScheme, defaultTheme, storageKey, }: ThemeScriptProps): import("react/jsx-runtime").JSX.Element;
@@ -1,28 +1,37 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
+ import { PRODUCT_THEMES } from '../../styles/themes/product-themes';
2
3
  import { COLOR_SCHEME_STORAGE_KEY, COLOR_SCHEMES } from '../../utils/color-scheme.constants';
3
- import { THEME_LINK_ID, THEME_STORAGE_KEY } from '../../utils/theme.constants';
4
+ import { THEME_STORAGE_KEY } from '../../utils/theme.constants';
4
5
 
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
- }
6
+ function buildInitScript(defaultColorScheme, defaultTheme, storageKey) {
7
+ const productThemes = JSON.stringify(PRODUCT_THEMES);
10
8
  const themeBlock = defaultTheme === void 0 ? "" : `try{
11
9
  var THEME_KEY=${JSON.stringify(storageKey)};
12
10
  var THEME_DEFAULT=${JSON.stringify(defaultTheme)};
13
- var HREFS=${JSON.stringify(hrefById)};
14
- var LINK_ID=${JSON.stringify(linkId)};
11
+ var PRODUCT_THEMES=${productThemes};
15
12
  var resolvedTheme=getCookie(THEME_KEY);
16
13
  if(!resolvedTheme){try{resolvedTheme=localStorage.getItem(THEME_KEY)}catch(e){}}
17
14
  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);
15
+ function applyProductTheme(themeId, scheme){
16
+ var target=document.documentElement;
17
+ var definition=PRODUCT_THEMES[themeId]||{};
18
+ var tokens={};
19
+ var source=scheme==='dark'?definition.dark:definition.light;
20
+ if(definition.base){for(var baseKey in definition.base){tokens[baseKey]=definition.base[baseKey];}}
21
+ if(source){for(var sourceKey in source){tokens[sourceKey]=source[sourceKey];}}
22
+ var knownThemeIds=Object.keys(PRODUCT_THEMES);
23
+ for(var i=0;i<knownThemeIds.length;i++){
24
+ var knownDefinition=PRODUCT_THEMES[knownThemeIds[i]]||{};
25
+ for(var sectionIndex=0;sectionIndex<3;sectionIndex++){
26
+ var section=sectionIndex===0?knownDefinition.base:sectionIndex===1?knownDefinition.light:knownDefinition.dark;
27
+ if(!section)continue;
28
+ for(var clearKey in section){target.style.removeProperty(clearKey);}
29
+ }
30
+ }
31
+ for(var token in tokens){target.style.setProperty(token,tokens[token]);}
24
32
  }
25
33
  document.documentElement.dataset.productTheme=resolvedTheme;
34
+ applyProductTheme(resolvedTheme, document.documentElement.dataset.theme);
26
35
  }catch(e){}`;
27
36
  return `(function(){
28
37
  function getCookie(name){var m=document.cookie.match(new RegExp('(?:^|; )'+name+'=([^;]*)'));return m?decodeURIComponent(m[1]):null}
@@ -41,15 +50,13 @@ function buildInitScript(defaultColorScheme, hrefs, defaultTheme, linkId, storag
41
50
  function ThemeScript({
42
51
  defaultColorScheme = "system",
43
52
  defaultTheme,
44
- hrefs = {},
45
- linkId = THEME_LINK_ID,
46
53
  storageKey = THEME_STORAGE_KEY
47
54
  }) {
48
55
  return /* @__PURE__ */ jsx(
49
56
  "script",
50
57
  {
51
58
  dangerouslySetInnerHTML: {
52
- __html: buildInitScript(defaultColorScheme, hrefs, defaultTheme, linkId, storageKey)
59
+ __html: buildInitScript(defaultColorScheme, defaultTheme, storageKey)
53
60
  }
54
61
  }
55
62
  );
@@ -4,13 +4,18 @@
4
4
  var react = require('react');
5
5
  var cookie_utils = require('../utils/cookie.utils');
6
6
  var localStorage_utils = require('../utils/localStorage.utils');
7
+ var sessionStorage_utils = require('../utils/sessionStorage.utils');
7
8
 
8
9
  function readStorage(storage, key) {
9
- return storage === "cookie" ? cookie_utils.readCookie(key) : localStorage_utils.readLocalStorage(key);
10
+ if (storage === "cookie") return cookie_utils.readCookie(key);
11
+ if (storage === "sessionStorage") return sessionStorage_utils.readSessionStorage(key);
12
+ return localStorage_utils.readLocalStorage(key);
10
13
  }
11
14
  function writeStorage(storage, key, value, maxAgeSeconds) {
12
15
  if (storage === "cookie") {
13
16
  cookie_utils.writeCookie(key, value, maxAgeSeconds);
17
+ } else if (storage === "sessionStorage") {
18
+ sessionStorage_utils.writeSessionStorage(key, value);
14
19
  } else {
15
20
  localStorage_utils.writeLocalStorage(key, value);
16
21
  }
@@ -1,5 +1,5 @@
1
1
  import { Dispatch, SetStateAction } from 'react';
2
- export type PersistentStateStorage = 'localStorage' | 'cookie';
2
+ export type PersistentStateStorage = 'localStorage' | 'sessionStorage' | 'cookie';
3
3
  /**
4
4
  * 'effect' (default): the stored value is read in a useEffect after mount,
5
5
  * so `value` starts as `initialValue` on every render — including the
@@ -2,13 +2,18 @@
2
2
  import { useRef, useState, useEffect } from 'react';
3
3
  import { readCookie, writeCookie } from '../utils/cookie.utils';
4
4
  import { readLocalStorage, writeLocalStorage } from '../utils/localStorage.utils';
5
+ import { readSessionStorage, writeSessionStorage } from '../utils/sessionStorage.utils';
5
6
 
6
7
  function readStorage(storage, key) {
7
- return storage === "cookie" ? readCookie(key) : readLocalStorage(key);
8
+ if (storage === "cookie") return readCookie(key);
9
+ if (storage === "sessionStorage") return readSessionStorage(key);
10
+ return readLocalStorage(key);
8
11
  }
9
12
  function writeStorage(storage, key, value, maxAgeSeconds) {
10
13
  if (storage === "cookie") {
11
14
  writeCookie(key, value, maxAgeSeconds);
15
+ } else if (storage === "sessionStorage") {
16
+ writeSessionStorage(key, value);
12
17
  } else {
13
18
  writeLocalStorage(key, value);
14
19
  }
@@ -2,11 +2,11 @@
2
2
  'use strict';
3
3
 
4
4
  var react = require('react');
5
+ var applyProductTheme = require('../styles/themes/apply-product-theme');
5
6
  var cookie_utils = require('../utils/cookie.utils');
6
7
  var localStorage_utils = require('../utils/localStorage.utils');
7
8
  var theme_constants = require('../utils/theme.constants');
8
9
 
9
- const PRELOAD_ID_PREFIX = "dbc-theme-preload-";
10
10
  function persistTheme(storageKey, id) {
11
11
  localStorage_utils.writeLocalStorage(storageKey, id);
12
12
  cookie_utils.writeCookie(storageKey, id);
@@ -14,47 +14,16 @@ function persistTheme(storageKey, id) {
14
14
  function getDomTheme() {
15
15
  return document.documentElement.dataset.productTheme;
16
16
  }
17
+ function getDomColorScheme() {
18
+ return document.documentElement.dataset.theme;
19
+ }
17
20
  function applyTheme(id) {
18
21
  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);
22
+ applyProductTheme.applyProductTheme(id, getDomColorScheme());
54
23
  }
55
24
  let currentTheme = null;
56
25
  let resolved = false;
57
- let preloadScheduled = false;
26
+ let observerAttached = false;
58
27
  const listeners = /* @__PURE__ */ new Set();
59
28
  function notify() {
60
29
  listeners.forEach((listener) => listener());
@@ -80,75 +49,43 @@ function resolveInitialTheme(storageKey, defaultTheme) {
80
49
  if (fromStorage) return fromStorage;
81
50
  return defaultTheme;
82
51
  }
83
- function ensureResolved(hrefs, defaultTheme, storageKey, linkId) {
52
+ function ensureResolved(defaultTheme, storageKey) {
84
53
  if (resolved) return;
85
54
  resolved = true;
86
55
  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);
56
+ applyTheme(value);
57
+ persistTheme(storageKey, value);
58
+ currentTheme = value;
59
+ notify();
98
60
  }
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
- }
61
+ function ensureColorSchemeObserver() {
62
+ if (observerAttached) return;
63
+ observerAttached = true;
64
+ const observer = new MutationObserver(() => {
65
+ if (!currentTheme) return;
66
+ applyProductTheme.applyProductTheme(currentTheme, getDomColorScheme());
67
+ });
68
+ observer.observe(document.documentElement, {
69
+ attributes: true,
70
+ attributeFilter: ["data-theme"]
71
+ });
122
72
  }
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);
73
+ function switchThemeInternal(storageKey, id) {
74
+ applyTheme(id);
75
+ persistTheme(storageKey, id);
76
+ currentTheme = id;
77
+ resolved = true;
78
+ notify();
136
79
  }
137
80
  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;
81
+ const { defaultTheme, storageKey = theme_constants.THEME_STORAGE_KEY } = options;
145
82
  react.useEffect(() => {
146
- ensureResolved(hrefs, defaultTheme, storageKey, linkId);
147
- if (preloadOthers) schedulePreload(hrefs, defaultTheme);
148
- }, []);
83
+ ensureColorSchemeObserver();
84
+ ensureResolved(defaultTheme, storageKey);
85
+ }, [defaultTheme, storageKey]);
149
86
  const theme = react.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
150
87
  function switchTheme(id) {
151
- switchThemeInternal(hrefs, defaultTheme, storageKey, linkId, id);
88
+ switchThemeInternal(storageKey, id);
152
89
  }
153
90
  return { theme, switchTheme };
154
91
  }