@vaam-apps/ui 0.1.0
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/CHANGELOG.md +104 -0
- package/LICENSE +21 -0
- package/README.md +286 -0
- package/dist/components/data/code.d.ts +37 -0
- package/dist/components/data/code.d.ts.map +1 -0
- package/dist/components/data/code.js +37 -0
- package/dist/components/data/code.js.map +1 -0
- package/dist/components/data/copy-button.d.ts +39 -0
- package/dist/components/data/copy-button.d.ts.map +1 -0
- package/dist/components/data/copy-button.js +50 -0
- package/dist/components/data/copy-button.js.map +1 -0
- package/dist/components/data/detail-row.d.ts +69 -0
- package/dist/components/data/detail-row.d.ts.map +1 -0
- package/dist/components/data/detail-row.js +27 -0
- package/dist/components/data/detail-row.js.map +1 -0
- package/dist/components/data/id-display.d.ts +33 -0
- package/dist/components/data/id-display.d.ts.map +1 -0
- package/dist/components/data/id-display.js +25 -0
- package/dist/components/data/id-display.js.map +1 -0
- package/dist/components/data/masked-value.d.ts +47 -0
- package/dist/components/data/masked-value.d.ts.map +1 -0
- package/dist/components/data/masked-value.js +46 -0
- package/dist/components/data/masked-value.js.map +1 -0
- package/dist/components/data/money.d.ts +28 -0
- package/dist/components/data/money.d.ts.map +1 -0
- package/dist/components/data/money.js +22 -0
- package/dist/components/data/money.js.map +1 -0
- package/dist/components/data/phone-display.d.ts +45 -0
- package/dist/components/data/phone-display.d.ts.map +1 -0
- package/dist/components/data/phone-display.js +23 -0
- package/dist/components/data/phone-display.js.map +1 -0
- package/dist/components/data/stat-tile.d.ts +33 -0
- package/dist/components/data/stat-tile.d.ts.map +1 -0
- package/dist/components/data/stat-tile.js +18 -0
- package/dist/components/data/stat-tile.js.map +1 -0
- package/dist/components/data/timestamp-display.d.ts +14 -0
- package/dist/components/data/timestamp-display.d.ts.map +1 -0
- package/dist/components/data/timestamp-display.js +77 -0
- package/dist/components/data/timestamp-display.js.map +1 -0
- package/dist/components/patterns/inline-banner.d.ts +22 -0
- package/dist/components/patterns/inline-banner.d.ts.map +1 -0
- package/dist/components/patterns/inline-banner.js +11 -0
- package/dist/components/patterns/inline-banner.js.map +1 -0
- package/dist/components/patterns/inline-empty-state.d.ts +20 -0
- package/dist/components/patterns/inline-empty-state.d.ts.map +1 -0
- package/dist/components/patterns/inline-empty-state.js +11 -0
- package/dist/components/patterns/inline-empty-state.js.map +1 -0
- package/dist/components/patterns/live-row.d.ts +23 -0
- package/dist/components/patterns/live-row.d.ts.map +1 -0
- package/dist/components/patterns/live-row.js +48 -0
- package/dist/components/patterns/live-row.js.map +1 -0
- package/dist/components/patterns/payload-inspector.d.ts +25 -0
- package/dist/components/patterns/payload-inspector.d.ts.map +1 -0
- package/dist/components/patterns/payload-inspector.js +33 -0
- package/dist/components/patterns/payload-inspector.js.map +1 -0
- package/dist/components/patterns/route-skeleton.d.ts +15 -0
- package/dist/components/patterns/route-skeleton.d.ts.map +1 -0
- package/dist/components/patterns/route-skeleton.js +7 -0
- package/dist/components/patterns/route-skeleton.js.map +1 -0
- package/dist/components/patterns/screen-layout.d.ts +14 -0
- package/dist/components/patterns/screen-layout.d.ts.map +1 -0
- package/dist/components/patterns/screen-layout.js +11 -0
- package/dist/components/patterns/screen-layout.js.map +1 -0
- package/dist/components/patterns/stale-write-banner.d.ts +26 -0
- package/dist/components/patterns/stale-write-banner.d.ts.map +1 -0
- package/dist/components/patterns/stale-write-banner.js +28 -0
- package/dist/components/patterns/stale-write-banner.js.map +1 -0
- package/dist/components/patterns/state-timeline.d.ts +58 -0
- package/dist/components/patterns/state-timeline.d.ts.map +1 -0
- package/dist/components/patterns/state-timeline.js +103 -0
- package/dist/components/patterns/state-timeline.js.map +1 -0
- package/dist/components/primitives/badge.d.ts +27 -0
- package/dist/components/primitives/badge.d.ts.map +1 -0
- package/dist/components/primitives/badge.js +33 -0
- package/dist/components/primitives/badge.js.map +1 -0
- package/dist/components/primitives/button.d.ts +12 -0
- package/dist/components/primitives/button.d.ts.map +1 -0
- package/dist/components/primitives/button.js +42 -0
- package/dist/components/primitives/button.js.map +1 -0
- package/dist/components/primitives/calendar.d.ts +39 -0
- package/dist/components/primitives/calendar.d.ts.map +1 -0
- package/dist/components/primitives/calendar.js +102 -0
- package/dist/components/primitives/calendar.js.map +1 -0
- package/dist/components/primitives/card.d.ts +24 -0
- package/dist/components/primitives/card.d.ts.map +1 -0
- package/dist/components/primitives/card.js +28 -0
- package/dist/components/primitives/card.js.map +1 -0
- package/dist/components/primitives/checkbox.d.ts +42 -0
- package/dist/components/primitives/checkbox.d.ts.map +1 -0
- package/dist/components/primitives/checkbox.js +31 -0
- package/dist/components/primitives/checkbox.js.map +1 -0
- package/dist/components/primitives/chip-select.d.ts +38 -0
- package/dist/components/primitives/chip-select.d.ts.map +1 -0
- package/dist/components/primitives/chip-select.js +25 -0
- package/dist/components/primitives/chip-select.js.map +1 -0
- package/dist/components/primitives/command-menu.d.ts +54 -0
- package/dist/components/primitives/command-menu.d.ts.map +1 -0
- package/dist/components/primitives/command-menu.js +24 -0
- package/dist/components/primitives/command-menu.js.map +1 -0
- package/dist/components/primitives/confirm-dialog.d.ts +52 -0
- package/dist/components/primitives/confirm-dialog.d.ts.map +1 -0
- package/dist/components/primitives/confirm-dialog.js +30 -0
- package/dist/components/primitives/confirm-dialog.js.map +1 -0
- package/dist/components/primitives/date-picker.d.ts +68 -0
- package/dist/components/primitives/date-picker.d.ts.map +1 -0
- package/dist/components/primitives/date-picker.js +107 -0
- package/dist/components/primitives/date-picker.js.map +1 -0
- package/dist/components/primitives/dialog.d.ts +50 -0
- package/dist/components/primitives/dialog.d.ts.map +1 -0
- package/dist/components/primitives/dialog.js +90 -0
- package/dist/components/primitives/dialog.js.map +1 -0
- package/dist/components/primitives/drawer.d.ts +71 -0
- package/dist/components/primitives/drawer.d.ts.map +1 -0
- package/dist/components/primitives/drawer.js +93 -0
- package/dist/components/primitives/drawer.js.map +1 -0
- package/dist/components/primitives/dropdown-menu.d.ts +40 -0
- package/dist/components/primitives/dropdown-menu.d.ts.map +1 -0
- package/dist/components/primitives/dropdown-menu.js +53 -0
- package/dist/components/primitives/dropdown-menu.js.map +1 -0
- package/dist/components/primitives/form-field.d.ts +94 -0
- package/dist/components/primitives/form-field.d.ts.map +1 -0
- package/dist/components/primitives/form-field.js +27 -0
- package/dist/components/primitives/form-field.js.map +1 -0
- package/dist/components/primitives/inline-confirm.d.ts +62 -0
- package/dist/components/primitives/inline-confirm.d.ts.map +1 -0
- package/dist/components/primitives/inline-confirm.js +11 -0
- package/dist/components/primitives/inline-confirm.js.map +1 -0
- package/dist/components/primitives/input.d.ts +4 -0
- package/dist/components/primitives/input.d.ts.map +1 -0
- package/dist/components/primitives/input.js +13 -0
- package/dist/components/primitives/input.js.map +1 -0
- package/dist/components/primitives/label.d.ts +26 -0
- package/dist/components/primitives/label.d.ts.map +1 -0
- package/dist/components/primitives/label.js +33 -0
- package/dist/components/primitives/label.js.map +1 -0
- package/dist/components/primitives/pagination.d.ts +54 -0
- package/dist/components/primitives/pagination.d.ts.map +1 -0
- package/dist/components/primitives/pagination.js +31 -0
- package/dist/components/primitives/pagination.js.map +1 -0
- package/dist/components/primitives/popover.d.ts +12 -0
- package/dist/components/primitives/popover.d.ts.map +1 -0
- package/dist/components/primitives/popover.js +22 -0
- package/dist/components/primitives/popover.js.map +1 -0
- package/dist/components/primitives/progress.d.ts +25 -0
- package/dist/components/primitives/progress.d.ts.map +1 -0
- package/dist/components/primitives/progress.js +25 -0
- package/dist/components/primitives/progress.js.map +1 -0
- package/dist/components/primitives/radio-group.d.ts +47 -0
- package/dist/components/primitives/radio-group.d.ts.map +1 -0
- package/dist/components/primitives/radio-group.js +11 -0
- package/dist/components/primitives/radio-group.js.map +1 -0
- package/dist/components/primitives/select.d.ts +39 -0
- package/dist/components/primitives/select.d.ts.map +1 -0
- package/dist/components/primitives/select.js +142 -0
- package/dist/components/primitives/select.js.map +1 -0
- package/dist/components/primitives/separator.d.ts +9 -0
- package/dist/components/primitives/separator.d.ts.map +1 -0
- package/dist/components/primitives/separator.js +16 -0
- package/dist/components/primitives/separator.js.map +1 -0
- package/dist/components/primitives/side-nav.d.ts +65 -0
- package/dist/components/primitives/side-nav.d.ts.map +1 -0
- package/dist/components/primitives/side-nav.js +100 -0
- package/dist/components/primitives/side-nav.js.map +1 -0
- package/dist/components/primitives/skeleton.d.ts +22 -0
- package/dist/components/primitives/skeleton.d.ts.map +1 -0
- package/dist/components/primitives/skeleton.js +25 -0
- package/dist/components/primitives/skeleton.js.map +1 -0
- package/dist/components/primitives/spinner.d.ts +20 -0
- package/dist/components/primitives/spinner.d.ts.map +1 -0
- package/dist/components/primitives/spinner.js +21 -0
- package/dist/components/primitives/spinner.js.map +1 -0
- package/dist/components/primitives/switch.d.ts +29 -0
- package/dist/components/primitives/switch.d.ts.map +1 -0
- package/dist/components/primitives/switch.js +24 -0
- package/dist/components/primitives/switch.js.map +1 -0
- package/dist/components/primitives/table.d.ts +23 -0
- package/dist/components/primitives/table.d.ts.map +1 -0
- package/dist/components/primitives/table.js +81 -0
- package/dist/components/primitives/table.js.map +1 -0
- package/dist/components/primitives/tabs.d.ts +38 -0
- package/dist/components/primitives/tabs.d.ts.map +1 -0
- package/dist/components/primitives/tabs.js +84 -0
- package/dist/components/primitives/tabs.js.map +1 -0
- package/dist/components/primitives/textarea.d.ts +4 -0
- package/dist/components/primitives/textarea.d.ts.map +1 -0
- package/dist/components/primitives/textarea.js +11 -0
- package/dist/components/primitives/textarea.js.map +1 -0
- package/dist/components/primitives/toast.d.ts +24 -0
- package/dist/components/primitives/toast.d.ts.map +1 -0
- package/dist/components/primitives/toast.js +58 -0
- package/dist/components/primitives/toast.js.map +1 -0
- package/dist/components/primitives/tooltip.d.ts +30 -0
- package/dist/components/primitives/tooltip.d.ts.map +1 -0
- package/dist/components/primitives/tooltip.js +6 -0
- package/dist/components/primitives/tooltip.js.map +1 -0
- package/dist/components/status/state-chip.d.ts +53 -0
- package/dist/components/status/state-chip.d.ts.map +1 -0
- package/dist/components/status/state-chip.js +8 -0
- package/dist/components/status/state-chip.js.map +1 -0
- package/dist/components/status/state-mark.d.ts +21 -0
- package/dist/components/status/state-mark.d.ts.map +1 -0
- package/dist/components/status/state-mark.js +90 -0
- package/dist/components/status/state-mark.js.map +1 -0
- package/dist/components/status/status-pill.d.ts +62 -0
- package/dist/components/status/status-pill.d.ts.map +1 -0
- package/dist/components/status/status-pill.js +65 -0
- package/dist/components/status/status-pill.js.map +1 -0
- package/dist/components/status/status-tokens.d.ts +110 -0
- package/dist/components/status/status-tokens.d.ts.map +1 -0
- package/dist/components/status/status-tokens.js +102 -0
- package/dist/components/status/status-tokens.js.map +1 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/cn.d.ts +11 -0
- package/dist/lib/cn.d.ts.map +1 -0
- package/dist/lib/cn.js +78 -0
- package/dist/lib/cn.js.map +1 -0
- package/dist/lib/money.d.ts +73 -0
- package/dist/lib/money.d.ts.map +1 -0
- package/dist/lib/money.js +123 -0
- package/dist/lib/money.js.map +1 -0
- package/dist/lib/omit-undefined.d.ts +17 -0
- package/dist/lib/omit-undefined.d.ts.map +1 -0
- package/dist/lib/omit-undefined.js +24 -0
- package/dist/lib/omit-undefined.js.map +1 -0
- package/package.json +100 -0
- package/src/components/data/code.tsx +43 -0
- package/src/components/data/copy-button.tsx +97 -0
- package/src/components/data/detail-row.tsx +113 -0
- package/src/components/data/id-display.tsx +49 -0
- package/src/components/data/masked-value.tsx +96 -0
- package/src/components/data/money.tsx +46 -0
- package/src/components/data/phone-display.tsx +60 -0
- package/src/components/data/stat-tile.tsx +68 -0
- package/src/components/data/timestamp-display.tsx +106 -0
- package/src/components/patterns/inline-banner.tsx +60 -0
- package/src/components/patterns/inline-empty-state.tsx +45 -0
- package/src/components/patterns/live-row.tsx +75 -0
- package/src/components/patterns/payload-inspector.tsx +157 -0
- package/src/components/patterns/route-skeleton.tsx +77 -0
- package/src/components/patterns/screen-layout.tsx +44 -0
- package/src/components/patterns/stale-write-banner.tsx +44 -0
- package/src/components/patterns/state-timeline.tsx +229 -0
- package/src/components/primitives/badge.tsx +40 -0
- package/src/components/primitives/button.tsx +54 -0
- package/src/components/primitives/calendar.tsx +136 -0
- package/src/components/primitives/card.tsx +70 -0
- package/src/components/primitives/checkbox.tsx +101 -0
- package/src/components/primitives/chip-select.tsx +105 -0
- package/src/components/primitives/command-menu.tsx +92 -0
- package/src/components/primitives/confirm-dialog.tsx +109 -0
- package/src/components/primitives/date-picker.tsx +271 -0
- package/src/components/primitives/dialog.tsx +195 -0
- package/src/components/primitives/drawer.tsx +326 -0
- package/src/components/primitives/dropdown-menu.tsx +128 -0
- package/src/components/primitives/form-field.tsx +136 -0
- package/src/components/primitives/inline-confirm.tsx +118 -0
- package/src/components/primitives/input.tsx +21 -0
- package/src/components/primitives/label.tsx +43 -0
- package/src/components/primitives/pagination.tsx +110 -0
- package/src/components/primitives/popover.tsx +41 -0
- package/src/components/primitives/progress.tsx +66 -0
- package/src/components/primitives/radio-group.tsx +90 -0
- package/src/components/primitives/select.tsx +254 -0
- package/src/components/primitives/separator.tsx +49 -0
- package/src/components/primitives/side-nav.tsx +253 -0
- package/src/components/primitives/skeleton.tsx +25 -0
- package/src/components/primitives/spinner.tsx +39 -0
- package/src/components/primitives/switch.tsx +74 -0
- package/src/components/primitives/table.tsx +157 -0
- package/src/components/primitives/tabs.tsx +163 -0
- package/src/components/primitives/textarea.tsx +21 -0
- package/src/components/primitives/toast.tsx +117 -0
- package/src/components/primitives/tooltip.tsx +38 -0
- package/src/components/status/state-chip.tsx +70 -0
- package/src/components/status/state-mark.tsx +154 -0
- package/src/components/status/status-pill.tsx +153 -0
- package/src/components/status/status-tokens.ts +173 -0
- package/src/index.ts +96 -0
- package/src/lib/cn.ts +168 -0
- package/src/lib/money.ts +164 -0
- package/src/lib/omit-undefined.ts +25 -0
- package/src/styles/theme.css +381 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detail-row.d.ts","sourceRoot":"","sources":["../../../src/components/data/detail-row.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;AAEhE,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,SAAS,CAAC;IACjB,QAAQ,EAAE,SAAS,CAAC;IACpB,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAkB,EAAE,SAAS,EAAE,EAAE,cAAc,+BA8B3F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,OAAkB,EAClB,SAAS,GACV,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,+BAMA"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../../lib/cn.js";
|
|
3
|
+
export function DetailRow({ label, children, variant = "inline", className }) {
|
|
4
|
+
if (variant === "divided") {
|
|
5
|
+
return (_jsxs("div", { className: cn("flex flex-col gap-0.5 border-edge-subtle border-b py-2 last:border-b-0", className), children: [_jsx("dt", { className: "text-caption text-subtle-foreground", children: label }), _jsx("dd", { className: "text-body text-foreground", children: children })] }));
|
|
6
|
+
}
|
|
7
|
+
if (variant === "stacked") {
|
|
8
|
+
return (_jsxs("div", { className: cn("flex flex-col gap-1", className), children: [_jsx("dt", { className: "text-muted-foreground", children: label }), _jsx("dd", { className: "text-caption", children: children })] }));
|
|
9
|
+
}
|
|
10
|
+
return (_jsxs("div", { className: cn("flex items-center justify-between gap-3", className), children: [_jsx("dt", { className: "text-muted-foreground", children: label }), _jsx("dd", { children: children })] }));
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The `<dl>` a group of [`DetailRow`]s belongs in.
|
|
14
|
+
*
|
|
15
|
+
* Exists so a caller cannot pair the rows with the wrong container — every
|
|
16
|
+
* one of the eight original sites wrapped its rows in a `<dl>` with its own
|
|
17
|
+
* spacing (`gap-2`, `gap-3`, or none), which is the same one-decision-in-
|
|
18
|
+
* many-places problem the rows themselves had, one level up.
|
|
19
|
+
*
|
|
20
|
+
* `divided` rows carry their own vertical rhythm through padding and the
|
|
21
|
+
* hairline rule, so the list adds no gap for them — passing `gap` there
|
|
22
|
+
* would double-space rows that are already separated by a border.
|
|
23
|
+
*/
|
|
24
|
+
export function DetailList({ children, variant = "inline", className, }) {
|
|
25
|
+
return (_jsx("dl", { className: cn("flex flex-col text-body", variant !== "divided" && "gap-3", className), children: children }));
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=detail-row.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detail-row.js","sourceRoot":"","sources":["../../../src/components/data/detail-row.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAqDlC,MAAM,UAAU,SAAS,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,GAAG,QAAQ,EAAE,SAAS,EAAkB;IAC1F,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,OAAO,CACL,eACE,SAAS,EAAE,EAAE,CACX,wEAAwE,EACxE,SAAS,CACV,aAED,aAAI,SAAS,EAAC,qCAAqC,YAAE,KAAK,GAAM,EAChE,aAAI,SAAS,EAAC,2BAA2B,YAAE,QAAQ,GAAM,IACrD,CACP,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,OAAO,CACL,eAAK,SAAS,EAAE,EAAE,CAAC,qBAAqB,EAAE,SAAS,CAAC,aAClD,aAAI,SAAS,EAAC,uBAAuB,YAAE,KAAK,GAAM,EAClD,aAAI,SAAS,EAAC,cAAc,YAAE,QAAQ,GAAM,IACxC,CACP,CAAC;IACJ,CAAC;IAED,OAAO,CACL,eAAK,SAAS,EAAE,EAAE,CAAC,yCAAyC,EAAE,SAAS,CAAC,aACtE,aAAI,SAAS,EAAC,uBAAuB,YAAE,KAAK,GAAM,EAClD,uBAAK,QAAQ,GAAM,IACf,CACP,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,UAAU,CAAC,EACzB,QAAQ,EACR,OAAO,GAAG,QAAQ,EAClB,SAAS,GAKV;IACC,OAAO,CACL,aAAI,SAAS,EAAE,EAAE,CAAC,yBAAyB,EAAE,OAAO,KAAK,SAAS,IAAI,OAAO,EAAE,SAAS,CAAC,YACtF,QAAQ,GACN,CACN,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface IdDisplayProps {
|
|
2
|
+
value: string;
|
|
3
|
+
/** `table` (default): a truncated prefix plus a hover-reveal copy
|
|
4
|
+
* button — enough to disambiguate visually within one screen of rows.
|
|
5
|
+
* `full`: the whole value, selectable, with an always-visible copy —
|
|
6
|
+
* detail views only. Copy always copies the FULL value in both. */
|
|
7
|
+
variant?: "table" | "full";
|
|
8
|
+
/** How many leading characters the `table` variant shows. Seven is the
|
|
9
|
+
* default because it is what makes two 23-character CUIDs distinguishable
|
|
10
|
+
* in a list without dominating the column; an id family with a shared
|
|
11
|
+
* prefix (`ord_`, `pi_`) needs more. */
|
|
12
|
+
truncateTo?: number;
|
|
13
|
+
className?: string | undefined;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* An opaque machine identifier — a CUID, a ULID, a provider's reference.
|
|
17
|
+
*
|
|
18
|
+
* Two rules, both learned the hard way and both enforced here rather than
|
|
19
|
+
* left to each call site:
|
|
20
|
+
*
|
|
21
|
+
* - **Never truncate in the middle.** `abc…xyz` makes two ids that differ
|
|
22
|
+
* only in the middle look identical, which is precisely the comparison
|
|
23
|
+
* a human scanning a column is making.
|
|
24
|
+
* - **Never decorate.** A displayed `msg_abc…` gets pasted into a filter
|
|
25
|
+
* that expects the bare value and returns nothing, or worse, a 400. If
|
|
26
|
+
* the prefix is part of the id, it is part of `value`.
|
|
27
|
+
*
|
|
28
|
+
* For a value that must be read in full — a config key, a role name, an
|
|
29
|
+
* enum literal — use `Code` instead. This component's truncation is wrong
|
|
30
|
+
* for anything a human is meant to read rather than match.
|
|
31
|
+
*/
|
|
32
|
+
export declare function IdDisplay({ value, variant, truncateTo, className }: IdDisplayProps): import("react").JSX.Element;
|
|
33
|
+
//# sourceMappingURL=id-display.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id-display.d.ts","sourceRoot":"","sources":["../../../src/components/data/id-display.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd;;;uEAGmE;IACnE,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC3B;;;4CAGwC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,OAAiB,EAAE,UAAc,EAAE,SAAS,EAAE,EAAE,cAAc,+BAahG"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../../lib/cn.js";
|
|
3
|
+
import { CopyButton } from "./copy-button.js";
|
|
4
|
+
/**
|
|
5
|
+
* An opaque machine identifier — a CUID, a ULID, a provider's reference.
|
|
6
|
+
*
|
|
7
|
+
* Two rules, both learned the hard way and both enforced here rather than
|
|
8
|
+
* left to each call site:
|
|
9
|
+
*
|
|
10
|
+
* - **Never truncate in the middle.** `abc…xyz` makes two ids that differ
|
|
11
|
+
* only in the middle look identical, which is precisely the comparison
|
|
12
|
+
* a human scanning a column is making.
|
|
13
|
+
* - **Never decorate.** A displayed `msg_abc…` gets pasted into a filter
|
|
14
|
+
* that expects the bare value and returns nothing, or worse, a 400. If
|
|
15
|
+
* the prefix is part of the id, it is part of `value`.
|
|
16
|
+
*
|
|
17
|
+
* For a value that must be read in full — a config key, a role name, an
|
|
18
|
+
* enum literal — use `Code` instead. This component's truncation is wrong
|
|
19
|
+
* for anything a human is meant to read rather than match.
|
|
20
|
+
*/
|
|
21
|
+
export function IdDisplay({ value, variant = "table", truncateTo = 7, className }) {
|
|
22
|
+
const shown = variant === "table" ? value.slice(0, truncateTo) : value;
|
|
23
|
+
return (_jsxs("span", { className: cn("group inline-flex items-center gap-1.5 font-mono tabular-nums", className), children: [_jsx("span", { className: variant === "full" ? "select-all" : undefined, title: value, children: shown }), _jsx(CopyButton, { value: value, revealOnGroupHover: variant === "table" })] }));
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=id-display.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id-display.js","sourceRoot":"","sources":["../../../src/components/data/id-display.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAiB3C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,EAAE,UAAU,GAAG,CAAC,EAAE,SAAS,EAAkB;IAC/F,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAEvE,OAAO,CACL,gBACE,SAAS,EAAE,EAAE,CAAC,+DAA+D,EAAE,SAAS,CAAC,aAEzF,eAAM,SAAS,EAAE,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,YACzE,KAAK,GACD,EACP,KAAC,UAAU,IAAC,KAAK,EAAE,KAAK,EAAE,kBAAkB,EAAE,OAAO,KAAK,OAAO,GAAI,IAChE,CACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export interface MaskedValueProps {
|
|
2
|
+
/** The full value. Present in the DOM only while revealed. */
|
|
3
|
+
value: string;
|
|
4
|
+
/** Trailing characters left visible while masked, so the value stays
|
|
5
|
+
* recognisable without being readable. `0` masks everything. */
|
|
6
|
+
reveal?: number;
|
|
7
|
+
/** Leading characters left visible — for a prefixed credential like
|
|
8
|
+
* `whsec_…` or `sk_live_…`, where the prefix says what kind of thing it
|
|
9
|
+
* is and is not itself secret. */
|
|
10
|
+
prefix?: number;
|
|
11
|
+
/** Allow revealing at all. `false` gives a permanently masked value with
|
|
12
|
+
* no toggle — for something displayed for recognition only. */
|
|
13
|
+
revealable?: boolean;
|
|
14
|
+
/** Offer a copy button. Copies the full value whether or not it is
|
|
15
|
+
* currently revealed — the point of masking is the screen, not the
|
|
16
|
+
* clipboard. */
|
|
17
|
+
copyable?: boolean;
|
|
18
|
+
/** Names the value in the toggle's and copy button's accessible names,
|
|
19
|
+
* e.g. `"signing secret"`. */
|
|
20
|
+
label?: string;
|
|
21
|
+
className?: string | undefined;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A secret or sensitive value, masked by default with an explicit reveal.
|
|
25
|
+
*
|
|
26
|
+
* # What this is and is not
|
|
27
|
+
*
|
|
28
|
+
* It is a **shoulder-surfing and screen-share control**, not a security
|
|
29
|
+
* boundary. Whoever renders this already received the value over the
|
|
30
|
+
* wire; anyone with the session and dev tools can read it regardless.
|
|
31
|
+
* Withholding data that has already crossed the wire to an authorised,
|
|
32
|
+
* authenticated session would be theatre. What masking buys is real but
|
|
33
|
+
* narrow: the value does not end up in a screenshot, a recording, or the
|
|
34
|
+
* eyeline of the person sitting behind the operator.
|
|
35
|
+
*
|
|
36
|
+
* If a value must genuinely never reach the client, that is an API
|
|
37
|
+
* decision — do not send it — and no component can substitute for it.
|
|
38
|
+
*
|
|
39
|
+
* # Why it is shared
|
|
40
|
+
*
|
|
41
|
+
* Both applications this package serves had hand-rolled one: a console
|
|
42
|
+
* masking a webhook signing secret as `whsec_••••••••••<last 4>`, and a
|
|
43
|
+
* payments dashboard rendering a masked payer reference and a TOTP
|
|
44
|
+
* enrolment secret as plain text because no such component existed.
|
|
45
|
+
*/
|
|
46
|
+
export declare function MaskedValue({ value, reveal, prefix, revealable, copyable, label, className, }: MaskedValueProps): import("react").JSX.Element;
|
|
47
|
+
//# sourceMappingURL=masked-value.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"masked-value.d.ts","sourceRoot":"","sources":["../../../src/components/data/masked-value.tsx"],"names":[],"mappings":"AAOA,MAAM,WAAW,gBAAgB;IAC/B,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAC;IACd;oEACgE;IAChE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;sCAEkC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;mEAC+D;IAC/D,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;oBAEgB;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;kCAC8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAeD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,MAAU,EACV,MAAU,EACV,UAAiB,EACjB,QAAe,EACf,KAAe,EACf,SAAS,GACV,EAAE,gBAAgB,+BAqBlB"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Eye, EyeOff } from "lucide-react";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { cn } from "../../lib/cn.js";
|
|
6
|
+
import { CopyButton } from "./copy-button.js";
|
|
7
|
+
const DOT = "•";
|
|
8
|
+
function mask(value, prefix, reveal) {
|
|
9
|
+
const head = value.slice(0, prefix);
|
|
10
|
+
const tail = reveal > 0 ? value.slice(-reveal) : "";
|
|
11
|
+
const hiddenCount = Math.max(value.length - head.length - tail.length, 0);
|
|
12
|
+
// A fixed run of dots, not one per character: the exact length of a
|
|
13
|
+
// secret is information, and padding it out to the real length leaks it
|
|
14
|
+
// to anyone looking over a shoulder.
|
|
15
|
+
const dots = DOT.repeat(hiddenCount === 0 ? 0 : Math.min(Math.max(hiddenCount, 6), 12));
|
|
16
|
+
return `${head}${dots}${tail}`;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A secret or sensitive value, masked by default with an explicit reveal.
|
|
20
|
+
*
|
|
21
|
+
* # What this is and is not
|
|
22
|
+
*
|
|
23
|
+
* It is a **shoulder-surfing and screen-share control**, not a security
|
|
24
|
+
* boundary. Whoever renders this already received the value over the
|
|
25
|
+
* wire; anyone with the session and dev tools can read it regardless.
|
|
26
|
+
* Withholding data that has already crossed the wire to an authorised,
|
|
27
|
+
* authenticated session would be theatre. What masking buys is real but
|
|
28
|
+
* narrow: the value does not end up in a screenshot, a recording, or the
|
|
29
|
+
* eyeline of the person sitting behind the operator.
|
|
30
|
+
*
|
|
31
|
+
* If a value must genuinely never reach the client, that is an API
|
|
32
|
+
* decision — do not send it — and no component can substitute for it.
|
|
33
|
+
*
|
|
34
|
+
* # Why it is shared
|
|
35
|
+
*
|
|
36
|
+
* Both applications this package serves had hand-rolled one: a console
|
|
37
|
+
* masking a webhook signing secret as `whsec_••••••••••<last 4>`, and a
|
|
38
|
+
* payments dashboard rendering a masked payer reference and a TOTP
|
|
39
|
+
* enrolment secret as plain text because no such component existed.
|
|
40
|
+
*/
|
|
41
|
+
export function MaskedValue({ value, reveal = 4, prefix = 0, revealable = true, copyable = true, label = "value", className, }) {
|
|
42
|
+
const [revealed, setRevealed] = useState(false);
|
|
43
|
+
const shown = revealed ? value : mask(value, prefix, reveal);
|
|
44
|
+
return (_jsxs("span", { className: cn("group inline-flex items-center gap-1.5", className), children: [_jsx("span", { className: cn("font-mono", revealed && "select-all break-all"), children: shown }), revealable && (_jsx("button", { type: "button", onClick: () => setRevealed((previous) => !previous), "aria-label": revealed ? `Hide ${label}` : `Reveal ${label}`, "aria-pressed": revealed, className: "shrink-0 text-subtle-foreground transition-colors hover:text-foreground", children: revealed ? _jsx(EyeOff, { size: 12, strokeWidth: 1.5 }) : _jsx(Eye, { size: 12, strokeWidth: 1.5 }) })), copyable && _jsx(CopyButton, { value: value, label: `Copy ${label}` })] }));
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=masked-value.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"masked-value.js","sourceRoot":"","sources":["../../../src/components/data/masked-value.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAyB3C,MAAM,GAAG,GAAG,GAAG,CAAC;AAEhB,SAAS,IAAI,CAAC,KAAa,EAAE,MAAc,EAAE,MAAc;IACzD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACpD,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC1E,oEAAoE;IACpE,wEAAwE;IACxE,qCAAqC;IACrC,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,WAAW,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IACxF,OAAO,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC;AACjC,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,WAAW,CAAC,EAC1B,KAAK,EACL,MAAM,GAAG,CAAC,EACV,MAAM,GAAG,CAAC,EACV,UAAU,GAAG,IAAI,EACjB,QAAQ,GAAG,IAAI,EACf,KAAK,GAAG,OAAO,EACf,SAAS,GACQ;IACjB,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAChD,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAE7D,OAAO,CACL,gBAAM,SAAS,EAAE,EAAE,CAAC,wCAAwC,EAAE,SAAS,CAAC,aACtE,eAAM,SAAS,EAAE,EAAE,CAAC,WAAW,EAAE,QAAQ,IAAI,sBAAsB,CAAC,YAAG,KAAK,GAAQ,EACnF,UAAU,IAAI,CACb,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,gBACvC,QAAQ,CAAC,CAAC,CAAC,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,EAAE,kBAC5C,QAAQ,EACtB,SAAS,EAAC,yEAAyE,YAElF,QAAQ,CAAC,CAAC,CAAC,KAAC,MAAM,IAAC,IAAI,EAAE,EAAE,EAAE,WAAW,EAAE,GAAG,GAAI,CAAC,CAAC,CAAC,KAAC,GAAG,IAAC,IAAI,EAAE,EAAE,EAAE,WAAW,EAAE,GAAG,GAAI,GACjF,CACV,EACA,QAAQ,IAAI,KAAC,UAAU,IAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,KAAK,EAAE,GAAI,IAC5D,CACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type FormatMoneyOptions, type MinorUnits } from "../../lib/money.js";
|
|
2
|
+
export interface MoneyProps extends FormatMoneyOptions {
|
|
3
|
+
/** An integer count of the currency's smallest unit. */
|
|
4
|
+
amount: MinorUnits;
|
|
5
|
+
/** ISO 4217 code, e.g. `"XAF"`. */
|
|
6
|
+
currency: string;
|
|
7
|
+
/** Tint by sign: credits green, debits red. Off by default — in a
|
|
8
|
+
* ledger where most rows are one direction, colouring every row is
|
|
9
|
+
* noise, and colour alone is not an accessible way to convey sign. The
|
|
10
|
+
* minus sign is always rendered regardless. */
|
|
11
|
+
tone?: "none" | "signed";
|
|
12
|
+
className?: string | undefined;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* An amount of money, right-alignable and vertically comparable.
|
|
16
|
+
*
|
|
17
|
+
* `tabular-nums` is the whole reason this is a component and not a call to
|
|
18
|
+
* [`formatMoney`] in a `<span>`: without fixed-width digits, a column of
|
|
19
|
+
* amounts does not line up at the decimal point, and scanning it for an
|
|
20
|
+
* outlier — the actual job — stops working.
|
|
21
|
+
*
|
|
22
|
+
* `<data value>` rather than `<span>`: the machine-readable amount stays
|
|
23
|
+
* in the DOM in minor units, so a copy-paste or a scraper gets the exact
|
|
24
|
+
* integer rather than a locale-formatted string it would have to parse
|
|
25
|
+
* back.
|
|
26
|
+
*/
|
|
27
|
+
export declare function Money({ amount, currency, tone, className, ...options }: MoneyProps): import("react").JSX.Element;
|
|
28
|
+
//# sourceMappingURL=money.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../../../src/components/data/money.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,kBAAkB,EAAe,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAExF,MAAM,WAAW,UAAW,SAAQ,kBAAkB;IACpD,wDAAwD;IACxD,MAAM,EAAE,UAAU,CAAC;IACnB,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB;;;mDAG+C;IAC/C,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAa,EAAE,SAAS,EAAE,GAAG,OAAO,EAAE,EAAE,UAAU,+BAgB3F"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../../lib/cn.js";
|
|
3
|
+
import { formatMoney } from "../../lib/money.js";
|
|
4
|
+
/**
|
|
5
|
+
* An amount of money, right-alignable and vertically comparable.
|
|
6
|
+
*
|
|
7
|
+
* `tabular-nums` is the whole reason this is a component and not a call to
|
|
8
|
+
* [`formatMoney`] in a `<span>`: without fixed-width digits, a column of
|
|
9
|
+
* amounts does not line up at the decimal point, and scanning it for an
|
|
10
|
+
* outlier — the actual job — stops working.
|
|
11
|
+
*
|
|
12
|
+
* `<data value>` rather than `<span>`: the machine-readable amount stays
|
|
13
|
+
* in the DOM in minor units, so a copy-paste or a scraper gets the exact
|
|
14
|
+
* integer rather than a locale-formatted string it would have to parse
|
|
15
|
+
* back.
|
|
16
|
+
*/
|
|
17
|
+
export function Money({ amount, currency, tone = "none", className, ...options }) {
|
|
18
|
+
const formatted = formatMoney(amount, currency, options);
|
|
19
|
+
const negative = String(amount).trim().startsWith("-");
|
|
20
|
+
return (_jsx("data", { value: `${String(amount)} ${currency}`, className: cn("font-mono tabular-nums whitespace-nowrap", tone === "signed" && (negative ? "text-state-danger-fg" : "text-state-success-fg"), className), children: formatted }));
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=money.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"money.js","sourceRoot":"","sources":["../../../src/components/data/money.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAA2B,WAAW,EAAmB,MAAM,iBAAiB,CAAC;AAexF;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,GAAG,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,EAAc;IAC1F,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IACzD,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IAEvD,OAAO,CACL,eACE,KAAK,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,QAAQ,EAAE,EACtC,SAAS,EAAE,EAAE,CACX,0CAA0C,EAC1C,IAAI,KAAK,QAAQ,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,uBAAuB,CAAC,EAClF,SAAS,CACV,YAEA,SAAS,GACL,CACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export interface PhoneDisplayProps {
|
|
2
|
+
/** E.164, e.g. `+237677123456`. Stored form, and what gets copied. */
|
|
3
|
+
value: string;
|
|
4
|
+
/**
|
|
5
|
+
* Regroups the E.164 digits for reading. Return `null` — or omit the
|
|
6
|
+
* prop — to render the value verbatim.
|
|
7
|
+
*
|
|
8
|
+
* Grouping is a per-country convention (`+237 6 77 12 34 56` in
|
|
9
|
+
* Cameroon, `+1 (415) 555-0132` in the NANP) and getting it wrong is
|
|
10
|
+
* worse than not grouping at all, because a mis-grouped number reads as
|
|
11
|
+
* a different number. This component therefore ships no default
|
|
12
|
+
* grouping and no built-in country table: a caller that knows its
|
|
13
|
+
* market passes a formatter, and everyone else gets the raw E.164,
|
|
14
|
+
* which is always correct if less pretty.
|
|
15
|
+
*/
|
|
16
|
+
format?: ((e164: string) => string | null) | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* A short mono tag after the number — a carrier, a line type, a region.
|
|
19
|
+
* Renders an em dash when absent, so a column of numbers stays aligned
|
|
20
|
+
* whether or not each row could be classified.
|
|
21
|
+
*/
|
|
22
|
+
tag?: string | undefined;
|
|
23
|
+
/** Hover text for the tag. Say where it came from and how much to trust
|
|
24
|
+
* it: a carrier inferred from a number prefix is a guess, and number
|
|
25
|
+
* portability means it is wrong for a real fraction of subscribers. */
|
|
26
|
+
tagTitle?: string | undefined;
|
|
27
|
+
className?: string | undefined;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A phone number, never truncated.
|
|
31
|
+
*
|
|
32
|
+
* Truncation is banned rather than discouraged: a half-shown phone number
|
|
33
|
+
* is worse than useless in an investigation, and unlike an opaque id
|
|
34
|
+
* (where a prefix is enough to match rows by eye) there is no prefix of a
|
|
35
|
+
* phone number that identifies it.
|
|
36
|
+
*
|
|
37
|
+
* Copy always copies the raw E.164 — the form that pastes into a query —
|
|
38
|
+
* not the grouped form on screen.
|
|
39
|
+
*
|
|
40
|
+
* Previously this was `MsisdnDisplay`, with Cameroon's `+237` grouping and
|
|
41
|
+
* a four-entry Cameroonian carrier table compiled in. Both are now the
|
|
42
|
+
* caller's: an application knows its market, a component library does not.
|
|
43
|
+
*/
|
|
44
|
+
export declare function PhoneDisplay({ value, format, tag, tagTitle, className }: PhoneDisplayProps): import("react").JSX.Element;
|
|
45
|
+
//# sourceMappingURL=phone-display.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phone-display.d.ts","sourceRoot":"","sources":["../../../src/components/data/phone-display.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,SAAS,CAAC;IACvD;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB;;2EAEuE;IACvE,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,iBAAiB,+BAY1F"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../../lib/cn.js";
|
|
3
|
+
import { CopyButton } from "./copy-button.js";
|
|
4
|
+
/**
|
|
5
|
+
* A phone number, never truncated.
|
|
6
|
+
*
|
|
7
|
+
* Truncation is banned rather than discouraged: a half-shown phone number
|
|
8
|
+
* is worse than useless in an investigation, and unlike an opaque id
|
|
9
|
+
* (where a prefix is enough to match rows by eye) there is no prefix of a
|
|
10
|
+
* phone number that identifies it.
|
|
11
|
+
*
|
|
12
|
+
* Copy always copies the raw E.164 — the form that pastes into a query —
|
|
13
|
+
* not the grouped form on screen.
|
|
14
|
+
*
|
|
15
|
+
* Previously this was `MsisdnDisplay`, with Cameroon's `+237` grouping and
|
|
16
|
+
* a four-entry Cameroonian carrier table compiled in. Both are now the
|
|
17
|
+
* caller's: an application knows its market, a component library does not.
|
|
18
|
+
*/
|
|
19
|
+
export function PhoneDisplay({ value, format, tag, tagTitle, className }) {
|
|
20
|
+
const formatted = format?.(value) ?? value;
|
|
21
|
+
return (_jsxs("span", { className: cn("group inline-flex items-center gap-1.5 whitespace-nowrap", className), children: [_jsx("span", { className: "font-mono tabular-nums", children: formatted }), _jsx("span", { className: "font-mono text-micro text-subtle-foreground", title: tagTitle, children: tag ?? "—" }), _jsx(CopyButton, { value: value, revealOnGroupHover: true })] }));
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=phone-display.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phone-display.js","sourceRoot":"","sources":["../../../src/components/data/phone-display.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AA+B3C;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,YAAY,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAqB;IACzF,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC;IAE3C,OAAO,CACL,gBAAM,SAAS,EAAE,EAAE,CAAC,0DAA0D,EAAE,SAAS,CAAC,aACxF,eAAM,SAAS,EAAC,wBAAwB,YAAE,SAAS,GAAQ,EAC3D,eAAM,SAAS,EAAC,6CAA6C,EAAC,KAAK,EAAE,QAAQ,YAC1E,GAAG,IAAI,GAAG,GACN,EACP,KAAC,UAAU,IAAC,KAAK,EAAE,KAAK,EAAE,kBAAkB,SAAG,IAC1C,CACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { StatusHue } from "../status/status-tokens.js";
|
|
3
|
+
export interface StatTileProps {
|
|
4
|
+
label: ReactNode;
|
|
5
|
+
/** The number itself. Pass a `Money` or a formatted string — this
|
|
6
|
+
* component does no formatting, because a tile has no way to know
|
|
7
|
+
* whether it is showing a count, a currency or a rate. */
|
|
8
|
+
value: ReactNode;
|
|
9
|
+
/** One line under the value: the comparison, the window, the caveat.
|
|
10
|
+
* A bare number with no denominator is the most common way a dashboard
|
|
11
|
+
* misleads, so this slot exists to make the denominator easy. */
|
|
12
|
+
caption?: ReactNode;
|
|
13
|
+
/** Tints the value. Leave unset unless the number's own colour carries
|
|
14
|
+
* meaning — a wall of coloured tiles makes the one that matters harder
|
|
15
|
+
* to find, which is the opposite of the point. */
|
|
16
|
+
tone?: StatusHue | undefined;
|
|
17
|
+
/** Top-right slot — a sparkline, a `StateChip`, a refresh button. */
|
|
18
|
+
action?: ReactNode;
|
|
19
|
+
className?: string | undefined;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* One headline number with its label and context.
|
|
23
|
+
*
|
|
24
|
+
* Borders, not shadows, and no card chrome of its own beyond a hairline:
|
|
25
|
+
* tiles appear in rows of three to six, and six shadowed boxes is visual
|
|
26
|
+
* noise where six outlined ones read as one instrument panel.
|
|
27
|
+
*
|
|
28
|
+
* `<dl>`/`<dt>`/`<dd>` rather than divs — a tile *is* a term and its
|
|
29
|
+
* definition, and the semantics make a row of them navigable rather than
|
|
30
|
+
* an undifferentiated wall of text to a screen reader.
|
|
31
|
+
*/
|
|
32
|
+
export declare function StatTile({ label, value, caption, tone, action, className }: StatTileProps): import("react").JSX.Element;
|
|
33
|
+
//# sourceMappingURL=stat-tile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stat-tile.d.ts","sourceRoot":"","sources":["../../../src/components/data/stat-tile.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAGzD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,SAAS,CAAC;IACjB;;8DAE0D;IAC1D,KAAK,EAAE,SAAS,CAAC;IACjB;;qEAEiE;IACjE,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB;;sDAEkD;IAClD,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC7B,qEAAqE;IACrE,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,aAAa,+BAgCzF"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../../lib/cn.js";
|
|
3
|
+
import { HUE_CLASSES } from "../status/status-tokens.js";
|
|
4
|
+
/**
|
|
5
|
+
* One headline number with its label and context.
|
|
6
|
+
*
|
|
7
|
+
* Borders, not shadows, and no card chrome of its own beyond a hairline:
|
|
8
|
+
* tiles appear in rows of three to six, and six shadowed boxes is visual
|
|
9
|
+
* noise where six outlined ones read as one instrument panel.
|
|
10
|
+
*
|
|
11
|
+
* `<dl>`/`<dt>`/`<dd>` rather than divs — a tile *is* a term and its
|
|
12
|
+
* definition, and the semantics make a row of them navigable rather than
|
|
13
|
+
* an undifferentiated wall of text to a screen reader.
|
|
14
|
+
*/
|
|
15
|
+
export function StatTile({ label, value, caption, tone, action, className }) {
|
|
16
|
+
return (_jsxs("div", { className: cn("relative flex flex-col rounded-box border border-edge bg-base-300 p-4", className), children: [action != null && _jsx("div", { className: "absolute top-4 right-4", children: action }), _jsxs("dl", { className: "flex flex-col gap-1", children: [_jsx("dt", { className: cn("text-caption text-muted-foreground", action != null && "pr-20"), children: label }), _jsx("dd", { className: cn("font-mono text-metric tabular-nums", tone === undefined ? "text-foreground" : HUE_CLASSES[tone].fg), children: value }), caption != null && _jsx("dd", { className: "text-caption text-subtle-foreground", children: caption })] })] }));
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=stat-tile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stat-tile.js","sourceRoot":"","sources":["../../../src/components/data/stat-tile.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAElC,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAqBtD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,QAAQ,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAiB;IACxF,OAAO,CACL,eACE,SAAS,EAAE,EAAE,CACX,uEAAuE,EACvE,SAAS,CACV,aASA,MAAM,IAAI,IAAI,IAAI,cAAK,SAAS,EAAC,wBAAwB,YAAE,MAAM,GAAO,EACzE,cAAI,SAAS,EAAC,qBAAqB,aACjC,aAAI,SAAS,EAAE,EAAE,CAAC,oCAAoC,EAAE,MAAM,IAAI,IAAI,IAAI,OAAO,CAAC,YAC/E,KAAK,GACH,EACL,aACE,SAAS,EAAE,EAAE,CACX,oCAAoC,EACpC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,EAAE,CAC9D,YAEA,KAAK,GACH,EACJ,OAAO,IAAI,IAAI,IAAI,aAAI,SAAS,EAAC,qCAAqC,YAAE,OAAO,GAAM,IACnF,IACD,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface TimestampDisplayProps {
|
|
2
|
+
/** ISO-8601. */
|
|
3
|
+
value: string;
|
|
4
|
+
className?: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Relative for anything under 24h (`2m`, `47m`, `6h`), absolute otherwise
|
|
8
|
+
* — design doc §7.2. Renders the absolute value on the server and on
|
|
9
|
+
* first client render (so server/client markup matches and Next doesn't
|
|
10
|
+
* warn/flash), then upgrades to relative once mounted, driven by the
|
|
11
|
+
* shared interval above rather than its own timer.
|
|
12
|
+
*/
|
|
13
|
+
export declare function TimestampDisplay({ value, className }: TimestampDisplayProps): import("react").JSX.Element;
|
|
14
|
+
//# sourceMappingURL=timestamp-display.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timestamp-display.d.ts","sourceRoot":"","sources":["../../../src/components/data/timestamp-display.tsx"],"names":[],"mappings":"AAiEA,MAAM,WAAW,qBAAqB;IACpC,gBAAgB;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,qBAAqB,+BA2B3E"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useState, useSyncExternalStore } from "react";
|
|
4
|
+
import { cn } from "../../lib/cn.js";
|
|
5
|
+
// A single shared 30s interval drives every `TimestampDisplay` instance's
|
|
6
|
+
// relative-time re-render (design doc §7.2: "a table of 200 rows must not
|
|
7
|
+
// run 200 timers"). Lazily started on the first mounted instance, torn
|
|
8
|
+
// down when the last one unmounts.
|
|
9
|
+
let tick = 0;
|
|
10
|
+
const listeners = new Set();
|
|
11
|
+
let timer = null;
|
|
12
|
+
function subscribe(listener) {
|
|
13
|
+
listeners.add(listener);
|
|
14
|
+
timer ??= setInterval(() => {
|
|
15
|
+
tick += 1;
|
|
16
|
+
for (const l of listeners)
|
|
17
|
+
l();
|
|
18
|
+
}, 30_000);
|
|
19
|
+
return () => {
|
|
20
|
+
listeners.delete(listener);
|
|
21
|
+
if (listeners.size === 0 && timer !== null) {
|
|
22
|
+
clearInterval(timer);
|
|
23
|
+
timer = null;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function useSharedTick() {
|
|
28
|
+
return useSyncExternalStore(subscribe, () => tick, () => 0);
|
|
29
|
+
}
|
|
30
|
+
function pad(n) {
|
|
31
|
+
return String(n).padStart(2, "0");
|
|
32
|
+
}
|
|
33
|
+
/** `2026-08-08 14:03:07` + a zone suffix — ISO-ordered so it sorts and
|
|
34
|
+
* compares visually (design doc §7.2). Only UTC is implemented (`Z`
|
|
35
|
+
* suffix, always): the Africa/Douala toggle lives on the top bar, which
|
|
36
|
+
* this screen doesn't build. "A bare local time with no zone label never
|
|
37
|
+
* appears anywhere in this product" still holds — the suffix is always
|
|
38
|
+
* present, just not yet switchable. */
|
|
39
|
+
function formatAbsolute(date) {
|
|
40
|
+
const y = date.getUTCFullYear();
|
|
41
|
+
const mo = pad(date.getUTCMonth() + 1);
|
|
42
|
+
const d = pad(date.getUTCDate());
|
|
43
|
+
const h = pad(date.getUTCHours());
|
|
44
|
+
const mi = pad(date.getUTCMinutes());
|
|
45
|
+
const s = pad(date.getUTCSeconds());
|
|
46
|
+
return `${y}-${mo}-${d} ${h}:${mi}:${s}Z`;
|
|
47
|
+
}
|
|
48
|
+
function formatRelative(diffMs) {
|
|
49
|
+
const minutes = Math.floor(diffMs / 60_000);
|
|
50
|
+
if (minutes < 1)
|
|
51
|
+
return "just now";
|
|
52
|
+
if (minutes < 60)
|
|
53
|
+
return `${minutes}m`;
|
|
54
|
+
const hours = Math.floor(minutes / 60);
|
|
55
|
+
return `${hours}h`;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Relative for anything under 24h (`2m`, `47m`, `6h`), absolute otherwise
|
|
59
|
+
* — design doc §7.2. Renders the absolute value on the server and on
|
|
60
|
+
* first client render (so server/client markup matches and Next doesn't
|
|
61
|
+
* warn/flash), then upgrades to relative once mounted, driven by the
|
|
62
|
+
* shared interval above rather than its own timer.
|
|
63
|
+
*/
|
|
64
|
+
export function TimestampDisplay({ value, className }) {
|
|
65
|
+
useSharedTick();
|
|
66
|
+
const [hydrated, setHydrated] = useState(false);
|
|
67
|
+
useEffect(() => setHydrated(true), []);
|
|
68
|
+
const date = new Date(value);
|
|
69
|
+
const absolute = formatAbsolute(date);
|
|
70
|
+
if (!hydrated) {
|
|
71
|
+
return (_jsx("span", { className: cn("font-mono text-subtle-foreground tabular-nums", className), children: absolute }));
|
|
72
|
+
}
|
|
73
|
+
const diffMs = Date.now() - date.getTime();
|
|
74
|
+
const display = diffMs >= 0 && diffMs < 24 * 3600 * 1000 ? formatRelative(diffMs) : absolute;
|
|
75
|
+
return (_jsx("span", { title: absolute, className: cn("font-mono text-subtle-foreground tabular-nums", className), children: display }));
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=timestamp-display.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timestamp-display.js","sourceRoot":"","sources":["../../../src/components/data/timestamp-display.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAClE,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAElC,0EAA0E;AAC1E,0EAA0E;AAC1E,uEAAuE;AACvE,mCAAmC;AAEnC,IAAI,IAAI,GAAG,CAAC,CAAC;AACb,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAC;AACxC,IAAI,KAAK,GAA0C,IAAI,CAAC;AAExD,SAAS,SAAS,CAAC,QAAoB;IACrC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACxB,KAAK,KAAK,WAAW,CAAC,GAAG,EAAE;QACzB,IAAI,IAAI,CAAC,CAAC;QACV,KAAK,MAAM,CAAC,IAAI,SAAS;YAAE,CAAC,EAAE,CAAC;IACjC,CAAC,EAAE,MAAM,CAAC,CAAC;IACX,OAAO,GAAG,EAAE;QACV,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC3B,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YAC3C,aAAa,CAAC,KAAK,CAAC,CAAC;YACrB,KAAK,GAAG,IAAI,CAAC;QACf,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,aAAa;IACpB,OAAO,oBAAoB,CACzB,SAAS,EACT,GAAG,EAAE,CAAC,IAAI,EACV,GAAG,EAAE,CAAC,CAAC,CACR,CAAC;AACJ,CAAC;AAED,SAAS,GAAG,CAAC,CAAS;IACpB,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACpC,CAAC;AAED;;;;;uCAKuC;AACvC,SAAS,cAAc,CAAC,IAAU;IAChC,MAAM,CAAC,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;IAChC,MAAM,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC;IACvC,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;IACjC,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IAClC,MAAM,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;IACrC,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;IACpC,OAAO,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC;AAC5C,CAAC;AAED,SAAS,cAAc,CAAC,MAAc;IACpC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IAC5C,IAAI,OAAO,GAAG,CAAC;QAAE,OAAO,UAAU,CAAC;IACnC,IAAI,OAAO,GAAG,EAAE;QAAE,OAAO,GAAG,OAAO,GAAG,CAAC;IACvC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACvC,OAAO,GAAG,KAAK,GAAG,CAAC;AACrB,CAAC;AAQD;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAAE,KAAK,EAAE,SAAS,EAAyB;IAC1E,aAAa,EAAE,CAAC;IAChB,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAChD,SAAS,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IAEvC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,MAAM,QAAQ,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IAEtC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,CACL,eAAM,SAAS,EAAE,EAAE,CAAC,+CAA+C,EAAE,SAAS,CAAC,YAC5E,QAAQ,GACJ,CACR,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;IAC3C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,MAAM,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IAE7F,OAAO,CACL,eACE,KAAK,EAAE,QAAQ,EACf,SAAS,EAAE,EAAE,CAAC,+CAA+C,EAAE,SAAS,CAAC,YAExE,OAAO,GACH,CACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export interface InlineBannerProps {
|
|
3
|
+
/** `'neutral'` — a standing notice (scope, caveat). `'danger'` — an
|
|
4
|
+
* error. `'warning'` — recoverable, needs attention (a stale write).
|
|
5
|
+
* `'success'` — a positive confirmation (a verified audit chain).
|
|
6
|
+
* `'uncertain'` — degraded or unknown, neither failure nor success (a
|
|
7
|
+
* stalled live feed, a UCS-2 volume spike). `'plain'` — a caption-only
|
|
8
|
+
* note with no border or fill, for something that doesn't need the
|
|
9
|
+
* weight of a bordered box.
|
|
10
|
+
*
|
|
11
|
+
* The last three were added after the R6 factorization, not with it:
|
|
12
|
+
* four route groups had hand-rolled boxes in exactly these tones because
|
|
13
|
+
* the variant they needed did not exist, and the agents doing the
|
|
14
|
+
* factorization were deliberately barred from editing this package. Two
|
|
15
|
+
* of them flagged the gap rather than inventing a competing component,
|
|
16
|
+
* which is the behaviour the constraint was for. */
|
|
17
|
+
variant?: "neutral" | "danger" | "warning" | "success" | "uncertain" | "plain";
|
|
18
|
+
children: ReactNode;
|
|
19
|
+
className?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function InlineBanner({ variant, children, className }: InlineBannerProps): import("react").JSX.Element;
|
|
22
|
+
//# sourceMappingURL=inline-banner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline-banner.d.ts","sourceRoot":"","sources":["../../../src/components/patterns/inline-banner.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAiBvC,MAAM,WAAW,iBAAiB;IAChC;;;;;;;;;;;;;wDAaoD;IACpD,OAAO,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;IAC/E,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,YAAY,CAAC,EAAE,OAAmB,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,iBAAiB,+BAsB3F"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../../lib/cn.js";
|
|
3
|
+
export function InlineBanner({ variant = "neutral", children, className }) {
|
|
4
|
+
return (_jsx("div", { className: cn("text-caption", variant === "neutral" &&
|
|
5
|
+
"rounded-sm border border-edge bg-surface-2 px-3 py-2 text-muted-foreground", variant === "danger" &&
|
|
6
|
+
"rounded-sm border border-state-danger-border bg-state-danger-bg px-3 py-2 text-state-danger-fg", variant === "warning" &&
|
|
7
|
+
"rounded-sm border border-state-warning-border bg-state-warning-bg px-3 py-2 text-state-warning-fg", variant === "success" &&
|
|
8
|
+
"rounded-sm border border-state-success-border bg-state-success-bg px-3 py-2 text-state-success-fg", variant === "uncertain" &&
|
|
9
|
+
"rounded-sm border border-state-uncertain-border bg-state-uncertain-bg px-3 py-2 text-state-uncertain-fg", variant === "plain" && "text-subtle-foreground", className), children: children }));
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=inline-banner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline-banner.js","sourceRoot":"","sources":["../../../src/components/patterns/inline-banner.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAoClC,MAAM,UAAU,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAqB;IAC1F,OAAO,CACL,cACE,SAAS,EAAE,EAAE,CACX,cAAc,EACd,OAAO,KAAK,SAAS;YACnB,4EAA4E,EAC9E,OAAO,KAAK,QAAQ;YAClB,gGAAgG,EAClG,OAAO,KAAK,SAAS;YACnB,mGAAmG,EACrG,OAAO,KAAK,SAAS;YACnB,mGAAmG,EACrG,OAAO,KAAK,WAAW;YACrB,yGAAyG,EAC3G,OAAO,KAAK,OAAO,IAAI,wBAAwB,EAC/C,SAAS,CACV,YAEA,QAAQ,GACL,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export interface InlineEmptyStateProps {
|
|
3
|
+
message: ReactNode;
|
|
4
|
+
action?: {
|
|
5
|
+
label: string;
|
|
6
|
+
onClick: () => void;
|
|
7
|
+
};
|
|
8
|
+
/** `'standalone'` is the one exception (§5.3): a screen with nothing else
|
|
9
|
+
* to do may centre a single line plus one primary action. Still no
|
|
10
|
+
* illustration, still no card. */
|
|
11
|
+
variant?: "inline" | "standalone";
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Enforces the binding rule: empty states are inline status lines, not
|
|
16
|
+
* centred placards. Use inside a table body (spanning all columns) or in
|
|
17
|
+
* the panel where the missing list would be.
|
|
18
|
+
*/
|
|
19
|
+
export declare function InlineEmptyState({ message, action, variant, className, }: InlineEmptyStateProps): import("react").JSX.Element;
|
|
20
|
+
//# sourceMappingURL=inline-empty-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline-empty-state.d.ts","sourceRoot":"","sources":["../../../src/components/patterns/inline-empty-state.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,SAAS,CAAC;IACnB,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC;IAChD;;sCAEkC;IAClC,OAAO,CAAC,EAAE,QAAQ,GAAG,YAAY,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,MAAM,EACN,OAAkB,EAClB,SAAS,GACV,EAAE,qBAAqB,+BAqBvB"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../../lib/cn.js";
|
|
3
|
+
/**
|
|
4
|
+
* Enforces the binding rule: empty states are inline status lines, not
|
|
5
|
+
* centred placards. Use inside a table body (spanning all columns) or in
|
|
6
|
+
* the panel where the missing list would be.
|
|
7
|
+
*/
|
|
8
|
+
export function InlineEmptyState({ message, action, variant = "inline", className, }) {
|
|
9
|
+
return (_jsxs("div", { className: cn("flex items-center gap-3 text-body text-muted-foreground", variant === "standalone" ? "justify-center py-16 text-center" : "py-3 text-left", className), children: [_jsx("span", { children: message }), action != null && (_jsx("button", { type: "button", onClick: action.onClick, className: "text-foreground underline decoration-edge-strong underline-offset-2 hover:decoration-foreground", children: action.label }))] }));
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=inline-empty-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline-empty-state.js","sourceRoot":"","sources":["../../../src/components/patterns/inline-empty-state.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAYlC;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAC/B,OAAO,EACP,MAAM,EACN,OAAO,GAAG,QAAQ,EAClB,SAAS,GACa;IACtB,OAAO,CACL,eACE,SAAS,EAAE,EAAE,CACX,yDAAyD,EACzD,OAAO,KAAK,YAAY,CAAC,CAAC,CAAC,kCAAkC,CAAC,CAAC,CAAC,gBAAgB,EAChF,SAAS,CACV,aAED,yBAAO,OAAO,GAAQ,EACrB,MAAM,IAAI,IAAI,IAAI,CACjB,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,MAAM,CAAC,OAAO,EACvB,SAAS,EAAC,iGAAiG,YAE1G,MAAM,CAAC,KAAK,GACN,CACV,IACG,CACP,CAAC;AACJ,CAAC"}
|