@dbcdk/react-components 0.0.171 → 0.0.172

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.
@@ -42,10 +42,6 @@
42
42
  min-width: 0; /* required for truncation inside flex */
43
43
  }
44
44
 
45
- h2.headline {
46
- min-block-size: var(--component-size-md);
47
- }
48
-
49
45
  /* CollapsibleHeadline wrapper */
50
46
  .collapsibleRoot {
51
47
  display: flex;
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var theme_constants = require('../../utils/theme.constants');
5
+
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}
11
+ 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){}})();`;
17
+ }
18
+ function ThemeScript({ defaultTheme = "system" }) {
19
+ return /* @__PURE__ */ jsxRuntime.jsx("script", { dangerouslySetInnerHTML: { __html: buildThemeInitScript(defaultTheme) } });
20
+ }
21
+
22
+ exports.ThemeScript = ThemeScript;
@@ -0,0 +1,25 @@
1
+ import type { ThemeVariant } from '../../utils/theme.constants';
2
+ export interface ThemeScriptProps {
3
+ /** Theme applied when nothing is stored yet. Defaults to 'system'. */
4
+ defaultTheme?: ThemeVariant;
5
+ }
6
+ /**
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.
10
+ *
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.
16
+ *
17
+ * Place it as early as possible in <head>, e.g. in Next.js's `_document.js`:
18
+ *
19
+ * ```tsx
20
+ * <Head>
21
+ * <ThemeScript />
22
+ * </Head>
23
+ * ```
24
+ */
25
+ export declare function ThemeScript({ defaultTheme }: ThemeScriptProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,20 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { THEME_STORAGE_KEY, THEME_VARIANTS } from '../../utils/theme.constants';
3
+
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}
9
+ 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){}})();`;
15
+ }
16
+ function ThemeScript({ defaultTheme = "system" }) {
17
+ return /* @__PURE__ */ jsx("script", { dangerouslySetInnerHTML: { __html: buildThemeInitScript(defaultTheme) } });
18
+ }
19
+
20
+ export { ThemeScript };
@@ -2,62 +2,88 @@
2
2
  'use strict';
3
3
 
4
4
  var react = require('react');
5
+ var theme_constants = require('../utils/theme.constants');
5
6
 
6
- const THEME_VARIANTS = ["light", "dark", "system"];
7
- const STORAGE_KEY = "dbc_theme";
8
- function isThemeVariant(x) {
9
- return !!x && THEME_VARIANTS.includes(x);
10
- }
11
7
  function getCookie(name) {
12
8
  const match = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`));
13
9
  return match ? decodeURIComponent(match[1]) : null;
14
10
  }
15
11
  function persistTheme(id) {
16
12
  try {
17
- localStorage.setItem(STORAGE_KEY, id);
13
+ localStorage.setItem(theme_constants.THEME_STORAGE_KEY, id);
18
14
  } catch {
19
15
  console.error("Failed to access localStorage");
20
16
  }
21
17
  try {
22
- document.cookie = `${STORAGE_KEY}=${encodeURIComponent(
18
+ document.cookie = `${theme_constants.THEME_STORAGE_KEY}=${encodeURIComponent(
23
19
  id
24
20
  )}; Path=/; Max-Age=${60 * 60 * 24 * 365}`;
25
21
  } catch {
26
22
  console.error("Failed to set theme cookie");
27
23
  }
28
24
  }
29
- function getTheme() {
25
+ function getDomTheme() {
30
26
  return document.documentElement.dataset.theme;
31
27
  }
32
28
  function applyTheme(id) {
33
29
  document.documentElement.dataset.theme = id;
34
30
  }
31
+ let currentTheme = null;
32
+ let resolved = false;
33
+ const listeners = /* @__PURE__ */ new Set();
34
+ function notify() {
35
+ listeners.forEach((listener) => listener());
36
+ }
37
+ function subscribe(listener) {
38
+ listeners.add(listener);
39
+ return () => {
40
+ listeners.delete(listener);
41
+ };
42
+ }
43
+ function getSnapshot() {
44
+ return currentTheme;
45
+ }
46
+ function getServerSnapshot() {
47
+ return null;
48
+ }
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;
64
+ }
65
+ function ensureResolved(initialTheme) {
66
+ if (resolved) return;
67
+ resolved = true;
68
+ const value = resolveInitialTheme(initialTheme);
69
+ applyTheme(value);
70
+ persistTheme(value);
71
+ currentTheme = value;
72
+ notify();
73
+ }
74
+ function switchTheme(id) {
75
+ applyTheme(id);
76
+ persistTheme(id);
77
+ currentTheme = id;
78
+ resolved = true;
79
+ notify();
80
+ return id;
81
+ }
35
82
  function useTheme(initialTheme = "system") {
36
- const [theme, setTheme] = react.useState(null);
37
83
  react.useEffect(() => {
38
- const themeFromDataAttributes = getTheme();
39
- let resolved = isThemeVariant(themeFromDataAttributes) ? themeFromDataAttributes : initialTheme;
40
- const fromCookie = getCookie(STORAGE_KEY);
41
- if (isThemeVariant(fromCookie)) {
42
- resolved = fromCookie;
43
- } else {
44
- try {
45
- const fromStorage = localStorage.getItem(STORAGE_KEY);
46
- if (isThemeVariant(fromStorage)) resolved = fromStorage;
47
- } catch {
48
- console.error("Failed to access localStorage");
49
- }
50
- }
51
- applyTheme(resolved);
52
- setTheme(resolved);
53
- persistTheme(resolved);
84
+ ensureResolved(initialTheme);
54
85
  }, [initialTheme]);
55
- const switchTheme = react.useCallback((id) => {
56
- applyTheme(id);
57
- setTheme(id);
58
- persistTheme(id);
59
- return id;
60
- }, []);
86
+ const theme = react.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
61
87
  return { theme, switchTheme };
62
88
  }
63
89
 
@@ -1,7 +1,6 @@
1
- declare const THEME_VARIANTS: readonly ["light", "dark", "system"];
2
- export type ThemeVariant = (typeof THEME_VARIANTS)[number];
1
+ import type { ThemeVariant } from '../utils/theme.constants';
2
+ export type { ThemeVariant };
3
3
  export declare function useTheme(initialTheme?: ThemeVariant): {
4
4
  theme: ThemeVariant | null;
5
5
  switchTheme: (id: ThemeVariant) => ThemeVariant;
6
6
  };
7
- export {};
@@ -1,61 +1,87 @@
1
1
  'use client';
2
- import { useState, useEffect, useCallback } from 'react';
2
+ import { useEffect, useSyncExternalStore } from 'react';
3
+ import { isThemeVariant, THEME_STORAGE_KEY } from '../utils/theme.constants';
3
4
 
4
- const THEME_VARIANTS = ["light", "dark", "system"];
5
- const STORAGE_KEY = "dbc_theme";
6
- function isThemeVariant(x) {
7
- return !!x && THEME_VARIANTS.includes(x);
8
- }
9
5
  function getCookie(name) {
10
6
  const match = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`));
11
7
  return match ? decodeURIComponent(match[1]) : null;
12
8
  }
13
9
  function persistTheme(id) {
14
10
  try {
15
- localStorage.setItem(STORAGE_KEY, id);
11
+ localStorage.setItem(THEME_STORAGE_KEY, id);
16
12
  } catch {
17
13
  console.error("Failed to access localStorage");
18
14
  }
19
15
  try {
20
- document.cookie = `${STORAGE_KEY}=${encodeURIComponent(
16
+ document.cookie = `${THEME_STORAGE_KEY}=${encodeURIComponent(
21
17
  id
22
18
  )}; Path=/; Max-Age=${60 * 60 * 24 * 365}`;
23
19
  } catch {
24
20
  console.error("Failed to set theme cookie");
25
21
  }
26
22
  }
27
- function getTheme() {
23
+ function getDomTheme() {
28
24
  return document.documentElement.dataset.theme;
29
25
  }
30
26
  function applyTheme(id) {
31
27
  document.documentElement.dataset.theme = id;
32
28
  }
29
+ let currentTheme = null;
30
+ let resolved = false;
31
+ const listeners = /* @__PURE__ */ new Set();
32
+ function notify() {
33
+ listeners.forEach((listener) => listener());
34
+ }
35
+ function subscribe(listener) {
36
+ listeners.add(listener);
37
+ return () => {
38
+ listeners.delete(listener);
39
+ };
40
+ }
41
+ function getSnapshot() {
42
+ return currentTheme;
43
+ }
44
+ function getServerSnapshot() {
45
+ return null;
46
+ }
47
+ function resolveInitialTheme(initialTheme) {
48
+ const themeFromDataAttributes = getDomTheme();
49
+ let resolvedTheme = isThemeVariant(themeFromDataAttributes) ? themeFromDataAttributes : initialTheme;
50
+ const fromCookie = getCookie(THEME_STORAGE_KEY);
51
+ if (isThemeVariant(fromCookie)) {
52
+ resolvedTheme = fromCookie;
53
+ } else {
54
+ try {
55
+ const fromStorage = localStorage.getItem(THEME_STORAGE_KEY);
56
+ if (isThemeVariant(fromStorage)) resolvedTheme = fromStorage;
57
+ } catch {
58
+ console.error("Failed to access localStorage");
59
+ }
60
+ }
61
+ return resolvedTheme;
62
+ }
63
+ function ensureResolved(initialTheme) {
64
+ if (resolved) return;
65
+ resolved = true;
66
+ const value = resolveInitialTheme(initialTheme);
67
+ applyTheme(value);
68
+ persistTheme(value);
69
+ currentTheme = value;
70
+ notify();
71
+ }
72
+ function switchTheme(id) {
73
+ applyTheme(id);
74
+ persistTheme(id);
75
+ currentTheme = id;
76
+ resolved = true;
77
+ notify();
78
+ return id;
79
+ }
33
80
  function useTheme(initialTheme = "system") {
34
- const [theme, setTheme] = useState(null);
35
81
  useEffect(() => {
36
- const themeFromDataAttributes = getTheme();
37
- let resolved = isThemeVariant(themeFromDataAttributes) ? themeFromDataAttributes : initialTheme;
38
- const fromCookie = getCookie(STORAGE_KEY);
39
- if (isThemeVariant(fromCookie)) {
40
- resolved = fromCookie;
41
- } else {
42
- try {
43
- const fromStorage = localStorage.getItem(STORAGE_KEY);
44
- if (isThemeVariant(fromStorage)) resolved = fromStorage;
45
- } catch {
46
- console.error("Failed to access localStorage");
47
- }
48
- }
49
- applyTheme(resolved);
50
- setTheme(resolved);
51
- persistTheme(resolved);
82
+ ensureResolved(initialTheme);
52
83
  }, [initialTheme]);
53
- const switchTheme = useCallback((id) => {
54
- applyTheme(id);
55
- setTheme(id);
56
- persistTheme(id);
57
- return id;
58
- }, []);
84
+ const theme = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
59
85
  return { theme, switchTheme };
60
86
  }
61
87
 
package/dist/index.cjs CHANGED
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ var ThemeScript = require('./components/theme-script/ThemeScript');
4
+ var theme_constants = require('./utils/theme.constants');
3
5
  var Icon = require('./components/icon/Icon');
4
6
  var UserDisplay = require('./components/user-display/UserDisplay');
5
7
  var Headline = require('./components/headline/Headline');
@@ -44,6 +46,18 @@ var CheckboxGroup = require('./components/forms/checkbox-group/CheckboxGroup');
44
46
 
45
47
 
46
48
 
49
+ Object.keys(ThemeScript).forEach(function (k) {
50
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
51
+ enumerable: true,
52
+ get: function () { return ThemeScript[k]; }
53
+ });
54
+ });
55
+ Object.keys(theme_constants).forEach(function (k) {
56
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
57
+ enumerable: true,
58
+ get: function () { return theme_constants[k]; }
59
+ });
60
+ });
47
61
  Object.keys(Icon).forEach(function (k) {
48
62
  if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
49
63
  enumerable: true,
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ export * from './components/theme-script/ThemeScript';
2
+ export * from './utils/theme.constants';
1
3
  export * from './components/icon/Icon';
2
4
  export * from './components/user-display/UserDisplay';
3
5
  export * from './components/headline/Headline';
package/dist/index.js CHANGED
@@ -1,3 +1,5 @@
1
+ export * from './components/theme-script/ThemeScript';
2
+ export * from './utils/theme.constants';
1
3
  export * from './components/icon/Icon';
2
4
  export * from './components/user-display/UserDisplay';
3
5
  export * from './components/headline/Headline';
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ const THEME_VARIANTS = ["light", "dark", "system"];
4
+ const THEME_STORAGE_KEY = "dbc_theme";
5
+ function isThemeVariant(x) {
6
+ return !!x && THEME_VARIANTS.includes(x);
7
+ }
8
+
9
+ exports.THEME_STORAGE_KEY = THEME_STORAGE_KEY;
10
+ exports.THEME_VARIANTS = THEME_VARIANTS;
11
+ exports.isThemeVariant = isThemeVariant;
@@ -0,0 +1,4 @@
1
+ export declare const THEME_VARIANTS: readonly ["light", "dark", "system"];
2
+ export type ThemeVariant = (typeof THEME_VARIANTS)[number];
3
+ export declare const THEME_STORAGE_KEY = "dbc_theme";
4
+ export declare function isThemeVariant(x: string | null | undefined): x is ThemeVariant;
@@ -0,0 +1,7 @@
1
+ const THEME_VARIANTS = ["light", "dark", "system"];
2
+ const THEME_STORAGE_KEY = "dbc_theme";
3
+ function isThemeVariant(x) {
4
+ return !!x && THEME_VARIANTS.includes(x);
5
+ }
6
+
7
+ export { THEME_STORAGE_KEY, THEME_VARIANTS, isThemeVariant };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dbcdk/react-components",
3
- "version": "0.0.171",
3
+ "version": "0.0.172",
4
4
  "description": "Reusable React components for DBC projects",
5
5
  "license": "ISC",
6
6
  "author": "",