@dbcdk/react-components 0.0.177 → 0.0.179

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -72,7 +72,7 @@ export default function RootLayout({ children }: Readonly<{ children: ReactNode
72
72
 
73
73
  ### 4) Switching theme in your application
74
74
 
75
- Example using `useTheme()`:
75
+ Example using `useTheme()` when you want to control the theme:
76
76
 
77
77
  ```tsx
78
78
  'use client'
@@ -99,6 +99,25 @@ export default function Header() {
99
99
  The hook updates `data-product-theme`, applies the selected product theme's
100
100
  token overrides, and persists the selection.
101
101
 
102
+ If you only need to read the active theme state, use `useGetActiveTheme()`
103
+ instead of `useTheme()`:
104
+
105
+ ```tsx
106
+ 'use client'
107
+
108
+ import { useGetActiveTheme } from '@dbcdk/react-components/client'
109
+
110
+ export function ThemeBadge() {
111
+ const { theme, colorScheme } = useGetActiveTheme()
112
+
113
+ return <span>{theme} / {colorScheme}</span>
114
+ }
115
+ ```
116
+
117
+ `useGetActiveTheme()` is intentionally read-only and takes no options. It
118
+ reports the current active theme state as established by `ThemeScript`,
119
+ persisted storage, or another mounted theme control.
120
+
102
121
  ---
103
122
 
104
123
  ## Using Storybook
package/dist/client.cjs CHANGED
@@ -19,6 +19,7 @@ var Footer = require('./components/page-layout/components/footer/Footer');
19
19
  var Input = require('./components/forms/input/Input');
20
20
  var SearchBox = require('./components/search-box/SearchBox');
21
21
  var useColorScheme = require('./hooks/useColorScheme');
22
+ var useGetActiveTheme = require('./hooks/useGetActiveTheme');
22
23
  var useTheme = require('./hooks/useTheme');
23
24
  var usePersistentState = require('./hooks/usePersistentState');
24
25
  var Chip = require('./components/chip/Chip');
@@ -203,6 +204,12 @@ Object.keys(useColorScheme).forEach(function (k) {
203
204
  get: function () { return useColorScheme[k]; }
204
205
  });
205
206
  });
207
+ Object.keys(useGetActiveTheme).forEach(function (k) {
208
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
209
+ enumerable: true,
210
+ get: function () { return useGetActiveTheme[k]; }
211
+ });
212
+ });
206
213
  Object.keys(useTheme).forEach(function (k) {
207
214
  if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
208
215
  enumerable: true,
package/dist/client.d.ts CHANGED
@@ -16,6 +16,7 @@ export * from './components/page-layout/components/footer/Footer';
16
16
  export * from './components/forms/input/Input';
17
17
  export * from './components/search-box/SearchBox';
18
18
  export * from './hooks/useColorScheme';
19
+ export * from './hooks/useGetActiveTheme';
19
20
  export * from './hooks/useTheme';
20
21
  export * from './hooks/usePersistentState';
21
22
  export * from './components/chip/Chip';
package/dist/client.js CHANGED
@@ -17,6 +17,7 @@ export * from './components/page-layout/components/footer/Footer';
17
17
  export * from './components/forms/input/Input';
18
18
  export * from './components/search-box/SearchBox';
19
19
  export * from './hooks/useColorScheme';
20
+ export * from './hooks/useGetActiveTheme';
20
21
  export * from './hooks/useTheme';
21
22
  export * from './hooks/usePersistentState';
22
23
  export * from './components/chip/Chip';
@@ -5,6 +5,7 @@ var jsxRuntime = require('react/jsx-runtime');
5
5
  var lucideReact = require('lucide-react');
6
6
  var react = require('react');
7
7
  var styles = require('./CopyButton.module.css');
8
+ var clipboard_utils = require('../../utils/clipboard.utils');
8
9
  var Button = require('../button/Button');
9
10
  var Input = require('../forms/input/Input');
10
11
  var Hyperlink = require('../hyperlink/Hyperlink');
@@ -48,30 +49,9 @@ function CopyButton(props) {
48
49
  }
49
50
  };
50
51
  const handleCopy = async () => {
51
- try {
52
- if (!window.isSecureContext || !navigator.clipboard) {
53
- throw new Error("Clipboard API unavailable");
54
- }
55
- await navigator.clipboard.writeText(text);
52
+ const success = await clipboard_utils.copyToClipboard(text);
53
+ if (success) {
56
54
  handleCopySuccess();
57
- } catch (err) {
58
- console.error("Failed to copy:", err);
59
- try {
60
- const textarea = document.createElement("textarea");
61
- textarea.value = text;
62
- textarea.setAttribute("readonly", "");
63
- textarea.style.position = "fixed";
64
- textarea.style.left = "-9999px";
65
- document.body.appendChild(textarea);
66
- textarea.select();
67
- const success = document.execCommand("copy");
68
- document.body.removeChild(textarea);
69
- if (success) {
70
- handleCopySuccess();
71
- }
72
- } catch (fallbackErr) {
73
- console.error("Fallback copy failed:", fallbackErr);
74
- }
75
55
  }
76
56
  };
77
57
  if (displayStyle === "link") {
@@ -3,6 +3,7 @@ import { jsx, jsxs } from 'react/jsx-runtime';
3
3
  import { Check, Copy } from 'lucide-react';
4
4
  import { useState } from 'react';
5
5
  import styles from './CopyButton.module.css';
6
+ import { copyToClipboard } from '../../utils/clipboard.utils';
6
7
  import { Button } from '../button/Button';
7
8
  import { Input } from '../forms/input/Input';
8
9
  import { Hyperlink } from '../hyperlink/Hyperlink';
@@ -42,30 +43,9 @@ function CopyButton(props) {
42
43
  }
43
44
  };
44
45
  const handleCopy = async () => {
45
- try {
46
- if (!window.isSecureContext || !navigator.clipboard) {
47
- throw new Error("Clipboard API unavailable");
48
- }
49
- await navigator.clipboard.writeText(text);
46
+ const success = await copyToClipboard(text);
47
+ if (success) {
50
48
  handleCopySuccess();
51
- } catch (err) {
52
- console.error("Failed to copy:", err);
53
- try {
54
- const textarea = document.createElement("textarea");
55
- textarea.value = text;
56
- textarea.setAttribute("readonly", "");
57
- textarea.style.position = "fixed";
58
- textarea.style.left = "-9999px";
59
- document.body.appendChild(textarea);
60
- textarea.select();
61
- const success = document.execCommand("copy");
62
- document.body.removeChild(textarea);
63
- if (success) {
64
- handleCopySuccess();
65
- }
66
- } catch (fallbackErr) {
67
- console.error("Fallback copy failed:", fallbackErr);
68
- }
69
49
  }
70
50
  };
71
51
  if (displayStyle === "link") {
@@ -22,10 +22,15 @@ function Headline({
22
22
  tone,
23
23
  variant,
24
24
  allowWrap = true,
25
+ grow = true,
25
26
  id
26
27
  }) {
27
28
  const Tag = `h${size}`;
28
- const containerClassName = [styles__default.default.headlineContainer, tone ? styles__default.default[`tone-${tone}`] : ""].filter(Boolean).join(" ");
29
+ const containerClassName = [
30
+ styles__default.default.headlineContainer,
31
+ tone ? styles__default.default[`tone-${tone}`] : "",
32
+ grow ? "" : styles__default.default.noGrow
33
+ ].filter(Boolean).join(" ");
29
34
  const headlineClassName = [
30
35
  styles__default.default.headline,
31
36
  disableMargin ? styles__default.default.noMargin : "",
@@ -15,6 +15,7 @@ export interface HeadlineProps extends React.AriaAttributes {
15
15
  allowWrap?: boolean;
16
16
  tone?: HeadlineTone;
17
17
  variant?: HeadlineVariant;
18
+ grow?: boolean;
18
19
  }
19
- export declare function Headline({ size, marker, disableMargin, children, severity, weight, subheader, addition, icon, tone, variant, allowWrap, id, }: PropsWithChildren<HeadlineProps>): React.JSX.Element;
20
+ export declare function Headline({ size, marker, disableMargin, children, severity, weight, subheader, addition, icon, tone, variant, allowWrap, grow, id, }: PropsWithChildren<HeadlineProps>): React.JSX.Element;
20
21
  export {};
@@ -16,10 +16,15 @@ function Headline({
16
16
  tone,
17
17
  variant,
18
18
  allowWrap = true,
19
+ grow = true,
19
20
  id
20
21
  }) {
21
22
  const Tag = `h${size}`;
22
- const containerClassName = [styles.headlineContainer, tone ? styles[`tone-${tone}`] : ""].filter(Boolean).join(" ");
23
+ const containerClassName = [
24
+ styles.headlineContainer,
25
+ tone ? styles[`tone-${tone}`] : "",
26
+ grow ? "" : styles.noGrow
27
+ ].filter(Boolean).join(" ");
23
28
  const headlineClassName = [
24
29
  styles.headline,
25
30
  disableMargin ? styles.noMargin : "",
@@ -7,6 +7,10 @@
7
7
  min-width: 0;
8
8
  }
9
9
 
10
+ .noGrow {
11
+ flex: 0 1 auto;
12
+ }
13
+
10
14
  .headlineBlock {
11
15
  display: flex;
12
16
  flex-direction: column;
@@ -0,0 +1,13 @@
1
+ 'use client';
2
+ 'use strict';
3
+
4
+ var useColorScheme = require('../hooks/useColorScheme');
5
+ var useTheme = require('./useTheme');
6
+
7
+ function useGetActiveTheme() {
8
+ const { theme } = useTheme.useTheme();
9
+ const { colorScheme } = useColorScheme.useColorScheme();
10
+ return { theme, colorScheme };
11
+ }
12
+
13
+ exports.useGetActiveTheme = useGetActiveTheme;
@@ -0,0 +1,8 @@
1
+ import type { ColorScheme } from '../utils/color-scheme.constants';
2
+ import type { ThemeId } from './useTheme';
3
+ export type { ColorScheme, ThemeId };
4
+ export interface UseGetActiveThemeResult {
5
+ theme: ThemeId | null;
6
+ colorScheme: ColorScheme | null;
7
+ }
8
+ export declare function useGetActiveTheme(): UseGetActiveThemeResult;
@@ -0,0 +1,11 @@
1
+ 'use client';
2
+ import { useColorScheme } from '../hooks/useColorScheme';
3
+ import { useTheme } from './useTheme';
4
+
5
+ function useGetActiveTheme() {
6
+ const { theme } = useTheme();
7
+ const { colorScheme } = useColorScheme();
8
+ return { theme, colorScheme };
9
+ }
10
+
11
+ export { useGetActiveTheme };
@@ -47,15 +47,17 @@ function resolveInitialTheme(storageKey, defaultTheme) {
47
47
  if (fromCookie) return fromCookie;
48
48
  const fromStorage = localStorage_utils.readLocalStorage(storageKey);
49
49
  if (fromStorage) return fromStorage;
50
- return defaultTheme;
50
+ return defaultTheme != null ? defaultTheme : null;
51
51
  }
52
52
  function ensureResolved(defaultTheme, storageKey) {
53
53
  if (resolved) return;
54
54
  resolved = true;
55
55
  const value = resolveInitialTheme(storageKey, defaultTheme);
56
- applyTheme(value);
57
- persistTheme(storageKey, value);
58
56
  currentTheme = value;
57
+ if (value) {
58
+ applyTheme(value);
59
+ persistTheme(storageKey, value);
60
+ }
59
61
  notify();
60
62
  }
61
63
  function ensureColorSchemeObserver() {
@@ -77,7 +79,7 @@ function switchThemeInternal(storageKey, id) {
77
79
  resolved = true;
78
80
  notify();
79
81
  }
80
- function useTheme(options) {
82
+ function useTheme(options = {}) {
81
83
  const { defaultTheme, storageKey = theme_constants.THEME_STORAGE_KEY } = options;
82
84
  react.useEffect(() => {
83
85
  ensureColorSchemeObserver();
@@ -2,10 +2,10 @@ import type { ThemeId } from '../styles/themes/types';
2
2
  export type { ThemeId };
3
3
  export interface UseThemeOptions {
4
4
  /** The theme the app should fall back to when nothing is stored yet. */
5
- defaultTheme: ThemeId;
5
+ defaultTheme?: ThemeId;
6
6
  storageKey?: string;
7
7
  }
8
- export declare function useTheme(options: UseThemeOptions): {
8
+ export declare function useTheme(options?: UseThemeOptions): {
9
9
  theme: ThemeId | null;
10
10
  switchTheme: (id: ThemeId) => void;
11
11
  };
@@ -45,15 +45,17 @@ function resolveInitialTheme(storageKey, defaultTheme) {
45
45
  if (fromCookie) return fromCookie;
46
46
  const fromStorage = readLocalStorage(storageKey);
47
47
  if (fromStorage) return fromStorage;
48
- return defaultTheme;
48
+ return defaultTheme != null ? defaultTheme : null;
49
49
  }
50
50
  function ensureResolved(defaultTheme, storageKey) {
51
51
  if (resolved) return;
52
52
  resolved = true;
53
53
  const value = resolveInitialTheme(storageKey, defaultTheme);
54
- applyTheme(value);
55
- persistTheme(storageKey, value);
56
54
  currentTheme = value;
55
+ if (value) {
56
+ applyTheme(value);
57
+ persistTheme(storageKey, value);
58
+ }
57
59
  notify();
58
60
  }
59
61
  function ensureColorSchemeObserver() {
@@ -75,7 +77,7 @@ function switchThemeInternal(storageKey, id) {
75
77
  resolved = true;
76
78
  notify();
77
79
  }
78
- function useTheme(options) {
80
+ function useTheme(options = {}) {
79
81
  const { defaultTheme, storageKey = THEME_STORAGE_KEY } = options;
80
82
  useEffect(() => {
81
83
  ensureColorSchemeObserver();
package/dist/index.cjs CHANGED
@@ -23,6 +23,7 @@ var severity_types = require('./constants/severity.types');
23
23
  var AppHeader = require('./components/app-header/AppHeader');
24
24
  var nestedFiltering = require('./utils/arrays/nested-filtering');
25
25
  var formatDate = require('./utils/date/formatDate');
26
+ var clipboard_utils = require('./utils/clipboard.utils');
26
27
  var AttributeChip = require('./components/attribute-chip/AttributeChip');
27
28
  var MetaBar = require('./components/meta-bar/MetaBar');
28
29
  var FadeOverlay = require('./components/overlay/fade-overlay/FadeOverlay');
@@ -186,6 +187,12 @@ Object.keys(formatDate).forEach(function (k) {
186
187
  get: function () { return formatDate[k]; }
187
188
  });
188
189
  });
190
+ Object.keys(clipboard_utils).forEach(function (k) {
191
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
192
+ enumerable: true,
193
+ get: function () { return clipboard_utils[k]; }
194
+ });
195
+ });
189
196
  Object.keys(AttributeChip).forEach(function (k) {
190
197
  if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
191
198
  enumerable: true,
package/dist/index.d.ts CHANGED
@@ -21,6 +21,7 @@ export * from './constants/severity.types';
21
21
  export * from './components/app-header/AppHeader';
22
22
  export * from './utils/arrays/nested-filtering';
23
23
  export * from './utils/date/formatDate';
24
+ export * from './utils/clipboard.utils';
24
25
  export * from './components/attribute-chip/AttributeChip';
25
26
  export * from './components/meta-bar/MetaBar';
26
27
  export * from './components/overlay/fade-overlay/FadeOverlay';
package/dist/index.js CHANGED
@@ -21,6 +21,7 @@ export * from './constants/severity.types';
21
21
  export * from './components/app-header/AppHeader';
22
22
  export * from './utils/arrays/nested-filtering';
23
23
  export * from './utils/date/formatDate';
24
+ export * from './utils/clipboard.utils';
24
25
  export * from './components/attribute-chip/AttributeChip';
25
26
  export * from './components/meta-bar/MetaBar';
26
27
  export * from './components/overlay/fade-overlay/FadeOverlay';
@@ -252,6 +252,10 @@ tr.dbc-table--hover:hover td:last-child {
252
252
  font-weight: var(--font-weight-bold);
253
253
  }
254
254
 
255
+ .dbc-center-text {
256
+ text-align: center;
257
+ }
258
+
255
259
  /* ==========================================================================
256
260
  Layout utilities
257
261
  ========================================================================== */
package/dist/styles.css CHANGED
@@ -252,6 +252,10 @@ tr.dbc-table--hover:hover td:last-child {
252
252
  font-weight: var(--font-weight-bold);
253
253
  }
254
254
 
255
+ .dbc-center-text {
256
+ text-align: center;
257
+ }
258
+
255
259
  /* ==========================================================================
256
260
  Layout utilities
257
261
  ========================================================================== */
@@ -0,0 +1,32 @@
1
+ 'use strict';
2
+
3
+ function isBrowser() {
4
+ return typeof window !== "undefined" && typeof document !== "undefined";
5
+ }
6
+ async function copyToClipboard(text) {
7
+ if (!isBrowser()) return false;
8
+ try {
9
+ if (!window.isSecureContext || !navigator.clipboard) {
10
+ throw new Error("Clipboard API unavailable");
11
+ }
12
+ await navigator.clipboard.writeText(text);
13
+ return true;
14
+ } catch {
15
+ try {
16
+ const textarea = document.createElement("textarea");
17
+ textarea.value = text;
18
+ textarea.setAttribute("readonly", "");
19
+ textarea.style.position = "fixed";
20
+ textarea.style.left = "-9999px";
21
+ document.body.appendChild(textarea);
22
+ textarea.select();
23
+ const success = document.execCommand("copy");
24
+ document.body.removeChild(textarea);
25
+ return success;
26
+ } catch {
27
+ return false;
28
+ }
29
+ }
30
+ }
31
+
32
+ exports.copyToClipboard = copyToClipboard;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Copy text to the clipboard.
3
+ * - Uses the async Clipboard API when available (requires a secure context)
4
+ * - Falls back to `document.execCommand('copy')` otherwise, so copying still
5
+ * works over plain HTTP (e.g. internal test environments)
6
+ * - Never throws; resolves to whether the copy succeeded
7
+ */
8
+ export declare function copyToClipboard(text: string): Promise<boolean>;
@@ -0,0 +1,30 @@
1
+ function isBrowser() {
2
+ return typeof window !== "undefined" && typeof document !== "undefined";
3
+ }
4
+ async function copyToClipboard(text) {
5
+ if (!isBrowser()) return false;
6
+ try {
7
+ if (!window.isSecureContext || !navigator.clipboard) {
8
+ throw new Error("Clipboard API unavailable");
9
+ }
10
+ await navigator.clipboard.writeText(text);
11
+ return true;
12
+ } catch {
13
+ try {
14
+ const textarea = document.createElement("textarea");
15
+ textarea.value = text;
16
+ textarea.setAttribute("readonly", "");
17
+ textarea.style.position = "fixed";
18
+ textarea.style.left = "-9999px";
19
+ document.body.appendChild(textarea);
20
+ textarea.select();
21
+ const success = document.execCommand("copy");
22
+ document.body.removeChild(textarea);
23
+ return success;
24
+ } catch {
25
+ return false;
26
+ }
27
+ }
28
+ }
29
+
30
+ export { copyToClipboard };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dbcdk/react-components",
3
- "version": "0.0.177",
3
+ "version": "0.0.179",
4
4
  "description": "Reusable React components for DBC projects",
5
5
  "license": "ISC",
6
6
  "author": "",