@dbcdk/react-components 0.0.87 → 0.0.89

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 (66) hide show
  1. package/dist/components/accordion/Accordion.d.ts +1 -0
  2. package/dist/components/accordion/components/AccordionRow.js +1 -1
  3. package/dist/components/button/Button.js +8 -1
  4. package/dist/components/button/Button.module.css +2 -1
  5. package/dist/components/card/Card.d.ts +1 -5
  6. package/dist/components/card/Card.js +29 -4
  7. package/dist/components/card/Card.module.css +85 -98
  8. package/dist/components/card-container/CardContainer.d.ts +2 -1
  9. package/dist/components/card-container/CardContainer.js +2 -2
  10. package/dist/components/card-container/CardContainer.module.css +10 -9
  11. package/dist/components/clear-button/ClearButton.d.ts +2 -1
  12. package/dist/components/clear-button/ClearButton.js +6 -2
  13. package/dist/components/clear-button/ClearButton.module.css +6 -0
  14. package/dist/components/divider/Divider.d.ts +5 -0
  15. package/dist/components/divider/Divider.js +12 -0
  16. package/dist/components/forms/input/Input.d.ts +2 -1
  17. package/dist/components/forms/input/Input.js +6 -2
  18. package/dist/components/forms/input/Input.module.css +32 -0
  19. package/dist/components/forms/select/Select.d.ts +2 -1
  20. package/dist/components/forms/select/Select.js +2 -2
  21. package/dist/components/forms/typeahead/Typeahead.d.ts +2 -1
  22. package/dist/components/forms/typeahead/Typeahead.js +180 -118
  23. package/dist/components/forms/typeahead/Typeahead.module.css +4 -0
  24. package/dist/components/grid/Grid.d.ts +21 -0
  25. package/dist/components/grid/Grid.js +21 -0
  26. package/dist/components/grid/Grid.module.css +20 -0
  27. package/dist/components/headline/Headline.d.ts +3 -3
  28. package/dist/components/headline/Headline.js +6 -6
  29. package/dist/components/headline/Headline.module.css +25 -6
  30. package/dist/components/nav-bar/NavBar.module.css +6 -2
  31. package/dist/components/overlay/modal/Modal.d.ts +2 -1
  32. package/dist/components/overlay/modal/Modal.js +5 -3
  33. package/dist/components/overlay/modal/provider/ModalProvider.js +2 -0
  34. package/dist/components/overlay/side-panel/SidePanel.d.ts +2 -1
  35. package/dist/components/overlay/side-panel/SidePanel.js +2 -2
  36. package/dist/components/page/Page.d.ts +5 -1
  37. package/dist/components/page/Page.js +6 -2
  38. package/dist/components/page/Page.module.css +54 -4
  39. package/dist/components/panel/Panel.d.ts +2 -1
  40. package/dist/components/panel/Panel.js +2 -2
  41. package/dist/components/popover/Popover.js +3 -3
  42. package/dist/components/stack/Stack.d.ts +16 -0
  43. package/dist/components/stack/Stack.js +19 -0
  44. package/dist/components/state-page/StatePage.d.ts +2 -1
  45. package/dist/components/state-page/StatePage.js +2 -2
  46. package/dist/components/table/Table.d.ts +1 -1
  47. package/dist/components/table/Table.js +22 -4
  48. package/dist/components/table/Table.module.css +14 -0
  49. package/dist/components/table/Table.types.d.ts +1 -0
  50. package/dist/components/tabs/Tabs.d.ts +3 -1
  51. package/dist/components/tabs/Tabs.js +4 -2
  52. package/dist/components/tabs/Tabs.module.css +4 -0
  53. package/dist/components/theme-button/ThemeButton.d.ts +1 -0
  54. package/dist/components/theme-button/ThemeButton.js +5 -1
  55. package/dist/components/toast/Toast.d.ts +2 -1
  56. package/dist/components/toast/Toast.js +2 -2
  57. package/dist/hooks/useViewportFill.d.ts +2 -6
  58. package/dist/hooks/useViewportFill.js +29 -24
  59. package/dist/index.d.ts +4 -0
  60. package/dist/index.js +4 -0
  61. package/dist/styles/css-helper-classes/flex.css +12 -0
  62. package/dist/styles/css-helper-classes/spacing.css +5 -0
  63. package/dist/styles/styles.css +154 -66
  64. package/dist/styles/themes/dbc/colors.css +10 -0
  65. package/dist/styles.css +154 -66
  66. package/package.json +1 -1
@@ -2,6 +2,7 @@ import type { JSX, ReactNode } from 'react';
2
2
  import type { Severity } from '../../constants/severity.types';
3
3
  export interface AccordionItem {
4
4
  header: string;
5
+ subheader?: ReactNode;
5
6
  headerAddition?: ReactNode;
6
7
  headerIcon?: ReactNode;
7
8
  severity?: Severity;
@@ -49,5 +49,5 @@ export function AccordionRow({ uid, index, item, isOpen, onToggle, shouldAnimate
49
49
  const buttonId = `${uid}-acc-btn-${index}`;
50
50
  const panelId = `${uid}-acc-panel-${index}`;
51
51
  const { innerRef, height, onTransitionEnd } = useCollapsibleHeight(isOpen, shouldAnimate);
52
- return (_jsxs("section", { className: `${styles.item} ${isOpen ? styles.open : ''} ${isDisabled ? styles.disabled : ''}`, children: [_jsx("div", { children: _jsxs("button", { type: "button", id: buttonId, className: styles.trigger, "aria-expanded": isOpen, "aria-controls": panelId, onClick: () => onToggle(index), disabled: isDisabled, children: [_jsxs("span", { className: styles.title, children: [item.headerIcon ? _jsx("span", { className: styles.icon, children: item.headerIcon }) : null, _jsx(Headline, { disableMargin: true, size: 4, weight: 500, severity: item.severity, allowWrap: isOpen, children: item.header }), item.headerAddition] }), _jsx("span", { className: styles.chevron, "aria-hidden": "true", children: _jsx(ChevronDown, {}) })] }) }), _jsx("div", { id: panelId, role: "region", "aria-labelledby": buttonId, className: `${styles.panel} ${shouldAnimate ? styles.animate : styles.noAnimate}`, style: { height }, onTransitionEnd: onTransitionEnd, children: _jsx("div", { ref: innerRef, className: styles.content, children: item.children }) })] }));
52
+ return (_jsxs("section", { className: `${styles.item} ${isOpen ? styles.open : ''} ${isDisabled ? styles.disabled : ''}`, children: [_jsx("div", { children: _jsxs("button", { type: "button", id: buttonId, className: styles.trigger, "aria-expanded": isOpen, "aria-controls": panelId, onClick: () => onToggle(index), disabled: isDisabled, children: [_jsxs("span", { className: styles.title, children: [item.headerIcon ? _jsx("span", { className: styles.icon, children: item.headerIcon }) : null, _jsx(Headline, { disableMargin: true, size: 4, weight: 500, severity: item.severity, subheader: item.subheader, allowWrap: isOpen, children: item.header }), item.headerAddition] }), _jsx("span", { className: styles.chevron, "aria-hidden": "true", children: _jsx(ChevronDown, {}) })] }) }), _jsx("div", { id: panelId, role: "region", "aria-labelledby": buttonId, className: `${styles.panel} ${shouldAnimate ? styles.animate : styles.noAnimate}`, style: { height }, onTransitionEnd: onTransitionEnd, children: _jsx("div", { ref: innerRef, className: styles.content, children: item.children }) })] }));
53
53
  }
@@ -67,7 +67,14 @@ export const Button = React.forwardRef(function Button({ variant = 'outlined', s
67
67
  });
68
68
  }
69
69
  else {
70
- buttonEl = (_jsx("button", { className: computedClassName, type: type, ...buttonProps, ...(tooltipEnabled ? triggerProps : {}), "aria-describedby": describedBy, children: content }));
70
+ const isDisabled = Boolean(buttonProps.disabled);
71
+ buttonEl = (_jsx("button", { className: computedClassName, type: type, ...buttonProps, ...(tooltipEnabled && !isDisabled ? triggerProps : {}), "aria-describedby": describedBy, children: content }));
72
+ if (tooltipEnabled && isDisabled) {
73
+ buttonEl = (_jsx("span", { ref: triggerProps.ref, onPointerEnter: triggerProps.onPointerEnter, onPointerLeave: triggerProps.onPointerLeave, style: {
74
+ display: fullWidth ? 'flex' : 'inline-flex',
75
+ cursor: 'not-allowed',
76
+ }, children: buttonEl }));
77
+ }
71
78
  }
72
79
  return buttonEl;
73
80
  });
@@ -2,7 +2,7 @@
2
2
  display: inline-flex;
3
3
  align-items: center;
4
4
  justify-content: center;
5
- gap: var(--spacing-xxs);
5
+ gap: var(--spacing-xs);
6
6
 
7
7
  font-family: var(--font-family);
8
8
  font-size: var(--font-size-sm);
@@ -117,6 +117,7 @@
117
117
 
118
118
  .button.lg {
119
119
  height: var(--component-size-lg);
120
+ font-size: var(--font-size-md);
120
121
  min-block-size: var(--component-size-lg);
121
122
  padding-inline: var(--spacing-lg);
122
123
  }
@@ -6,6 +6,7 @@ type CardSize = 'sm' | 'md' | 'lg';
6
6
  type CardImagePlacement = 'left' | 'right' | 'top';
7
7
  export interface CardProps {
8
8
  title?: string;
9
+ subheader?: ReactNode;
9
10
  loading?: boolean;
10
11
  variant?: CardVariant;
11
12
  size?: CardSize;
@@ -21,11 +22,6 @@ export interface CardProps {
21
22
  sectionTitle?: string;
22
23
  showSectionDivider?: boolean;
23
24
  children?: ReactNode;
24
- /**
25
- * Keep current behavior: if provided, Card becomes "linked".
26
- * NOTE: this assumes your Hyperlink component can render the passed element correctly.
27
- * If Hyperlink expects an <a>, pass <a href="...">...</a> or whatever your existing pattern is.
28
- */
29
25
  link?: ReactNode;
30
26
  width?: 25 | 33 | 50 | 66 | 75 | 100;
31
27
  headlineSize?: 1 | 2 | 3 | 4 | 5 | 6;
@@ -4,6 +4,27 @@ import { CardMeta, CardMetaRow } from './components/CardMeta';
4
4
  import { Headline } from '../headline/Headline';
5
5
  import { Hyperlink } from '../hyperlink/Hyperlink';
6
6
  import { SkeletonLoaderItem } from '../skeleton-loader/skeleton-loader-item/SkeletonLoaderItem';
7
+ /**
8
+ * Fix for flex gap issue:
9
+ * Distributes gap proportionally across cards
10
+ */
11
+ function getGapShare(width) {
12
+ switch (width) {
13
+ case 25:
14
+ return 'calc(var(--card-container-gap, var(--spacing-md)) * 3 / 4)';
15
+ case 33:
16
+ return 'calc(var(--card-container-gap, var(--spacing-md)) * 2 / 3)';
17
+ case 50:
18
+ return 'calc(var(--card-container-gap, var(--spacing-md)) / 2)';
19
+ case 66:
20
+ return 'calc(var(--card-container-gap, var(--spacing-md)) * 2 / 3)';
21
+ case 75:
22
+ return 'calc(var(--card-container-gap, var(--spacing-md)) * 3 / 4)';
23
+ case 100:
24
+ default:
25
+ return '0px';
26
+ }
27
+ }
7
28
  function getInnerPlacementClass(imgPlacement, s) {
8
29
  switch (imgPlacement) {
9
30
  case 'top':
@@ -24,8 +45,13 @@ function getVariantClass(variant, s) {
24
45
  return s.variantDefault;
25
46
  }
26
47
  }
27
- function CardImpl({ title, loading = false, variant = 'default', size = 'md', headerMarker = true, headerIcon, headerAddition, severity, image, imgPlacement = 'left', mediaWidth, actions, headerMeta, sectionTitle, showSectionDivider = false, children, link, width, headlineSize = 4, }) {
28
- const outerStyle = width ? { ['--width']: `${width}%` } : undefined;
48
+ function CardImpl({ title, subheader, loading = false, variant = 'default', size = 'md', headerMarker = true, headerIcon, headerAddition, severity, image, imgPlacement = 'left', mediaWidth, actions, headerMeta, sectionTitle, showSectionDivider = false, children, link, width, headlineSize = 4, }) {
49
+ const outerStyle = width
50
+ ? {
51
+ ['--width']: `${width}%`,
52
+ ['--gap-share']: getGapShare(width),
53
+ }
54
+ : undefined;
29
55
  const mediaStyle = mediaWidth
30
56
  ? { ['--card-media-width']: `${mediaWidth}px` }
31
57
  : undefined;
@@ -35,8 +61,7 @@ function CardImpl({ title, loading = false, variant = 'default', size = 'md', he
35
61
  const showSection = !loading && (showSectionDivider || !!sectionTitle);
36
62
  const showBody = !loading && !!children;
37
63
  const showActions = !loading && !!actions;
38
- const inner = (_jsxs("div", { className: `${styles.inner} ${innerPlacementClass}`, children: [image ? (_jsx("div", { className: styles.media, style: mediaStyle, children: image })) : null, _jsxs("div", { className: styles.content, children: [hasHeader ? (_jsxs("header", { className: styles.header, children: [title ? (_jsx(Headline, { severity: severity, marker: headerMarker, icon: headerIcon, addition: headerAddition, size: headlineSize, weight: 500, disableMargin: true, children: title })) : null, headerMeta ? _jsx("div", { className: styles.headerMeta, children: headerMeta }) : null] })) : null, loading ? (_jsx("div", { className: styles.loadingList, "aria-busy": "true", "aria-live": "polite", children: Array.from({ length: 4 }, (_, index) => (_jsxs("div", { className: styles.loadingRow, children: [_jsx(SkeletonLoaderItem, {}), _jsx(SkeletonLoaderItem, { width: "100%" })] }, index))) })) : null, showSection ? (_jsxs("div", { className: styles.section, children: [showSectionDivider ? _jsx("div", { className: styles.sectionDivider }) : null, sectionTitle ? _jsx("div", { className: styles.sectionTitle, children: sectionTitle }) : null] })) : null, showBody ? _jsx("div", { className: styles.body, children: children }) : null, showActions ? _jsx("div", { className: styles.actions, children: actions }) : null] })] }));
39
- // keep existing behavior
64
+ const inner = (_jsxs("div", { className: `${styles.inner} ${innerPlacementClass}`, children: [image ? (_jsx("div", { className: styles.media, style: mediaStyle, children: image })) : null, _jsxs("div", { className: styles.content, children: [hasHeader ? (_jsxs("header", { className: styles.header, children: [title ? (_jsx(Headline, { severity: severity, marker: headerMarker, icon: headerIcon, addition: headerAddition, subheader: subheader, size: headlineSize, weight: 500, disableMargin: true, children: title })) : null, headerMeta ? _jsx("div", { className: styles.headerMeta, children: headerMeta }) : null] })) : null, loading ? (_jsx("div", { className: styles.loadingList, "aria-busy": "true", "aria-live": "polite", children: Array.from({ length: 4 }, (_, index) => (_jsxs("div", { className: styles.loadingRow, children: [_jsx(SkeletonLoaderItem, {}), _jsx(SkeletonLoaderItem, { width: "100%" })] }, index))) })) : null, showSection ? (_jsxs("div", { className: styles.section, children: [showSectionDivider ? _jsx("div", { className: styles.sectionDivider }) : null, sectionTitle ? _jsx("div", { className: styles.sectionTitle, children: sectionTitle }) : null] })) : null, showBody ? _jsx("div", { className: styles.body, children: children }) : null, showActions ? _jsx("div", { className: styles.actions, children: actions }) : null] })] }));
40
65
  const cardContent = link ? _jsx(Hyperlink, { children: link }) : inner;
41
66
  return (_jsx("div", { className: `${styles.outerContainer} ${styles[size]}`, style: outerStyle, children: _jsx("div", { className: `${styles.container} ${variantClass}`, children: cardContent }) }));
42
67
  }
@@ -1,62 +1,53 @@
1
- /* OUTER WRAPPER (optional width control) */
2
1
  .outerContainer {
3
- inline-size: var(--width, 100%);
2
+ --width: 100%;
3
+ --gap-share: 0px;
4
+
5
+ flex: 0 1 calc(var(--width) - var(--gap-share));
6
+ min-width: 0;
7
+ box-sizing: border-box;
4
8
  }
5
9
 
6
- /* CARD CONTAINER */
10
+ /* Card surface */
7
11
  .container {
8
- inline-size: 100%;
9
- position: relative;
10
- color: var(--card-fg-default, var(--color-fg-default));
11
- background-color: var(--card-bg-default, var(--color-bg-surface));
12
- border-radius: var(--border-radius-md);
13
- border: var(--border-width-thin) solid var(--color-border-subtle);
14
- box-shadow: var(--shadow-xs);
15
- transition:
16
- color var(--transition-fast) var(--ease-standard),
17
- box-shadow var(--transition-fast) var(--ease-standard),
18
- transform var(--transition-fast) var(--ease-standard);
19
- }
12
+ height: 100%;
13
+ border-radius: var(--border-radius-sm);
14
+ box-sizing: border-box;
20
15
 
21
- .container:hover {
22
- box-shadow: var(--shadow-sm);
16
+ /* ✅ Restore border */
17
+ border: 1px solid var(--color-border-subtle);
18
+
19
+ /* Optional: ensure background sits under border */
20
+ background-clip: padding-box;
23
21
  }
24
22
 
25
- /* BACKGROUND VARIANTS */
23
+ /* Variants */
26
24
  .variantDefault {
27
- background-color: var(--card-bg-default, var(--color-bg-surface));
25
+ background-color: var(--card-bg-default, var(--color-bg-surface, var(--color-bg-surface-subtle)));
28
26
  }
29
27
 
30
28
  .variantSubtle {
31
- background-color: var(--card-bg-subtle, var(--color-bg-contextual-subtle));
29
+ background-color: var(--card-bg-subtle, var(--color-bg-surface-subtle, var(--color-bg-surface)));
32
30
  }
33
31
 
34
- /* SIZE VARIANTS (define vars once; inner uses them) */
35
- .sm {
36
- --card-pad: var(--spacing-md);
37
- --card-gap: var(--spacing-md);
32
+ /* Sizes */
33
+ .sm .container {
34
+ padding: var(--spacing-md);
38
35
  }
39
- .md {
40
- --card-pad: var(--spacing-lg);
41
- --card-gap: var(--spacing-lg);
36
+
37
+ .md .container {
38
+ padding: var(--spacing-lg);
42
39
  }
43
- .lg {
44
- --card-pad: var(--spacing-xl);
45
- --card-gap: var(--spacing-xl);
40
+
41
+ .lg .container {
42
+ padding: var(--spacing-xl);
46
43
  }
47
44
 
48
- /* INNER LAYOUT */
45
+ /* Layout */
49
46
  .inner {
50
- margin-inline: auto;
51
47
  display: flex;
52
- align-items: flex-start;
53
- padding: var(--card-pad);
54
- gap: var(--card-gap);
55
- }
56
-
57
- /* Media placement */
58
- .innerImgTop {
59
- flex-direction: column;
48
+ gap: var(--spacing-lg);
49
+ height: 100%;
50
+ min-width: 0;
60
51
  }
61
52
 
62
53
  .innerImgLeft {
@@ -67,101 +58,97 @@
67
58
  flex-direction: row-reverse;
68
59
  }
69
60
 
70
- /* MEDIA */
71
- .media {
72
- flex: 0 0 auto;
73
- display: flex;
74
- align-items: flex-start;
75
- justify-content: center;
76
- }
77
-
78
- .innerImgLeft .media,
79
- .innerImgRight .media {
80
- inline-size: var(--card-media-width, 56px);
81
- max-inline-size: 25%;
82
- }
83
-
84
- .innerImgTop .media {
85
- inline-size: 100%;
86
- max-inline-size: 100%;
61
+ .innerImgTop {
62
+ flex-direction: column;
87
63
  }
88
64
 
89
- .media img,
90
- .media picture,
91
- .media video,
92
- .media svg {
93
- display: block;
94
- inline-size: 100%;
95
- block-size: auto;
65
+ .media {
66
+ flex: 0 0 var(--card-media-width, auto);
67
+ min-width: 0;
96
68
  }
97
69
 
98
- /* CONTENT */
99
70
  .content {
100
- flex: 1 1 auto;
101
- min-inline-size: 0;
102
71
  display: flex;
72
+ flex: 1 1 auto;
103
73
  flex-direction: column;
104
74
  gap: var(--spacing-md);
75
+ min-width: 0;
105
76
  }
106
77
 
107
- /* HEADER */
78
+ /* Header */
108
79
  .header {
109
80
  display: flex;
110
81
  align-items: flex-start;
111
82
  justify-content: space-between;
112
- gap: var(--spacing-sm);
113
- }
114
-
115
- .header > :first-child {
116
- flex: 1 1 auto;
117
- min-inline-size: 0;
83
+ gap: var(--spacing-md);
84
+ min-width: 0;
118
85
  }
119
86
 
120
87
  .headerMeta {
121
- display: flex;
122
- align-items: center;
123
- gap: var(--spacing-xs);
88
+ flex: 0 0 auto;
124
89
  }
125
90
 
126
- /* LOADING */
127
- .loadingList {
128
- display: grid;
129
- gap: var(--spacing-sm);
91
+ /* Body */
92
+ .body {
93
+ min-width: 0;
130
94
  }
131
95
 
132
- .loadingRow {
133
- display: grid;
134
- grid-template-columns: auto 1fr;
96
+ /* Actions */
97
+ .actions {
98
+ display: flex;
99
+ flex-wrap: wrap;
135
100
  gap: var(--spacing-sm);
136
- align-items: center;
101
+ margin-top: auto;
137
102
  }
138
103
 
139
- /* SECTION */
104
+ /* Section */
140
105
  .section {
141
106
  display: flex;
142
107
  flex-direction: column;
143
- gap: var(--spacing-md);
108
+ gap: var(--spacing-sm);
144
109
  }
145
110
 
146
111
  .sectionDivider {
147
- inline-size: 100%;
148
- block-size: var(--border-width-thin);
149
- background: var(--color-border-subtle);
112
+ width: 100%;
113
+ height: 1px;
114
+ background-color: var(--color-border-subtle);
150
115
  }
151
116
 
152
117
  .sectionTitle {
153
- font-weight: var(--font-weight-semibold);
118
+ font-weight: 500;
154
119
  }
155
120
 
156
- /* BODY */
157
- .body {
158
- /* neutral container */
121
+ /* Loading */
122
+ .loadingList {
123
+ display: flex;
124
+ flex-direction: column;
125
+ gap: var(--spacing-md);
159
126
  }
160
127
 
161
- /* ACTIONS */
162
- .actions {
163
- margin-top: auto;
128
+ .loadingRow {
164
129
  display: flex;
165
- justify-content: flex-end;
130
+ flex-direction: column;
166
131
  gap: var(--spacing-sm);
167
132
  }
133
+
134
+ /* Responsive */
135
+ @media screen and (max-width: 767px) {
136
+ .outerContainer {
137
+ --width: 100%;
138
+ --gap-share: 0px;
139
+
140
+ flex-basis: 100%;
141
+ max-width: 100%;
142
+ }
143
+
144
+ .inner,
145
+ .innerImgLeft,
146
+ .innerImgRight,
147
+ .innerImgTop {
148
+ flex-direction: column;
149
+ }
150
+
151
+ .header {
152
+ flex-direction: column;
153
+ }
154
+ }
@@ -2,10 +2,11 @@ import type { JSX, ReactNode } from 'react';
2
2
  import { Severity } from '../../constants/severity.types';
3
3
  interface CardContainerProps {
4
4
  headline?: string;
5
+ subheader?: ReactNode;
5
6
  children?: ReactNode[];
6
7
  expand?: boolean;
7
8
  severity?: Severity;
8
9
  displayHeaderMarker?: boolean;
9
10
  }
10
- export declare function CardContainer({ children, headline, expand, severity, displayHeaderMarker, }: CardContainerProps): JSX.Element;
11
+ export declare function CardContainer({ children, headline, subheader, expand, severity, displayHeaderMarker, }: CardContainerProps): JSX.Element;
11
12
  export {};
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Headline } from '../../components/headline/Headline';
3
3
  import styles from './CardContainer.module.css';
4
- export function CardContainer({ children, headline, expand, severity, displayHeaderMarker, }) {
5
- return (_jsxs("div", { className: styles.wrapper, children: [headline && (_jsx(Headline, { marker: displayHeaderMarker, severity: severity, disableMargin: true, children: headline })), _jsx("div", { className: styles.container, style: { ['--expand']: expand ? '1' : '0' }, children: children })] }));
4
+ export function CardContainer({ children, headline, subheader, expand, severity, displayHeaderMarker, }) {
5
+ return (_jsxs("div", { className: styles.wrapper, children: [headline && (_jsx(Headline, { marker: displayHeaderMarker, severity: severity, disableMargin: true, subheader: subheader, children: headline })), _jsx("div", { className: styles.container, style: { ['--expand']: expand ? '1' : '0' }, children: children })] }));
6
6
  }
@@ -8,8 +8,8 @@
8
8
  display: flex;
9
9
  flex-wrap: wrap;
10
10
  gap: var(--spacing-md);
11
+ --card-container-gap: var(--spacing-md);
11
12
  border-radius: var(--border-radius-sm);
12
- --width: 100%;
13
13
  }
14
14
 
15
15
  .container.default {
@@ -47,14 +47,15 @@
47
47
  padding: var(--spacing-lg);
48
48
  }
49
49
 
50
- @media screen and (min-width: 768px) {
51
- .container {
52
- --width: 33.333%;
53
- }
54
- }
55
-
56
50
  .container > * {
57
- flex-grow: var(--expand);
58
- flex-shrink: 1;
51
+ min-width: 0;
59
52
  box-sizing: border-box;
60
53
  }
54
+
55
+ /* Mobile: stack */
56
+ @media screen and (max-width: 767px) {
57
+ .container > * {
58
+ flex-basis: 100% !important;
59
+ max-width: 100%;
60
+ }
61
+ }
@@ -1,6 +1,7 @@
1
1
  import type { ReactNode } from 'react';
2
+ import * as React from 'react';
2
3
  interface ClearButtonProps {
3
- onClick: () => void;
4
+ onClick?: (event?: React.MouseEvent | React.KeyboardEvent) => void;
4
5
  absolute?: boolean;
5
6
  }
6
7
  export declare function ClearButton({ onClick, absolute }: ClearButtonProps): ReactNode;
@@ -2,8 +2,12 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { X } from 'lucide-react';
3
3
  import styles from './ClearButton.module.css';
4
4
  export function ClearButton({ onClick, absolute }) {
5
- return (_jsx("span", { className: `${styles.clearButton} ${absolute ? styles.absolute : ''}`, children: _jsx("span", { className: styles.button, role: "button", onClick: e => {
5
+ return (_jsx("span", { className: `${styles.clearButton} ${absolute ? styles.absolute : ''}`, children: _jsx("button", { className: styles.button, type: "button", "data-input-role": "clear", onMouseDown: e => {
6
+ e.preventDefault();
6
7
  e.stopPropagation();
7
- onClick();
8
+ }, onClick: e => {
9
+ e.preventDefault();
10
+ e.stopPropagation();
11
+ onClick === null || onClick === void 0 ? void 0 : onClick(e);
8
12
  }, children: _jsx(X, { size: 16 }) }) }));
9
13
  }
@@ -1,8 +1,14 @@
1
1
  .clearButton .button {
2
+ appearance: none;
2
3
  display: flex;
3
4
  align-items: center;
4
5
  justify-content: center;
6
+ margin: 0;
7
+ border: 0;
8
+ background: transparent;
5
9
  color: var(--color-fg-subtle);
10
+ font: inherit;
11
+ line-height: 0;
6
12
  padding: var(--spacing-xxs);
7
13
  cursor: pointer;
8
14
  border-radius: 100%;
@@ -0,0 +1,5 @@
1
+ type DividerProps = {
2
+ spacing?: 'sm' | 'md' | 'lg';
3
+ };
4
+ export declare const Divider: ({ spacing }: DividerProps) => import("react/jsx-runtime").JSX.Element;
5
+ export {};
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ const spacingMap = {
3
+ sm: 'var(--spacing-sm)',
4
+ md: 'var(--spacing-md)',
5
+ lg: 'var(--spacing-lg)',
6
+ };
7
+ export const Divider = ({ spacing = 'md' }) => {
8
+ return (_jsx("div", { style: {
9
+ borderTop: '1px solid var(--color-border-subtle)',
10
+ marginBlock: spacingMap[spacing],
11
+ } }));
12
+ };
@@ -10,10 +10,11 @@ export type InputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size
10
10
  maxWidth?: string | number;
11
11
  inputSize?: Exclude<Size, 'xl'>;
12
12
  variant?: InputVariant;
13
- onClear?: () => void;
13
+ onClear?: (event?: React.MouseEvent | React.KeyboardEvent) => void;
14
14
  onButtonClick?: () => void;
15
15
  buttonLabel?: string;
16
16
  buttonIcon?: React.ReactNode;
17
+ trailingLabel?: string;
17
18
  tooltip?: React.ReactNode;
18
19
  tooltipPlacement?: 'top' | 'right' | 'bottom' | 'left';
19
20
  tooltipOpenOnFocus?: boolean;
@@ -18,7 +18,7 @@ function mergeRefs(...refs) {
18
18
  }
19
19
  };
20
20
  }
21
- export const Input = forwardRef(function Input({ label, error, helpText, orientation = 'vertical', labelWidth = '160px', fullWidth = false, required, tooltip, tooltipPlacement = 'right', modified = false, icon, autoFocus, minWidth, width, maxWidth, inputSize = 'md', variant = 'outlined', onClear, onButtonClick, buttonLabel, buttonIcon, id, tooltipOpenOnFocus = true, style, className, fieldClassName, inputClassName, startAdornment, endAdornment, ...inputProps }, ref) {
21
+ export const Input = forwardRef(function Input({ label, error, helpText, orientation = 'vertical', labelWidth = '160px', fullWidth = false, required, tooltip, tooltipPlacement = 'right', modified = false, icon, autoFocus, minWidth, width, maxWidth, inputSize = 'md', variant = 'outlined', onClear, onButtonClick, buttonLabel, buttonIcon, trailingLabel, id, tooltipOpenOnFocus = true, style, className, fieldClassName, inputClassName, startAdornment, endAdornment, ...inputProps }, ref) {
22
22
  const inputRef = useRef(null);
23
23
  const reactId = useId();
24
24
  const inputId = id !== null && id !== void 0 ? id : `input-${reactId}`;
@@ -28,6 +28,7 @@ export const Input = forwardRef(function Input({ label, error, helpText, orienta
28
28
  (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
29
29
  }, [autoFocus]);
30
30
  const hasButton = Boolean(onButtonClick || buttonLabel || buttonIcon);
31
+ const hasTrailingLabel = Boolean(trailingLabel);
31
32
  const hasValue = Boolean(inputProps.value);
32
33
  const hasVisibleClear = Boolean(onClear && hasValue);
33
34
  const hasEndAdornment = Boolean(endAdornment);
@@ -54,6 +55,7 @@ export const Input = forwardRef(function Input({ label, error, helpText, orienta
54
55
  onClear ? styles.withClear : '',
55
56
  hasInlineClear ? styles.withInlineClear : '',
56
57
  hasButton ? styles.withButton : '',
58
+ hasTrailingLabel ? styles.withTrailingLabel : '',
57
59
  className !== null && className !== void 0 ? className : '',
58
60
  ]
59
61
  .filter(Boolean)
@@ -68,6 +70,8 @@ export const Input = forwardRef(function Input({ label, error, helpText, orienta
68
70
  .filter(Boolean)
69
71
  .join(' '), "data-forminput": "field", "data-modified": modified ? 'true' : undefined, "aria-disabled": inputProps.disabled ? 'true' : undefined, ...(tooltip ? triggerProps : {}), children: [icon && (_jsx("span", { className: styles.icon, "data-input-role": "icon", children: icon })), startAdornment && (_jsx("span", { className: styles.startAdornment, "data-input-role": "start-adornment", children: startAdornment })), _jsx("input", { ...inputProps, id: inputId, ref: mergeRefs(inputRef, ref), className: [styles.input, inputSize ? styles[inputSize] : '', inputClassName !== null && inputClassName !== void 0 ? inputClassName : '']
70
72
  .filter(Boolean)
71
- .join(' ') }), (reservesInlineClearSlot || hasEndAdornment) && (_jsxs("span", { className: styles.endAdornment, "data-input-role": "end-adornment", children: [reservesInlineClearSlot ? (_jsx("span", { className: styles.clearSlot, "aria-hidden": hasVisibleClear ? undefined : 'true', children: hasVisibleClear && onClear ? _jsx(ClearButton, { onClick: onClear }) : null })) : null, endAdornment] })), hasVisibleClear && !hasEndAdornment && onClear ? (_jsx(ClearButton, { onClick: onClear, absolute: true })) : null] }), hasButton && (_jsxs(Button, { onClick: onButtonClick, className: styles.trailingButton, type: "button", variant: trailingButtonVariant, size: inputSize, children: [buttonIcon !== null && buttonIcon !== void 0 ? buttonIcon : null, buttonLabel !== null && buttonLabel !== void 0 ? buttonLabel : null] }))] }) }));
73
+ .join(' ') }), (reservesInlineClearSlot || hasEndAdornment) && (_jsxs("span", { className: styles.endAdornment, "data-input-role": "end-adornment", children: [reservesInlineClearSlot ? (_jsx("span", { className: styles.clearSlot, "aria-hidden": hasVisibleClear ? undefined : 'true', children: hasVisibleClear && onClear ? _jsx(ClearButton, { onClick: onClear }) : null })) : null, endAdornment] })), hasVisibleClear && !hasEndAdornment && onClear ? (_jsx(ClearButton, { onClick: onClear, absolute: true })) : null] }), hasTrailingLabel && (_jsx("span", { className: [styles.trailingLabel, inputSize ? styles[inputSize] : '']
74
+ .filter(Boolean)
75
+ .join(' '), children: trailingLabel })), hasButton && (_jsxs(Button, { onClick: onButtonClick, className: styles.trailingButton, type: "button", variant: trailingButtonVariant, size: inputSize, children: [buttonIcon !== null && buttonIcon !== void 0 ? buttonIcon : null, buttonLabel !== null && buttonLabel !== void 0 ? buttonLabel : null] }))] }) }));
72
76
  });
73
77
  Input.displayName = 'Input';
@@ -375,6 +375,38 @@
375
375
  block-size: var(--icon-size-md);
376
376
  }
377
377
 
378
+ /* Trailing label (unit, currency, etc.) */
379
+ .withTrailingLabel .field {
380
+ border-top-right-radius: 0;
381
+ border-bottom-right-radius: 0;
382
+ }
383
+
384
+ .trailingLabel {
385
+ display: flex;
386
+ align-items: center;
387
+ flex: 0 0 auto;
388
+ padding-inline: var(--spacing-sm);
389
+ font-family: var(--font-family);
390
+ font-size: var(--font-size-sm);
391
+ color: var(--color-fg-muted);
392
+ background: var(--color-bg-toolbar);
393
+ border: var(--border-width-thin) solid var(--color-border-default);
394
+ margin-left: calc(-1 * var(--border-width-thin));
395
+ border-top-right-radius: var(--border-radius-default);
396
+ border-bottom-right-radius: var(--border-radius-default);
397
+ white-space: nowrap;
398
+ user-select: none;
399
+ box-sizing: border-box;
400
+ }
401
+
402
+ .withTrailingLabel:has(.standalone) .trailingLabel {
403
+ border-top-right-radius: var(--border-radius-rounded);
404
+ border-bottom-right-radius: var(--border-radius-rounded);
405
+ border-color: var(--color-border-subtle);
406
+ background-color: var(--color-bg-surface);
407
+ box-shadow: var(--shadow-xs), var(--shadow-sm);
408
+ }
409
+
378
410
  /* Trailing action button (outside field) */
379
411
  .trailingButton {
380
412
  flex: 0 0 auto;
@@ -15,8 +15,9 @@ export type SelectProps<T> = Omit<InputContainerProps, 'children' | 'htmlFor' |
15
15
  datakey?: string;
16
16
  dataCy?: string;
17
17
  disabled?: boolean;
18
+ minWidth?: string | number;
18
19
  tooltip?: React.ReactNode;
19
20
  tooltipPlacement?: 'top' | 'right' | 'bottom' | 'left';
20
21
  };
21
- export declare function Select<T extends string | number | Record<string, any>>({ label, error, helpText, orientation, labelWidth, fullWidth, required, tooltip, tooltipPlacement, modified, id, options, selectedValue, onChange, placeholder, size, variant, onClear, datakey, dataCy, disabled, }: SelectProps<T>): React.ReactNode;
22
+ export declare function Select<T extends string | number | Record<string, any>>({ label, error, helpText, orientation, labelWidth, fullWidth, required, tooltip, tooltipPlacement, modified, id, options, selectedValue, onChange, placeholder, size, variant, onClear, datakey, dataCy, disabled, minWidth, }: SelectProps<T>): React.ReactNode;
22
23
  export {};
@@ -9,7 +9,7 @@ import { ClearButton } from '../../clear-button/ClearButton';
9
9
  import { Menu } from '../../menu/Menu';
10
10
  import { Popover } from '../../popover/Popover';
11
11
  import { InputContainer } from '../input-container/InputContainer';
12
- export function Select({ label, error, helpText, orientation = 'vertical', labelWidth = '160px', fullWidth = true, required, tooltip, tooltipPlacement = 'right', modified = false, id, options, selectedValue, onChange, placeholder = 'Vælg', size, variant = 'outlined', onClear, datakey, dataCy, disabled, }) {
12
+ export function Select({ label, error, helpText, orientation = 'vertical', labelWidth = '160px', fullWidth = true, required, tooltip, tooltipPlacement = 'right', modified = false, id, options, selectedValue, onChange, placeholder = 'Vælg', size, variant = 'outlined', onClear, datakey, dataCy, disabled, minWidth, }) {
13
13
  const generatedId = useId();
14
14
  const controlId = id !== null && id !== void 0 ? id : `select-${generatedId}`;
15
15
  const describedById = `${controlId}-desc`;
@@ -169,7 +169,7 @@ export function Select({ label, error, helpText, orientation = 'vertical', label
169
169
  returnFocus: true, trigger: (toggle, icon, isOpen) => (_jsx(Button, { disabled: disabled, ...(tooltipEnabled ? triggerProps : {}), id: controlId, "data-cy": dataCy !== null && dataCy !== void 0 ? dataCy : 'select-button', onKeyDown: handleKeyDown, fullWidth: fullWidth, variant: variant, onClick: e => {
170
170
  resetActiveToSelected();
171
171
  toggle(e);
172
- }, size: size, type: "button", "data-forminput": true, "aria-haspopup": "listbox", "aria-expanded": !!isOpen, "aria-controls": listboxId, "aria-invalid": Boolean(error) || undefined, "aria-describedby": describedBy, className: modified ? styles.triggerModified : undefined, children: _jsxs("span", { className: "dbc-flex dbc-justify-between dbc-items-center dbc-gap-xxs", style: { width: '100%' }, children: [_jsx("span", { children: selected ? selected.label : _jsx("span", { className: "dbc-muted-text", children: placeholder }) }), _jsxs("span", { className: "dbc-flex dbc-items-center dbc-gap-xxs", children: [onClear && selected && _jsx(ClearButton, { onClick: onClear }), _jsx("span", { style: { color: 'var(--color-fg-subtle)', display: 'inline-flex' }, children: icon })] })] }) })), children: _jsx(Menu, { onKeyDown: handleKeyDown, role: "listbox", children: options.map((opt, index) => {
172
+ }, size: size, style: minWidth != null ? { minWidth } : undefined, type: "button", "data-forminput": true, "aria-haspopup": "listbox", "aria-expanded": !!isOpen, "aria-controls": listboxId, "aria-invalid": Boolean(error) || undefined, "aria-describedby": describedBy, className: modified ? styles.triggerModified : undefined, children: _jsxs("span", { className: "dbc-flex dbc-justify-between dbc-items-center dbc-gap-xxs", style: { width: '100%' }, children: [_jsx("span", { children: selected ? selected.label : _jsx("span", { className: "dbc-muted-text", children: placeholder }) }), _jsxs("span", { className: "dbc-flex dbc-items-center dbc-gap-xxs", children: [onClear && selected && _jsx(ClearButton, { onClick: onClear }), _jsx("span", { style: { color: 'var(--color-fg-subtle)', display: 'inline-flex' }, children: icon })] })] }) })), children: _jsx(Menu, { onKeyDown: handleKeyDown, role: "listbox", children: options.map((opt, index) => {
173
173
  const isSelected = typeof opt.value === 'object' && typeof selectedValue === 'object' && datakey
174
174
  ? (selectedValue === null || selectedValue === void 0 ? void 0 : selectedValue[datakey]) === opt.value[datakey]
175
175
  : opt.value === selectedValue;
@@ -33,6 +33,7 @@ interface TypeaheadProps<T> {
33
33
  spellCheck?: InputProps['spellCheck'];
34
34
  popoverAnchorRef?: React.RefObject<HTMLElement | null>;
35
35
  fitContent?: boolean;
36
+ enableHotkey?: boolean;
36
37
  }
37
- export declare function Typeahead<T extends string | number>({ options, mode, multiValueDisplayMode, multiSelectedValuesDisplayMode, multiSelectedValueChipContent, selectedValue, onChange, placeholder, variant, disabled, fullWidth, onClear, emptyMessage, filterOptions, inputProps, inputSize, width, minWidth, popoverWidth, autoComplete, autoCorrect, autoCapitalize, spellCheck, popoverAnchorRef, fitContent, }: TypeaheadProps<T>): React.ReactElement;
38
+ export declare function Typeahead<T extends string | number>({ options, mode, multiValueDisplayMode, multiSelectedValuesDisplayMode, multiSelectedValueChipContent, selectedValue, onChange, placeholder, variant, disabled, fullWidth, onClear, emptyMessage, filterOptions, inputProps, inputSize, width, minWidth, popoverWidth, autoComplete, autoCorrect, autoCapitalize, spellCheck, popoverAnchorRef, fitContent, enableHotkey, }: TypeaheadProps<T>): React.ReactElement;
38
39
  export {};