@dbcdk/react-components 0.0.108 → 0.0.110

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 (35) hide show
  1. package/dist/client.cjs +14 -0
  2. package/dist/client.d.ts +2 -0
  3. package/dist/client.js +2 -0
  4. package/dist/components/button/Button.module.css +3 -0
  5. package/dist/components/card/Card.module.css +4 -1
  6. package/dist/components/forms/file-upload/FileUpload.cjs +117 -0
  7. package/dist/components/forms/file-upload/FileUpload.d.ts +15 -0
  8. package/dist/components/forms/file-upload/FileUpload.js +111 -0
  9. package/dist/components/forms/file-upload/FileUpload.module.css +75 -0
  10. package/dist/components/lightbox/Lightbox.cjs +219 -0
  11. package/dist/components/lightbox/Lightbox.d.ts +21 -0
  12. package/dist/components/lightbox/Lightbox.js +213 -0
  13. package/dist/components/lightbox/Lightbox.module.css +161 -0
  14. package/dist/components/menu/Menu.cjs +42 -5
  15. package/dist/components/menu/Menu.d.ts +3 -0
  16. package/dist/components/menu/Menu.js +42 -5
  17. package/dist/components/metric-tile/MetricTile.cjs +9 -2
  18. package/dist/components/metric-tile/MetricTile.d.ts +4 -2
  19. package/dist/components/metric-tile/MetricTile.js +9 -2
  20. package/dist/components/metric-tile/MetricTile.module.css +19 -15
  21. package/dist/components/overlay/tooltip/Tooltip.cjs +30 -8
  22. package/dist/components/overlay/tooltip/Tooltip.d.ts +4 -5
  23. package/dist/components/overlay/tooltip/Tooltip.js +31 -9
  24. package/dist/components/table/Table.cjs +3 -1
  25. package/dist/components/table/Table.d.ts +1 -1
  26. package/dist/components/table/Table.js +3 -1
  27. package/dist/components/table/Table.module.css +29 -4
  28. package/dist/components/table/Table.types.d.ts +1 -0
  29. package/dist/components/table/components/TableRow.cjs +9 -2
  30. package/dist/components/table/components/TableRow.js +9 -2
  31. package/dist/index.css +2 -0
  32. package/dist/styles/styles.css +4 -0
  33. package/dist/styles/themes/dbc/colors.css +27 -0
  34. package/dist/styles.css +4 -0
  35. package/package.json +1 -1
@@ -0,0 +1,213 @@
1
+ 'use client';
2
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
3
+ import { Download, X, ChevronLeft, ChevronRight } from 'lucide-react';
4
+ import { useRef, useState, useEffect, useCallback } from 'react';
5
+ import { MetaBar } from '../../client';
6
+ import styles from './Lightbox.module.css';
7
+ import { Button } from '../button/Button';
8
+
9
+ function extFromMime(mime) {
10
+ var _a;
11
+ const map = {
12
+ "image/jpeg": "jpg",
13
+ "image/png": "png",
14
+ "image/webp": "webp",
15
+ "image/gif": "gif"
16
+ };
17
+ return (_a = mime && map[mime]) != null ? _a : "jpg";
18
+ }
19
+ function Lightbox({
20
+ items,
21
+ title,
22
+ initialIndex = 0,
23
+ thumbnailHeight = 300,
24
+ showDownload = true,
25
+ getDownloadFilename,
26
+ buttonAddition,
27
+ trigger
28
+ }) {
29
+ var _a;
30
+ const dialogRef = useRef(null);
31
+ const [open, setOpen] = useState(false);
32
+ const [index, setIndex] = useState(initialIndex);
33
+ const [downloading, setDownloading] = useState(false);
34
+ const current = items[index];
35
+ const multiple = items.length > 1;
36
+ useEffect(() => {
37
+ setIndex((i) => Math.min(Math.max(i, 0), Math.max(items.length - 1, 0)));
38
+ }, [items.length]);
39
+ const openAt = useCallback((i = 0) => {
40
+ setIndex(i);
41
+ setOpen(true);
42
+ }, []);
43
+ const close = useCallback(() => {
44
+ var _a2;
45
+ setDownloading(false);
46
+ (_a2 = dialogRef.current) == null ? void 0 : _a2.close();
47
+ setOpen(false);
48
+ }, []);
49
+ const prev = useCallback(() => {
50
+ setIndex((i) => (i - 1 + items.length) % items.length);
51
+ }, [items.length]);
52
+ const next = useCallback(() => {
53
+ setIndex((i) => (i + 1) % items.length);
54
+ }, [items.length]);
55
+ useEffect(() => {
56
+ const dialog = dialogRef.current;
57
+ if (!dialog) return;
58
+ if (open && !dialog.open) dialog.showModal();
59
+ else if (!open && dialog.open) dialog.close();
60
+ }, [open]);
61
+ useEffect(() => {
62
+ const dialog = dialogRef.current;
63
+ if (!dialog) return;
64
+ const onCancel = (e) => {
65
+ e.preventDefault();
66
+ close();
67
+ };
68
+ const onClose = () => {
69
+ setOpen(false);
70
+ setDownloading(false);
71
+ };
72
+ dialog.addEventListener("cancel", onCancel);
73
+ dialog.addEventListener("close", onClose);
74
+ return () => {
75
+ dialog.removeEventListener("cancel", onCancel);
76
+ dialog.removeEventListener("close", onClose);
77
+ };
78
+ }, [close]);
79
+ useEffect(() => {
80
+ if (!open || !multiple) return;
81
+ const onKeyDown = (e) => {
82
+ if (e.key === "ArrowLeft") prev();
83
+ if (e.key === "ArrowRight") next();
84
+ };
85
+ window.addEventListener("keydown", onKeyDown);
86
+ return () => window.removeEventListener("keydown", onKeyDown);
87
+ }, [open, multiple, prev, next]);
88
+ const download = useCallback(async () => {
89
+ var _a2;
90
+ if (!(current == null ? void 0 : current.downloadSrc)) return;
91
+ setDownloading(true);
92
+ try {
93
+ const res = await fetch(current.downloadSrc, { cache: "no-store" });
94
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
95
+ const blob = await res.blob();
96
+ const objectUrl = URL.createObjectURL(blob);
97
+ const ext = extFromMime((_a2 = current.mimeType) != null ? _a2 : blob.type);
98
+ const filename = getDownloadFilename ? getDownloadFilename(current, index) : `image-${index + 1}.${ext}`;
99
+ const a = document.createElement("a");
100
+ a.href = objectUrl;
101
+ a.download = filename;
102
+ document.body.appendChild(a);
103
+ a.click();
104
+ a.remove();
105
+ URL.revokeObjectURL(objectUrl);
106
+ } catch (err) {
107
+ console.warn("[Lightbox] download failed", err);
108
+ } finally {
109
+ setDownloading(false);
110
+ }
111
+ }, [current, index, getDownloadFilename]);
112
+ if (!items.length) return null;
113
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
114
+ trigger ? trigger(openAt) : /* @__PURE__ */ jsx("div", { className: styles.thumbnails, children: items.map((item, i) => {
115
+ var _a2;
116
+ return /* @__PURE__ */ jsxs(
117
+ "button",
118
+ {
119
+ type: "button",
120
+ className: styles.thumbnailBtn,
121
+ onClick: () => openAt(i),
122
+ "aria-label": (_a2 = item.title) != null ? _a2 : `Billede ${i + 1}`,
123
+ children: [
124
+ /* @__PURE__ */ jsx("div", { className: styles.thumbnailImg, style: { height: thumbnailHeight }, children: item.thumbnail }),
125
+ /* @__PURE__ */ jsx("div", { className: styles.thumbnailMeta, children: /* @__PURE__ */ jsx(
126
+ MetaBar,
127
+ {
128
+ items: [
129
+ { label: "H\xF8jde", value: item.height != null ? `${item.height}px` : "-" },
130
+ { label: "Bredde", value: item.width != null ? `${item.width}px` : "-" }
131
+ ]
132
+ }
133
+ ) })
134
+ ]
135
+ },
136
+ i
137
+ );
138
+ }) }),
139
+ /* @__PURE__ */ jsx(
140
+ "dialog",
141
+ {
142
+ ref: dialogRef,
143
+ "aria-label": title != null ? title : "Lightbox",
144
+ tabIndex: -1,
145
+ className: styles.dialog,
146
+ onClick: (e) => {
147
+ if (e.target === e.currentTarget) close();
148
+ },
149
+ children: current && /* @__PURE__ */ jsxs(
150
+ "div",
151
+ {
152
+ className: styles.panel,
153
+ "data-surface": "inverse",
154
+ onClick: (e) => {
155
+ if (e.target === e.currentTarget) close();
156
+ },
157
+ children: [
158
+ /* @__PURE__ */ jsxs("div", { className: styles.toolbar, children: [
159
+ /* @__PURE__ */ jsxs("div", { className: styles.toolbarInfo, children: [
160
+ title && /* @__PURE__ */ jsx("span", { className: styles.titleMain, children: title }),
161
+ current.title && /* @__PURE__ */ jsxs("span", { className: styles.titleItem, children: [
162
+ title ? "\xB7 " : "",
163
+ current.title
164
+ ] }),
165
+ multiple && /* @__PURE__ */ jsxs("span", { className: styles.counter, children: [
166
+ title || current.title ? "\xB7 " : "",
167
+ index + 1,
168
+ "/",
169
+ items.length
170
+ ] })
171
+ ] }),
172
+ /* @__PURE__ */ jsxs("div", { className: styles.toolbarActions, children: [
173
+ buttonAddition && /* @__PURE__ */ jsx("div", { className: styles.buttonAdditionSlot, children: buttonAddition }),
174
+ showDownload && current.downloadSrc && /* @__PURE__ */ jsx(
175
+ Button,
176
+ {
177
+ variant: "outlined",
178
+ size: "sm",
179
+ icon: /* @__PURE__ */ jsx(Download, { size: 16 }),
180
+ onClick: download,
181
+ loading: downloading,
182
+ "aria-label": "Download",
183
+ children: "Download"
184
+ }
185
+ ),
186
+ /* @__PURE__ */ jsx(
187
+ Button,
188
+ {
189
+ variant: "outlined",
190
+ size: "sm",
191
+ icon: /* @__PURE__ */ jsx(X, { size: 16 }),
192
+ onClick: close,
193
+ "aria-label": "Luk",
194
+ children: "Luk"
195
+ }
196
+ )
197
+ ] })
198
+ ] }),
199
+ /* @__PURE__ */ jsx("div", { className: styles.imageArea, children: (_a = current.preview) != null ? _a : current.thumbnail }),
200
+ multiple && /* @__PURE__ */ jsxs("div", { className: styles.nav, children: [
201
+ /* @__PURE__ */ jsx(Button, { variant: "outlined", size: "sm", onClick: prev, "aria-label": "Forrige billede", children: /* @__PURE__ */ jsx(ChevronLeft, { size: 20 }) }),
202
+ /* @__PURE__ */ jsx("span", { className: styles.navHint, children: "\u2190 \u2192 \xB7 Esc" }),
203
+ /* @__PURE__ */ jsx(Button, { variant: "outlined", size: "sm", onClick: next, "aria-label": "N\xE6ste billede", children: /* @__PURE__ */ jsx(ChevronRight, { size: 20 }) })
204
+ ] })
205
+ ]
206
+ }
207
+ )
208
+ }
209
+ )
210
+ ] });
211
+ }
212
+
213
+ export { Lightbox };
@@ -0,0 +1,161 @@
1
+ .thumbnails {
2
+ display: flex;
3
+ flex-wrap: wrap;
4
+ align-items: flex-end;
5
+ gap: var(--spacing-md);
6
+ }
7
+
8
+ .thumbnailBtn {
9
+ background: none;
10
+ border: none;
11
+ padding: 0;
12
+ cursor: pointer;
13
+ display: flex;
14
+ flex-direction: column;
15
+ gap: var(--spacing-xs);
16
+ flex: none;
17
+ text-align: left;
18
+ }
19
+
20
+ .thumbnailImg {
21
+ overflow: hidden;
22
+ border-radius: var(--border-radius-default);
23
+ border: 1px solid var(--color-border-default);
24
+ box-shadow: var(--shadow-sm);
25
+ transition:
26
+ transform var(--transition-fast) var(--ease-standard),
27
+ box-shadow var(--transition-fast) var(--ease-standard);
28
+ }
29
+
30
+ .thumbnailBtn:hover .thumbnailImg {
31
+ transform: scale(1.02);
32
+ }
33
+
34
+ .thumbnailImg > img {
35
+ height: 100%;
36
+ width: auto;
37
+ display: block;
38
+ }
39
+
40
+ .thumbnailMeta {
41
+ display: flex;
42
+ gap: var(--spacing-2xs);
43
+ }
44
+
45
+ .dialog {
46
+ margin: 0;
47
+ padding: 0;
48
+ width: 100%;
49
+ max-width: none;
50
+ height: 100%;
51
+ max-height: none;
52
+ background: transparent;
53
+ border: none;
54
+ outline: none;
55
+ }
56
+
57
+ .dialog::backdrop {
58
+ background: color-mix(in srgb, black 85%, transparent);
59
+ }
60
+
61
+ .panel {
62
+ display: flex;
63
+ flex-direction: column;
64
+ align-items: center;
65
+ justify-content: center;
66
+ height: 100%;
67
+ width: 100%;
68
+ padding: var(--spacing-sm);
69
+ box-sizing: border-box;
70
+ gap: var(--spacing-sm);
71
+ }
72
+
73
+ /* ── Toolbar ────────────────────────────────────────────────── */
74
+
75
+ .toolbar {
76
+ display: flex;
77
+ align-items: center;
78
+ justify-content: space-between;
79
+ gap: var(--spacing-md);
80
+ width: 100%;
81
+ max-width: min(96vw, 1200px);
82
+ flex-shrink: 0;
83
+ }
84
+
85
+ .toolbarInfo {
86
+ display: flex;
87
+ align-items: center;
88
+ gap: var(--spacing-xs);
89
+ color: var(--color-fg-default);
90
+ font-size: var(--font-size-sm);
91
+ font-family: var(--font-family);
92
+ min-width: 0;
93
+ overflow: hidden;
94
+ }
95
+
96
+ .titleMain {
97
+ font-weight: var(--font-weight-medium);
98
+ white-space: nowrap;
99
+ overflow: hidden;
100
+ text-overflow: ellipsis;
101
+ }
102
+
103
+ .titleItem {
104
+ color: var(--color-fg-muted);
105
+ white-space: nowrap;
106
+ overflow: hidden;
107
+ text-overflow: ellipsis;
108
+ }
109
+
110
+ .counter {
111
+ color: var(--color-fg-subtle);
112
+ white-space: nowrap;
113
+ flex-shrink: 0;
114
+ }
115
+
116
+ .toolbarActions {
117
+ display: flex;
118
+ align-items: center;
119
+ gap: var(--spacing-xs);
120
+ flex-shrink: 0;
121
+ }
122
+
123
+ .buttonAdditionSlot {
124
+ margin-inline-end: var(--spacing-sm);
125
+ }
126
+
127
+ .imageArea {
128
+ width: 100%;
129
+ max-width: min(96vw, 1200px);
130
+ background: black;
131
+ border-radius: var(--border-radius-default);
132
+ overflow: hidden;
133
+ display: flex;
134
+ align-items: center;
135
+ justify-content: center;
136
+ position: relative;
137
+ flex: 1 1 0;
138
+ min-height: 0;
139
+ }
140
+
141
+ .imageArea > img {
142
+ max-height: 100%;
143
+ max-width: 100%;
144
+ object-fit: contain;
145
+ display: block;
146
+ }
147
+
148
+ .nav {
149
+ display: flex;
150
+ align-items: center;
151
+ justify-content: space-between;
152
+ width: 100%;
153
+ max-width: min(96vw, 1200px);
154
+ flex-shrink: 0;
155
+ }
156
+
157
+ .navHint {
158
+ color: var(--color-fg-subtle);
159
+ font-size: var(--font-size-xs);
160
+ font-family: var(--font-family);
161
+ }
@@ -30,7 +30,7 @@ function _interopNamespace(e) {
30
30
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
31
31
  var styles__default = /*#__PURE__*/_interopDefault(styles);
32
32
 
33
- const INTERACTIVE_SELECTOR = 'a:not([disabled]), button:not([disabled]), [tabindex]:not([tabindex="-1"]):not([aria-disabled="true"]), [role="menuitem"]:not([aria-disabled="true"]), [role="option"]:not([aria-disabled="true"])';
33
+ const INTERACTIVE_SELECTOR = 'a:not([disabled]), button:not([disabled]), [tabindex]:not([tabindex="-1"]):not([aria-disabled="true"]), [role="menuitem"]:not([aria-disabled="true"]), [role="menuitemradio"]:not([aria-disabled="true"]), [role="menuitemcheckbox"]:not([aria-disabled="true"]), [role="option"]:not([aria-disabled="true"])';
34
34
  function getMenuItems(el) {
35
35
  return Array.from(el.querySelectorAll(INTERACTIVE_SELECTOR));
36
36
  }
@@ -211,7 +211,26 @@ const MenuCheckItem = React__namespace.forwardRef(
211
211
  );
212
212
  MenuCheckItem.displayName = "Menu.CheckItem";
213
213
  const MenuRadioItem = React__namespace.forwardRef(
214
- ({ name, value, checked, disabled, label, onValueChange, className, ...liProps }, ref) => {
214
+ ({
215
+ name,
216
+ value,
217
+ checked,
218
+ active,
219
+ disabled,
220
+ label,
221
+ interactiveRef,
222
+ interactiveProps,
223
+ onValueChange,
224
+ className,
225
+ ...liProps
226
+ }, ref) => {
227
+ var _a, _b;
228
+ const interactiveClass = [
229
+ styles__default.default.interactiveChild,
230
+ styles__default.default.item,
231
+ active ? styles__default.default.active : "",
232
+ checked ? styles__default.default.selected : ""
233
+ ].filter(Boolean).join(" ");
215
234
  return /* @__PURE__ */ jsxRuntime.jsx(
216
235
  "li",
217
236
  {
@@ -222,14 +241,32 @@ const MenuRadioItem = React__namespace.forwardRef(
222
241
  children: /* @__PURE__ */ jsxRuntime.jsx(
223
242
  "div",
224
243
  {
225
- className: styles__default.default.interactiveChild,
244
+ ref: interactiveRef,
245
+ role: (_a = interactiveProps == null ? void 0 : interactiveProps.role) != null ? _a : "menuitemradio",
246
+ tabIndex: (_b = interactiveProps == null ? void 0 : interactiveProps.tabIndex) != null ? _b : 0,
247
+ "aria-checked": checked || void 0,
248
+ "aria-selected": checked || void 0,
249
+ "aria-disabled": disabled || void 0,
250
+ className: interactiveClass,
251
+ ...interactiveProps,
226
252
  onClick: (event) => {
227
- var _a;
253
+ var _a2, _b2;
254
+ (_a2 = interactiveProps == null ? void 0 : interactiveProps.onClick) == null ? void 0 : _a2.call(interactiveProps, event);
255
+ if (event.defaultPrevented) return;
228
256
  if (disabled) return;
229
257
  const target = event.target;
230
- if ((_a = target.closest("label")) != null ? _a : target.closest("input")) return;
258
+ if ((_b2 = target.closest("label")) != null ? _b2 : target.closest("input")) return;
231
259
  onValueChange == null ? void 0 : onValueChange(value);
232
260
  },
261
+ onKeyDown: (event) => {
262
+ var _a2;
263
+ (_a2 = interactiveProps == null ? void 0 : interactiveProps.onKeyDown) == null ? void 0 : _a2.call(interactiveProps, event);
264
+ if (event.defaultPrevented || disabled) return;
265
+ if (event.key === "Enter" || event.key === " ") {
266
+ event.preventDefault();
267
+ onValueChange == null ? void 0 : onValueChange(value);
268
+ }
269
+ },
233
270
  children: /* @__PURE__ */ jsxRuntime.jsx(
234
271
  RadioButton.RadioButton,
235
272
  {
@@ -40,8 +40,11 @@ export interface MenuRadioItemProps extends Omit<React.LiHTMLAttributes<HTMLLIEl
40
40
  name: string;
41
41
  value: string;
42
42
  checked: boolean;
43
+ active?: boolean;
43
44
  disabled?: boolean;
44
45
  label: string;
46
+ interactiveRef?: React.Ref<HTMLDivElement>;
47
+ interactiveProps?: React.HTMLAttributes<HTMLDivElement>;
45
48
  onValueChange?: (value: string) => void;
46
49
  }
47
50
  export declare const Menu: React.ForwardRefExoticComponent<MenuProps & React.RefAttributes<HTMLUListElement>> & {
@@ -5,7 +5,7 @@ import styles from './Menu.module.css';
5
5
  import { Checkbox } from '../forms/checkbox/Checkbox';
6
6
  import { RadioButton } from '../forms/radio-buttons/RadioButton';
7
7
 
8
- const INTERACTIVE_SELECTOR = 'a:not([disabled]), button:not([disabled]), [tabindex]:not([tabindex="-1"]):not([aria-disabled="true"]), [role="menuitem"]:not([aria-disabled="true"]), [role="option"]:not([aria-disabled="true"])';
8
+ const INTERACTIVE_SELECTOR = 'a:not([disabled]), button:not([disabled]), [tabindex]:not([tabindex="-1"]):not([aria-disabled="true"]), [role="menuitem"]:not([aria-disabled="true"]), [role="menuitemradio"]:not([aria-disabled="true"]), [role="menuitemcheckbox"]:not([aria-disabled="true"]), [role="option"]:not([aria-disabled="true"])';
9
9
  function getMenuItems(el) {
10
10
  return Array.from(el.querySelectorAll(INTERACTIVE_SELECTOR));
11
11
  }
@@ -186,7 +186,26 @@ const MenuCheckItem = React.forwardRef(
186
186
  );
187
187
  MenuCheckItem.displayName = "Menu.CheckItem";
188
188
  const MenuRadioItem = React.forwardRef(
189
- ({ name, value, checked, disabled, label, onValueChange, className, ...liProps }, ref) => {
189
+ ({
190
+ name,
191
+ value,
192
+ checked,
193
+ active,
194
+ disabled,
195
+ label,
196
+ interactiveRef,
197
+ interactiveProps,
198
+ onValueChange,
199
+ className,
200
+ ...liProps
201
+ }, ref) => {
202
+ var _a, _b;
203
+ const interactiveClass = [
204
+ styles.interactiveChild,
205
+ styles.item,
206
+ active ? styles.active : "",
207
+ checked ? styles.selected : ""
208
+ ].filter(Boolean).join(" ");
190
209
  return /* @__PURE__ */ jsx(
191
210
  "li",
192
211
  {
@@ -197,14 +216,32 @@ const MenuRadioItem = React.forwardRef(
197
216
  children: /* @__PURE__ */ jsx(
198
217
  "div",
199
218
  {
200
- className: styles.interactiveChild,
219
+ ref: interactiveRef,
220
+ role: (_a = interactiveProps == null ? void 0 : interactiveProps.role) != null ? _a : "menuitemradio",
221
+ tabIndex: (_b = interactiveProps == null ? void 0 : interactiveProps.tabIndex) != null ? _b : 0,
222
+ "aria-checked": checked || void 0,
223
+ "aria-selected": checked || void 0,
224
+ "aria-disabled": disabled || void 0,
225
+ className: interactiveClass,
226
+ ...interactiveProps,
201
227
  onClick: (event) => {
202
- var _a;
228
+ var _a2, _b2;
229
+ (_a2 = interactiveProps == null ? void 0 : interactiveProps.onClick) == null ? void 0 : _a2.call(interactiveProps, event);
230
+ if (event.defaultPrevented) return;
203
231
  if (disabled) return;
204
232
  const target = event.target;
205
- if ((_a = target.closest("label")) != null ? _a : target.closest("input")) return;
233
+ if ((_b2 = target.closest("label")) != null ? _b2 : target.closest("input")) return;
206
234
  onValueChange == null ? void 0 : onValueChange(value);
207
235
  },
236
+ onKeyDown: (event) => {
237
+ var _a2;
238
+ (_a2 = interactiveProps == null ? void 0 : interactiveProps.onKeyDown) == null ? void 0 : _a2.call(interactiveProps, event);
239
+ if (event.defaultPrevented || disabled) return;
240
+ if (event.key === "Enter" || event.key === " ") {
241
+ event.preventDefault();
242
+ onValueChange == null ? void 0 : onValueChange(value);
243
+ }
244
+ },
208
245
  children: /* @__PURE__ */ jsx(
209
246
  RadioButton,
210
247
  {
@@ -8,15 +8,22 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
8
 
9
9
  var styles__default = /*#__PURE__*/_interopDefault(styles);
10
10
 
11
+ function toSize(v) {
12
+ return typeof v === "number" ? `${v}px` : v;
13
+ }
11
14
  function MetricTile({
12
15
  severity,
13
16
  label,
14
17
  value,
15
18
  tooltip,
16
19
  size = "md",
17
- width
20
+ minWidth,
21
+ maxWidth
18
22
  }) {
19
- const style = width != null ? { width: typeof width === "number" ? `${width}px` : width } : void 0;
23
+ const style = minWidth != null || maxWidth != null ? {
24
+ ...minWidth != null ? { minWidth: toSize(minWidth) } : {},
25
+ ...maxWidth != null ? { maxWidth: toSize(maxWidth) } : {}
26
+ } : void 0;
20
27
  return /* @__PURE__ */ jsxRuntime.jsx(client.Tooltip, { content: tooltip, placement: "bottom", children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: styles__default.default.tile, "data-severity": severity, "data-size": size, style, children: [
21
28
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.label, children: label }),
22
29
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.value, children: value })
@@ -1,13 +1,15 @@
1
1
  import type { ReactNode, JSX } from 'react';
2
2
  import { Severity } from '../../constants/severity.types';
3
3
  type MetricTileSize = 'sm' | 'md' | 'lg';
4
+ type SizeValue = number | string;
4
5
  interface MetricTileProps {
5
6
  severity: Severity;
6
7
  label: string;
7
8
  value: string | number;
8
9
  tooltip?: ReactNode;
9
10
  size?: MetricTileSize;
10
- width?: number | string;
11
+ minWidth?: SizeValue;
12
+ maxWidth?: SizeValue;
11
13
  }
12
- export declare function MetricTile({ severity, label, value, tooltip, size, width, }: MetricTileProps): JSX.Element;
14
+ export declare function MetricTile({ severity, label, value, tooltip, size, minWidth, maxWidth, }: MetricTileProps): JSX.Element;
13
15
  export {};
@@ -2,15 +2,22 @@ import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { Tooltip } from '../../client';
3
3
  import styles from './MetricTile.module.css';
4
4
 
5
+ function toSize(v) {
6
+ return typeof v === "number" ? `${v}px` : v;
7
+ }
5
8
  function MetricTile({
6
9
  severity,
7
10
  label,
8
11
  value,
9
12
  tooltip,
10
13
  size = "md",
11
- width
14
+ minWidth,
15
+ maxWidth
12
16
  }) {
13
- const style = width != null ? { width: typeof width === "number" ? `${width}px` : width } : void 0;
17
+ const style = minWidth != null || maxWidth != null ? {
18
+ ...minWidth != null ? { minWidth: toSize(minWidth) } : {},
19
+ ...maxWidth != null ? { maxWidth: toSize(maxWidth) } : {}
20
+ } : void 0;
14
21
  return /* @__PURE__ */ jsx(Tooltip, { content: tooltip, placement: "bottom", children: /* @__PURE__ */ jsxs("span", { className: styles.tile, "data-severity": severity, "data-size": size, style, children: [
15
22
  /* @__PURE__ */ jsx("span", { className: styles.label, children: label }),
16
23
  /* @__PURE__ */ jsx("span", { className: styles.value, children: value })
@@ -3,9 +3,9 @@
3
3
  flex-direction: column;
4
4
  align-items: center;
5
5
  justify-content: center;
6
- gap: var(--spacing-2xs);
7
- padding: var(--spacing-sm) var(--spacing-md);
8
- min-width: 72px;
6
+ gap: 2px;
7
+ padding: var(--spacing-xs) var(--spacing-sm);
8
+ min-width: 64px;
9
9
  overflow: hidden;
10
10
  box-sizing: border-box;
11
11
  border-radius: var(--border-radius-md);
@@ -14,35 +14,37 @@
14
14
  }
15
15
 
16
16
  .tile[data-size='sm'] {
17
- padding: var(--spacing-xs) var(--spacing-sm);
17
+ gap: 1px;
18
+ padding: 6px var(--spacing-sm);
18
19
  min-width: 56px;
19
20
  border-radius: var(--border-radius-sm);
20
21
  }
21
22
 
22
23
  .tile[data-size='lg'] {
24
+ gap: var(--spacing-2xs);
23
25
  padding: var(--spacing-md) var(--spacing-lg);
24
26
  min-width: 96px;
25
27
  border-radius: var(--border-radius-lg);
26
28
  }
27
29
 
28
30
  .tile[data-severity='success'] {
29
- background-color: var(--color-status-success-bg);
30
- color: var(--color-status-success-fg);
31
+ background-color: var(--color-status-success-bg-soft);
32
+ color: var(--color-status-success-fg-soft);
31
33
  }
32
34
 
33
35
  .tile[data-severity='error'] {
34
- background-color: var(--color-status-error-bg);
35
- color: var(--color-status-error-fg);
36
+ background-color: var(--color-status-error-bg-soft);
37
+ color: var(--color-status-error-fg-soft);
36
38
  }
37
39
 
38
40
  .tile[data-severity='warning'] {
39
- background-color: var(--color-status-warning-bg);
40
- color: var(--color-status-warning-fg);
41
+ background-color: var(--color-status-warning-bg-soft);
42
+ color: var(--color-status-warning-fg-soft);
41
43
  }
42
44
 
43
45
  .tile[data-severity='info'] {
44
- background-color: var(--color-status-info-bg);
45
- color: var(--color-status-info-fg);
46
+ background-color: var(--color-status-info-bg-soft);
47
+ color: var(--color-status-info-fg-soft);
46
48
  }
47
49
 
48
50
  .tile[data-severity='neutral'] {
@@ -57,7 +59,7 @@
57
59
 
58
60
  .label {
59
61
  font-size: var(--font-size-xs);
60
- font-weight: var(--font-weight-medium);
62
+ font-weight: var(--font-weight-semibold, 600);
61
63
  text-transform: uppercase;
62
64
  letter-spacing: var(--letter-spacing-wide);
63
65
  line-height: 1;
@@ -69,9 +71,10 @@
69
71
  }
70
72
 
71
73
  .value {
72
- font-size: var(--font-size-lg);
74
+ font-size: var(--font-size-md);
73
75
  font-weight: var(--font-weight-bold);
74
76
  line-height: 1;
77
+ font-variant-numeric: tabular-nums;
75
78
  max-width: 100%;
76
79
  overflow: hidden;
77
80
  white-space: nowrap;
@@ -79,7 +82,8 @@
79
82
  }
80
83
 
81
84
  .tile[data-size='sm'] .label {
82
- font-size: var(--font-size-sm);
85
+ font-size: var(--font-size-xs);
86
+ letter-spacing: normal;
83
87
  }
84
88
 
85
89
  .tile[data-size='sm'] .value {