@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.
- package/dist/components/button/Button.module.css +21 -10
- package/dist/components/chip/Chip.d.ts +3 -2
- package/dist/components/chip/Chip.js +2 -1
- package/dist/components/chip/Chip.module.css +107 -67
- package/dist/components/code-block/CodeBlock.js +28 -19
- package/dist/components/code-block/CodeBlock.module.css +69 -67
- package/dist/components/filter-field/FilterField.d.ts +2 -1
- package/dist/components/filter-field/FilterField.js +4 -9
- package/dist/components/filter-field/FilterField.module.css +203 -152
- package/dist/components/filtering/chip-multi-toggle/ChipMultiToggle.d.ts +2 -3
- package/dist/components/filtering/chip-multi-toggle/ChipMultiToggle.js +3 -5
- package/dist/components/filtering/chip-multi-toggle/ChipMultiToggle.module.css +13 -13
- package/dist/components/forms/input/Input.d.ts +1 -1
- package/dist/components/forms/input/Input.js +3 -9
- package/dist/components/forms/input/Input.module.css +106 -17
- package/dist/components/hyperlink/Hyperlink.module.css +14 -3
- package/dist/components/interval-select/IntervalSelect.js +2 -2
- package/dist/components/overlay/modal/Modal.d.ts +2 -1
- package/dist/components/overlay/modal/Modal.js +6 -6
- package/dist/components/overlay/modal/Modal.module.css +52 -19
- package/dist/components/popover/Popover.module.css +4 -1
- package/dist/components/segmented-progress-bar/SegmentedProgressBar.d.ts +4 -2
- package/dist/components/segmented-progress-bar/SegmentedProgressBar.js +17 -19
- package/dist/components/segmented-progress-bar/SegmentedProgressBar.module.css +128 -20
- package/dist/components/sidebar/Sidebar.d.ts +2 -1
- package/dist/components/sidebar/Sidebar.js +2 -2
- package/dist/components/sidebar/components/sidebar-container/SidebarContainer.d.ts +2 -3
- package/dist/components/sidebar/components/sidebar-container/SidebarContainer.js +2 -4
- package/dist/components/sidebar/components/sidebar-container/SidebarContainer.module.css +2 -1
- package/dist/components/table/Table.d.ts +6 -6
- package/dist/components/table/Table.js +153 -78
- package/dist/components/table/Table.module.css +335 -171
- package/dist/components/table/TanstackTable.d.ts +1 -1
- package/dist/components/table/TanstackTable.js +14 -12
- package/dist/components/table/table.utils.d.ts +1 -1
- package/dist/components/table/table.utils.js +2 -3
- package/dist/components/table/tanstackTable.utils.d.ts +9 -10
- package/dist/components/table/tanstackTable.utils.js +50 -30
- package/dist/hooks/useViewportFill.d.ts +6 -5
- package/dist/hooks/useViewportFill.js +43 -41
- package/dist/src/styles/styles.css +9 -3
- package/dist/styles/styles.css +9 -3
- package/dist/styles/themes/dbc/base.css +0 -3
- package/dist/styles/themes/dbc/dark.css +44 -12
- package/dist/styles/themes/dbc/light.css +33 -7
- 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;
|
|
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;
|
|
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:
|
|
62
|
+
border-color: transparent;
|
|
62
63
|
color: var(--color-disabled-fg);
|
|
63
64
|
cursor: not-allowed;
|
|
64
|
-
opacity:
|
|
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
|
-
/*
|
|
78
|
-
.input:
|
|
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
|
-
.
|
|
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
|
-
|
|
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
|
-
.
|
|
97
|
-
|
|
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
|
-
|
|
44
|
-
|
|
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
|
|
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: [
|
|
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]);
|
|
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
|
-
|
|
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
|
-
|
|
9
|
-
padding
|
|
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
|
-
/*
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,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
|
|
8
|
-
const
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
};
|