@dbcdk/react-components 0.0.19 → 0.0.21

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 (46) hide show
  1. package/dist/components/button/Button.module.css +21 -10
  2. package/dist/components/chip/Chip.d.ts +3 -2
  3. package/dist/components/chip/Chip.js +2 -1
  4. package/dist/components/chip/Chip.module.css +107 -67
  5. package/dist/components/code-block/CodeBlock.js +28 -19
  6. package/dist/components/code-block/CodeBlock.module.css +69 -67
  7. package/dist/components/filter-field/FilterField.d.ts +2 -1
  8. package/dist/components/filter-field/FilterField.js +4 -9
  9. package/dist/components/filter-field/FilterField.module.css +203 -152
  10. package/dist/components/filtering/chip-multi-toggle/ChipMultiToggle.d.ts +2 -3
  11. package/dist/components/filtering/chip-multi-toggle/ChipMultiToggle.js +3 -5
  12. package/dist/components/filtering/chip-multi-toggle/ChipMultiToggle.module.css +13 -13
  13. package/dist/components/forms/input/Input.d.ts +1 -1
  14. package/dist/components/forms/input/Input.js +3 -9
  15. package/dist/components/forms/input/Input.module.css +106 -17
  16. package/dist/components/hyperlink/Hyperlink.module.css +14 -3
  17. package/dist/components/interval-select/IntervalSelect.js +2 -2
  18. package/dist/components/overlay/modal/Modal.d.ts +2 -1
  19. package/dist/components/overlay/modal/Modal.js +6 -6
  20. package/dist/components/overlay/modal/Modal.module.css +52 -19
  21. package/dist/components/popover/Popover.module.css +4 -1
  22. package/dist/components/segmented-progress-bar/SegmentedProgressBar.d.ts +4 -2
  23. package/dist/components/segmented-progress-bar/SegmentedProgressBar.js +17 -19
  24. package/dist/components/segmented-progress-bar/SegmentedProgressBar.module.css +128 -20
  25. package/dist/components/sidebar/Sidebar.d.ts +2 -1
  26. package/dist/components/sidebar/Sidebar.js +2 -2
  27. package/dist/components/sidebar/components/sidebar-container/SidebarContainer.d.ts +2 -3
  28. package/dist/components/sidebar/components/sidebar-container/SidebarContainer.js +2 -4
  29. package/dist/components/sidebar/components/sidebar-container/SidebarContainer.module.css +2 -1
  30. package/dist/components/table/Table.d.ts +6 -6
  31. package/dist/components/table/Table.js +153 -78
  32. package/dist/components/table/Table.module.css +335 -171
  33. package/dist/components/table/TanstackTable.d.ts +1 -1
  34. package/dist/components/table/TanstackTable.js +14 -12
  35. package/dist/components/table/table.utils.d.ts +1 -1
  36. package/dist/components/table/table.utils.js +2 -3
  37. package/dist/components/table/tanstackTable.utils.d.ts +9 -10
  38. package/dist/components/table/tanstackTable.utils.js +50 -30
  39. package/dist/hooks/useViewportFill.d.ts +6 -5
  40. package/dist/hooks/useViewportFill.js +43 -41
  41. package/dist/src/styles/styles.css +9 -3
  42. package/dist/styles/styles.css +9 -3
  43. package/dist/styles/themes/dbc/base.css +0 -3
  44. package/dist/styles/themes/dbc/dark.css +44 -12
  45. package/dist/styles/themes/dbc/light.css +33 -7
  46. package/package.json +1 -1
@@ -1,11 +1,9 @@
1
1
  /* Root control group */
2
2
  .container {
3
- /* inline-flex makes width behave weird in parents */
4
3
  display: inline-flex;
5
4
  align-items: stretch;
6
5
  flex-grow: 1;
7
6
  gap: 0;
8
- /* width control */
9
7
  inline-size: var(--input-width, auto);
10
8
  min-inline-size: var(--input-min-width, 0);
11
9
  max-inline-size: var(--input-max-width, none);
@@ -24,21 +22,23 @@
24
22
  display: flex;
25
23
  align-items: center;
26
24
  flex: 1 1 auto;
27
- min-inline-size: 0; /* critical for ellipsis inside flex */
25
+ min-inline-size: 0;
28
26
  color: var(--color-fg-default);
29
27
  }
30
28
 
31
29
  /* Actual input */
32
30
  .input {
33
31
  flex: 1 1 auto;
34
- min-inline-size: 0; /* critical */
32
+ min-inline-size: 0;
35
33
  inline-size: 100%;
34
+ box-sizing: border-box;
35
+ text-overflow: ellipsis;
36
+
37
+ color: var(--color-fg-default);
36
38
  background: var(--color-bg-surface);
37
39
  font-family: var(--font-family);
38
40
  font-size: var(--font-size-sm);
39
41
  line-height: var(--line-height-normal);
40
- box-sizing: border-box;
41
- text-overflow: ellipsis;
42
42
 
43
43
  border: var(--border-width-thin) solid var(--color-border-default);
44
44
  border-radius: var(--border-radius-default);
@@ -49,7 +49,8 @@
49
49
  transition:
50
50
  background-color var(--transition-fast) var(--ease-standard),
51
51
  border-color var(--transition-fast) var(--ease-standard),
52
- box-shadow var(--transition-fast) var(--ease-standard);
52
+ box-shadow var(--transition-fast) var(--ease-standard),
53
+ color var(--transition-fast) var(--ease-standard);
53
54
  }
54
55
 
55
56
  .input::placeholder {
@@ -58,11 +59,13 @@
58
59
 
59
60
  .input:disabled {
60
61
  background-color: var(--color-disabled-bg);
61
- border: 0;
62
+ border-color: transparent;
62
63
  color: var(--color-disabled-fg);
63
64
  cursor: not-allowed;
64
- opacity: 0.5;
65
+ opacity: 1;
66
+ box-shadow: none;
65
67
  }
68
+
66
69
  /* Button group styling */
67
70
  .withButton .input {
68
71
  border-top-right-radius: 0;
@@ -74,27 +77,99 @@
74
77
  padding-inline-end: calc(var(--spacing-md) + 28px);
75
78
  }
76
79
 
77
- /* Hover & focus */
78
- .input:hover {
80
+ /* Global focus reset - variants own visible focus treatment */
81
+ .input:focus-visible {
82
+ outline: none;
83
+ }
84
+
85
+ /* =========================
86
+ Variants
87
+ ========================= */
88
+
89
+ /* Stronger field chrome for forms/settings */
90
+ .outlined {
91
+ background-color: var(--color-bg-surface);
92
+ border-color: var(--color-border-default);
93
+ }
94
+
95
+ .outlined:hover:not(:disabled) {
79
96
  border-color: var(--color-border-strong);
80
97
  }
81
98
 
82
- .input:focus-visible {
99
+ .outlined:focus-visible:not(:disabled) {
83
100
  border-color: var(--color-border-selected);
101
+ box-shadow: inset 0 0 0 1px var(--color-border-selected);
84
102
  }
85
103
 
86
- /* Variants */
87
- .filled {
104
+ .surface {
88
105
  background-color: var(--color-bg-surface);
106
+ border: 1px solid var(--color-border-subtle);
107
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
108
+ }
109
+
110
+ .surface:hover:not(:disabled) {
111
+ border-color: var(--color-border-default);
112
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
113
+ }
114
+
115
+ .surface:focus-visible:not(:disabled) {
116
+ border-color: var(--color-border-selected);
117
+ box-shadow: inset 0 0 0 1px var(--color-border-selected);
118
+ }
119
+
120
+ /* Old filled/tinted behavior, now renamed subtle */
121
+ .subtle {
122
+ background-color: var(--color-bg-toolbar);
123
+ border-color: transparent;
124
+ box-shadow: inset 0 0 0 1px transparent;
125
+ }
126
+
127
+ .subtle:hover:not(:disabled) {
128
+ background-color: var(--color-bg-toolbar-hover);
89
129
  }
130
+
131
+ .subtle:focus-visible:not(:disabled) {
132
+ border-color: var(--color-border-selected);
133
+ box-shadow: inset 0 0 0 1px var(--color-border-selected);
134
+ }
135
+
136
+ /* Embedded = chromeless input for grouped controls like FilterField */
137
+ .embedded {
138
+ background-color: transparent;
139
+ border-color: transparent;
140
+ border-radius: 0;
141
+ box-shadow: none;
142
+ padding-inline: calc(var(--spacing-sm) + var(--spacing-2xs));
143
+ padding-block: 0;
144
+ }
145
+
146
+ .embedded:hover:not(:disabled),
147
+ .embedded:focus:not(:disabled),
148
+ .embedded:focus-visible:not(:disabled) {
149
+ background-color: transparent;
150
+ border-color: transparent;
151
+ box-shadow: none;
152
+ outline: none;
153
+ }
154
+
90
155
  .standalone {
91
- border-radius: var(--border-radius-rounded);
92
156
  background-color: var(--color-bg-surface);
157
+ border-color: var(--color-border-default);
158
+ border-radius: var(--border-radius-rounded);
93
159
  box-shadow: var(--shadow-xs), var(--shadow-md);
94
160
  }
95
161
 
96
- .outlined {
97
- background-color: transparent;
162
+ .standalone:hover:not(:disabled) {
163
+ border-color: var(--color-border-strong);
164
+ box-shadow: var(--shadow-sm), var(--shadow-md);
165
+ }
166
+
167
+ .standalone:focus-visible:not(:disabled) {
168
+ border-color: var(--color-border-selected);
169
+ box-shadow:
170
+ var(--shadow-xs),
171
+ var(--shadow-md),
172
+ inset 0 0 0 1px var(--color-border-selected);
98
173
  }
99
174
 
100
175
  /* Sizes */
@@ -103,14 +178,17 @@
103
178
  font-size: var(--font-size-sm);
104
179
  padding: 0 var(--spacing-xxs);
105
180
  }
181
+
106
182
  .sm {
107
183
  block-size: var(--component-size-sm);
108
184
  font-size: var(--font-size-sm);
109
185
  }
186
+
110
187
  .md {
111
188
  block-size: var(--component-size-md);
112
189
  font-size: var(--font-size-sm);
113
190
  }
191
+
114
192
  .lg {
115
193
  block-size: var(--component-size-lg);
116
194
  font-size: var(--font-size-lg);
@@ -121,6 +199,10 @@
121
199
  padding-inline-start: calc(var(--icon-size-md) + var(--spacing-lg));
122
200
  }
123
201
 
202
+ .embedded.inputWithIcon {
203
+ padding-inline-start: calc(var(--icon-size-md) + var(--spacing-xl));
204
+ }
205
+
124
206
  .icon {
125
207
  position: absolute;
126
208
  inset-inline-start: var(--spacing-md);
@@ -133,7 +215,13 @@
133
215
  block-size: var(--icon-size-md);
134
216
  pointer-events: none;
135
217
  color: var(--color-fg-subtle);
218
+ transition: color var(--transition-fast) var(--ease-standard);
136
219
  }
220
+
221
+ .field:focus-within .icon {
222
+ color: var(--color-fg-muted);
223
+ }
224
+
137
225
  .icon svg {
138
226
  inline-size: var(--icon-size-md);
139
227
  block-size: var(--icon-size-md);
@@ -147,6 +235,7 @@
147
235
  border-left: var(--border-width-thin) solid transparent;
148
236
  margin-left: calc(-1 * var(--border-width-thin));
149
237
  }
238
+
150
239
  .trailingButton:hover {
151
240
  border-color: var(--color-border-default);
152
241
  z-index: 2;
@@ -36,12 +36,24 @@
36
36
  }
37
37
  }
38
38
 
39
+ .link {
40
+ position: relative;
41
+ display: inline-block;
42
+ max-width: 100%;
43
+ }
44
+
45
+ .link {
46
+ position: relative;
47
+ display: inline-block;
48
+ max-width: 100%;
49
+ }
50
+
39
51
  .link::after {
40
52
  content: '';
41
53
  position: absolute;
42
54
  left: 0;
43
- bottom: -2px;
44
- width: 100%;
55
+ right: 0;
56
+ bottom: 1px;
45
57
  height: 1px;
46
58
  background-color: currentColor;
47
59
  transform: scaleX(0);
@@ -52,7 +64,6 @@
52
64
  .link:hover::after {
53
65
  transform: scaleX(1);
54
66
  }
55
-
56
67
  .link:focus-visible {
57
68
  outline: 2px solid var(--color-brand);
58
69
  outline-offset: 2px;
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { Check, Clock } from 'lucide-react';
3
+ import { Check } from 'lucide-react';
4
4
  import { useEffect, useId, useMemo, useRef, useState } from 'react';
5
5
  import { Button } from '../../components/button/Button';
6
6
  import { ClearButton } from '../../components/clear-button/ClearButton';
@@ -87,7 +87,7 @@ id, options, selectedValue, onChange, baseDate, placeholder = 'Vælg interval',
87
87
  // Reset active to selected when opening
88
88
  setActiveIndex(selectedIndex >= 0 ? selectedIndex : 0);
89
89
  onClick(e);
90
- }, size: size, type: "button", "aria-haspopup": "listbox", "aria-expanded": !!isOpen, "aria-controls": listboxId, "aria-invalid": Boolean(error) || undefined, "aria-describedby": describedBy, children: _jsxs("span", { className: "dbc-flex dbc-justify-between dbc-items-center dbc-gap-xxs", style: { width: '100%' }, children: [_jsxs("span", { className: "dbc-flex dbc-items-center dbc-gap-xxs", children: [_jsx(Clock, { size: 14 }), selected ? selected.label : placeholder] }), onClear && selected && _jsx(ClearButton, { onClick: onClear }), icon] }) })), children: _jsx(Menu, { id: listboxId, onKeyDown: handleKeyDown, role: "listbox", children: options.map((opt, index) => {
90
+ }, size: size, type: "button", "aria-haspopup": "listbox", "aria-expanded": !!isOpen, "aria-controls": listboxId, "aria-invalid": Boolean(error) || undefined, "aria-describedby": describedBy, children: _jsxs("span", { className: "dbc-flex dbc-justify-between dbc-items-center dbc-gap-xxs", style: { width: '100%' }, children: [_jsx("span", { className: "dbc-flex dbc-items-center dbc-gap-xxs", children: selected ? selected.label : placeholder }), onClear && selected && _jsx(ClearButton, { onClick: onClear }), icon] }) })), children: _jsx(Menu, { id: listboxId, onKeyDown: handleKeyDown, role: "listbox", children: options.map((opt, index) => {
91
91
  const isSelected = opt.minutesAgo === selectedValue;
92
92
  const isActive = index === activeIndex;
93
93
  return (_jsx(Menu.Item, { active: isActive,
@@ -19,5 +19,6 @@ export type ModalProps = {
19
19
  severity?: Severity;
20
20
  disableContentSpacing?: boolean;
21
21
  dataCy?: string;
22
+ width?: number | string;
22
23
  };
23
- export declare function Modal({ isOpen, onRequestClose, header, content, children, primaryAction, secondaryAction, closeOnOverlayClick, severity, disableContentSpacing, isLoading, dataCy, }: ModalProps): React.ReactNode;
24
+ export declare function Modal({ isOpen, onRequestClose, header, content, children, primaryAction, secondaryAction, closeOnOverlayClick, severity, disableContentSpacing, isLoading, dataCy, width, }: ModalProps): React.ReactNode;
@@ -5,7 +5,7 @@ import { useEffect, useId, useRef } from 'react';
5
5
  import { Button } from '../../../components/button/Button';
6
6
  import { Headline } from '../../../components/headline/Headline';
7
7
  import styles from './Modal.module.css';
8
- export function Modal({ isOpen, onRequestClose, header, content, children, primaryAction, secondaryAction, closeOnOverlayClick = true, severity, disableContentSpacing = false, isLoading, dataCy, }) {
8
+ export function Modal({ isOpen, onRequestClose, header, content, children, primaryAction, secondaryAction, closeOnOverlayClick = true, severity, disableContentSpacing = false, isLoading, dataCy, width, }) {
9
9
  const titleId = useId();
10
10
  const dialogRef = useRef(null);
11
11
  const lastActiveElementRef = useRef(null);
@@ -30,7 +30,6 @@ export function Modal({ isOpen, onRequestClose, header, content, children, prima
30
30
  const focusableSelectors = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
31
31
  const focusable = dialog.querySelectorAll(focusableSelectors);
32
32
  if (focusable.length > 0) {
33
- // Prefer focusing the first input/select/textarea if present
34
33
  const preferred = (_a = dialog.querySelector('input, select, textarea')) !== null && _a !== void 0 ? _a : focusable[0];
35
34
  preferred.focus();
36
35
  }
@@ -68,13 +67,11 @@ export function Modal({ isOpen, onRequestClose, header, content, children, prima
68
67
  document.addEventListener('keydown', handleKeyDown);
69
68
  return () => {
70
69
  document.removeEventListener('keydown', handleKeyDown);
71
- // Restore focus only when closing (true -> false) happens elsewhere
72
- // so we do it when modal unmounts while open.
73
70
  if (lastActiveElementRef.current) {
74
71
  lastActiveElementRef.current.focus();
75
72
  }
76
73
  };
77
- }, [isOpen]); // <-- IMPORTANT: only depend on isOpen
74
+ }, [isOpen]);
78
75
  if (!isOpen)
79
76
  return null;
80
77
  const handleOverlayClick = () => {
@@ -88,5 +85,8 @@ export function Modal({ isOpen, onRequestClose, header, content, children, prima
88
85
  const resolvedSecondaryAction = secondaryAction !== null && secondaryAction !== void 0 ? secondaryAction : (primaryAction ? { label: 'Luk', onClick: onRequestCloseRef.current } : undefined);
89
86
  const shouldRenderFooter = Boolean(primaryAction || resolvedSecondaryAction);
90
87
  const body = children !== null && children !== void 0 ? children : content;
91
- return (_jsx("div", { className: styles.overlay, onClick: handleOverlayClick, children: _jsxs("div", { "data-cy": dataCy, ref: dialogRef, className: `${styles.modal} ${disableContentSpacing ? '' : styles.contentSpacing}`, onClick: stopPropagation, role: "dialog", "aria-modal": "true", "aria-labelledby": header ? titleId : undefined, children: [_jsxs("div", { className: styles.header, children: [header && (_jsx(Headline, { severity: severity, size: 3, disableMargin: true, children: header })), _jsx(Button, { type: "button", variant: "inline", onClick: () => onRequestCloseRef.current(), "aria-label": "Luk", shape: "round", icon: _jsx(X, {}) })] }), _jsx("div", { className: styles.body, children: body }), shouldRenderFooter && (_jsxs("div", { className: styles.footer, children: [resolvedSecondaryAction && (_jsxs(Button, { type: "button", variant: "outlined", onClick: resolvedSecondaryAction.onClick, disabled: isLoading, children: [resolvedSecondaryAction.icon && (_jsx("span", { className: styles.icon, children: resolvedSecondaryAction.icon })), _jsx("span", { children: resolvedSecondaryAction.label })] })), primaryAction && (_jsxs(Button, { type: "button", variant: "primary", onClick: primaryAction.onClick, disabled: primaryAction.disabled || isLoading, loading: isLoading, children: [primaryAction.icon && _jsx("span", { className: styles.icon, children: primaryAction.icon }), _jsx("span", { children: primaryAction.label })] }))] }))] }) }));
88
+ const resolvedWidth = typeof width === 'number' ? `${width}px` : typeof width === 'string' ? width : undefined;
89
+ return (_jsx("div", { className: styles.overlay, onClick: handleOverlayClick, children: _jsxs("div", { "data-cy": dataCy, ref: dialogRef, className: `${styles.modal} ${disableContentSpacing ? '' : styles.contentSpacing}`, style: resolvedWidth
90
+ ? { ['--modal-width']: resolvedWidth }
91
+ : undefined, onClick: stopPropagation, role: "dialog", "aria-modal": "true", "aria-labelledby": header ? titleId : undefined, tabIndex: -1, children: [_jsxs("div", { className: styles.header, children: [header && (_jsx(Headline, { severity: severity, size: 3, disableMargin: true, children: header })), _jsx(Button, { type: "button", variant: "inline", onClick: () => onRequestCloseRef.current(), "aria-label": "Luk", shape: "round", icon: _jsx(X, {}) })] }), _jsx("div", { className: styles.body, children: body }), shouldRenderFooter && (_jsxs("div", { className: styles.footer, children: [resolvedSecondaryAction && (_jsxs(Button, { type: "button", variant: "outlined", onClick: resolvedSecondaryAction.onClick, disabled: isLoading, children: [resolvedSecondaryAction.icon && (_jsx("span", { className: styles.icon, children: resolvedSecondaryAction.icon })), _jsx("span", { children: resolvedSecondaryAction.label })] })), primaryAction && (_jsxs(Button, { type: "button", variant: "primary", onClick: primaryAction.onClick, disabled: primaryAction.disabled || isLoading, loading: isLoading, children: [primaryAction.icon && _jsx("span", { className: styles.icon, children: primaryAction.icon }), _jsx("span", { children: primaryAction.label })] }))] }))] }) }));
92
92
  }
@@ -2,62 +2,87 @@
2
2
  position: fixed;
3
3
  inset: 0;
4
4
  background: var(--overlay-scrim);
5
+
5
6
  display: flex;
6
- align-items: flex-start;
7
7
  justify-content: center;
8
- padding-top: clamp(var(--spacing-md), 12vh, 24vh);
9
- padding-bottom: var(--spacing-md);
8
+
9
+ padding: clamp(var(--spacing-sm), 10vh, var(--spacing-xl));
10
10
  z-index: var(--z-backdrop-modal);
11
+
12
+ /* Overlay can scroll if modal is taller than viewport */
11
13
  overflow-y: auto;
12
14
  }
13
15
 
16
+ /* Default width can be overridden by --modal-width from props */
14
17
  .modal {
18
+ --modal-width: 700px;
19
+
15
20
  background: var(--color-bg-surface);
16
21
  border-radius: var(--border-radius-lg);
17
- min-width: 320px;
18
- max-width: 700px;
19
- max-height: calc(100vh - (2 * var(--spacing-md)));
20
- display: flex;
21
- flex-direction: column;
22
22
  box-shadow: var(--shadow-lg);
23
23
  font-family: var(--font-family);
24
- min-width: 500px;
25
- z-index: var(--z-modal);
26
24
  color: var(--color-fg-default);
27
- }
25
+ z-index: var(--z-modal);
26
+
27
+ /* Responsive width: never exceed viewport */
28
+ width: min(
29
+ var(--modal-width),
30
+ calc(100vw - 2 * clamp(var(--spacing-sm), 4vw, var(--spacing-lg)))
31
+ );
32
+ min-width: 320px;
33
+
34
+ /* Critical: prevent “below bottom of screen”
35
+ Prefer svh on mobile; fallback to vh. */
36
+ max-height: calc(100svh - 2 * clamp(var(--spacing-sm), 10vh, var(--spacing-xl)));
37
+ max-height: calc(100vh - 2 * clamp(var(--spacing-sm), 10vh, var(--spacing-xl)));
38
+
39
+ display: flex;
40
+ flex-direction: column;
28
41
 
29
- /* Slightly more relaxed on small screens */
30
- @media (max-width: var(--bp-sm)) {
31
- .modal {
32
- width: calc(100% - 2 * var(--spacing-md));
33
- max-width: 100%;
34
- }
42
+ /* If you want it slightly lower than top padding, keep it aligned start */
43
+ align-self: flex-start;
44
+
45
+ /* Helps on iOS when address bar changes */
46
+ overscroll-behavior: contain;
35
47
  }
36
48
 
49
+ /* Header/footer pinned; body scrolls */
37
50
  .header {
38
51
  font-size: var(--font-size-md);
39
52
  font-weight: var(--font-weight-semibold);
53
+
40
54
  display: flex;
41
55
  justify-content: space-between;
42
56
  align-items: center;
57
+
43
58
  padding: var(--spacing-md);
44
59
  padding-bottom: 0;
60
+
61
+ /* Keeps header readable if body scrolls underneath */
62
+ flex: 0 0 auto;
45
63
  }
46
64
 
47
65
  .body {
48
- overflow-y: auto;
66
+ flex: 1 1 auto;
67
+ overflow: auto;
68
+
49
69
  font-size: var(--font-size-sm);
50
70
  line-height: var(--line-height-normal);
51
71
  color: var(--color-fg-muted);
72
+
52
73
  padding: var(--spacing-md);
74
+ min-height: 0; /* IMPORTANT: allows flex child to actually scroll */
53
75
  }
54
76
 
55
77
  .footer {
78
+ flex: 0 0 auto;
79
+
56
80
  display: flex;
57
81
  justify-content: flex-end;
58
82
  gap: var(--spacing-xs);
59
- padding-top: 0;
83
+
60
84
  padding: var(--spacing-md);
85
+ padding-top: 0;
61
86
  }
62
87
 
63
88
  .icon {
@@ -65,3 +90,11 @@
65
90
  align-items: center;
66
91
  justify-content: center;
67
92
  }
93
+
94
+ .contentSpacing .body > :first-child {
95
+ margin-top: 0;
96
+ }
97
+
98
+ .contentSpacing .body > :last-child {
99
+ margin-bottom: 0;
100
+ }
@@ -1,6 +1,9 @@
1
1
  .container {
2
2
  position: relative;
3
- display: inline-block;
3
+ display: inline-flex;
4
+ align-items: stretch;
5
+ height: 100%;
6
+ min-width: 0;
4
7
  }
5
8
 
6
9
  .content {
@@ -1,9 +1,10 @@
1
1
  import React from 'react';
2
2
  import { TooltipPlacement } from '../../components/overlay/tooltip/TooltipProvider';
3
3
  import type { Severity } from '../../constants/severity.types';
4
+ type ProgressSeverity = 'done' | 'progress' | 'missing';
4
5
  export type Segment = {
5
6
  value: number;
6
- severity?: Severity;
7
+ severity?: Severity | ProgressSeverity;
7
8
  color?: string;
8
9
  label?: React.ReactNode;
9
10
  };
@@ -11,7 +12,7 @@ export interface SegmentedProgressBarProps {
11
12
  segments: Segment[];
12
13
  total?: number;
13
14
  showRemainder?: boolean;
14
- remainderSeverity?: Severity;
15
+ remainderSeverity?: Severity | ProgressSeverity;
15
16
  centerLabel?: React.ReactNode | ((filledPct: number) => React.ReactNode);
16
17
  height?: number;
17
18
  rounded?: boolean;
@@ -22,3 +23,4 @@ export interface SegmentedProgressBarProps {
22
23
  'data-testid'?: string;
23
24
  }
24
25
  export declare const SegmentedProgressBar: React.FC<SegmentedProgressBarProps>;
26
+ export {};
@@ -1,13 +1,10 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useTooltipTrigger } from '../../components/overlay/tooltip/useTooltipTrigger';
4
- import { SeverityBgColor, SeverityTextColor } from '../../constants/severity';
5
4
  import styles from './SegmentedProgressBar.module.css';
6
5
  function SegmentWithTooltip({ seg, index, tooltipPlacement, }) {
7
- var _a, _b, _c, _d, _e;
8
- const bg = (_a = seg.color) !== null && _a !== void 0 ? _a : SeverityBgColor[(_b = seg.severity) !== null && _b !== void 0 ? _b : 'neutral'];
9
- const fg = SeverityTextColor[(_c = seg.severity) !== null && _c !== void 0 ? _c : 'neutral'];
10
- const tooltipContent = typeof seg.label !== 'undefined' ? seg.label : String((_d = seg.value) !== null && _d !== void 0 ? _d : 0);
6
+ var _a, _b;
7
+ const tooltipContent = typeof seg.label !== 'undefined' ? seg.label : String((_a = seg.value) !== null && _a !== void 0 ? _a : 0);
11
8
  const tooltipEnabled = tooltipContent !== null && tooltipContent !== undefined && tooltipContent !== '';
12
9
  const { triggerProps } = useTooltipTrigger({
13
10
  content: tooltipEnabled ? tooltipContent : null,
@@ -16,11 +13,13 @@ function SegmentWithTooltip({ seg, index, tooltipPlacement, }) {
16
13
  });
17
14
  return (_jsx("span", { className: styles.segmentWrapper, style: {
18
15
  flex: `${Math.max(seg.value || 0, 0)} 0 0`,
19
- height: '100%',
20
- minWidth: '2px',
21
- }, ...(tooltipEnabled ? triggerProps : {}), children: _jsx("div", { className: styles.progressSegment, "data-index": index, "data-severity": (_e = seg.severity) !== null && _e !== void 0 ? _e : 'neutral', style: { background: bg, color: fg } }) }));
16
+ }, ...(tooltipEnabled ? triggerProps : {}), children: _jsx("span", { className: styles.progressSegment, "data-index": index, "data-severity": (_b = seg.severity) !== null && _b !== void 0 ? _b : 'neutral', style: seg.color
17
+ ? {
18
+ ['--segment-color']: seg.color,
19
+ }
20
+ : undefined }) }));
22
21
  }
23
- export const SegmentedProgressBar = ({ segments, total, showRemainder = true, remainderSeverity = 'neutral', centerLabel, height, rounded = true, trackColor = 'var(--color-neutral)', tooltipPlacement = 'top', className, ...aria }) => {
22
+ export const SegmentedProgressBar = ({ segments, total, showRemainder = true, remainderSeverity = 'neutral', centerLabel, height, rounded = true, trackColor, tooltipPlacement = 'top', className, ...aria }) => {
24
23
  const sum = segments.reduce((acc, s) => acc + (s.value || 0), 0);
25
24
  const effectiveTotal = total !== null && total !== void 0 ? total : sum;
26
25
  const safeTotal = Math.max(effectiveTotal, 0.0001);
@@ -36,15 +35,14 @@ export const SegmentedProgressBar = ({ segments, total, showRemainder = true, re
36
35
  base.push(remainder);
37
36
  return base.join('/');
38
37
  })();
39
- const rootClass = [styles.container, className].filter(Boolean).join(' ');
40
38
  const filteredSegments = computedSegments.filter(x => x.value > 0);
41
- return (_jsx("div", { className: rootClass, role: "progressbar", "aria-valuemin": 0, "aria-valuemax": effectiveTotal, "aria-valuenow": Math.min(sum, effectiveTotal), ...aria, children: _jsxs("div", { className: styles.progressBar, style: {
42
- height: height ? `${height}px` : `var(--component-size-xs)`,
43
- borderRadius: rounded ? `var(--border-radius-rounded)` : `var(--border-radius-none)`,
44
- background: trackColor,
45
- }, children: [_jsx("span", { className: styles.segmentsContainer, children: filteredSegments.map((seg, i) => (_jsx(SegmentWithTooltip, { seg: seg, index: i, tooltipPlacement: tooltipPlacement }, `${i}`))) }), _jsx("div", { className: `${styles.progressCenter} ${filteredSegments.length === 0 ? styles.emptySegments : ''}`, children: centerLabel
46
- ? typeof centerLabel === 'function'
47
- ? centerLabel(pctFilled)
48
- : centerLabel
49
- : autoNumbers })] }) }));
39
+ const rootClass = [styles.container, className].filter(Boolean).join(' ');
40
+ return (_jsx("div", { className: rootClass, role: "progressbar", "aria-valuemin": 0, "aria-valuemax": effectiveTotal, "aria-valuenow": Math.min(sum, effectiveTotal), ...aria, children: _jsxs("div", { className: styles.progressBar, "data-rounded": rounded ? 'true' : 'false', style: {
41
+ height: height ? `${height}px` : '24px',
42
+ background: trackColor !== null && trackColor !== void 0 ? trackColor : 'var(--color-bg-surface-subtle)',
43
+ }, children: [_jsx("span", { className: styles.segmentsContainer, children: filteredSegments.map((seg, i) => (_jsx(SegmentWithTooltip, { seg: seg, index: i, tooltipPlacement: tooltipPlacement }, `${i}`))) }), _jsx("div", { className: `${styles.progressCenter} ${filteredSegments.length === 0 ? styles.emptySegments : ''}`, children: _jsx("span", { className: styles.progressCenterLabel, children: centerLabel
44
+ ? typeof centerLabel === 'function'
45
+ ? centerLabel(pctFilled)
46
+ : centerLabel
47
+ : autoNumbers }) })] }) }));
50
48
  };