@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":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,8EAA8E;AAC9E,yEAAyE;AACzE,4CAA4C;AAC5C,8EAA8E;AAC9E,cAAc,wBAAwB,CAAC;AACvC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yBAAyB,CAAC;AACxC,cAAc,iCAAiC,CAAC;AAChD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qCAAqC,CAAC;AACpD,8EAA8E;AAC9E,uEAAuE;AACvE,gEAAgE;AAChE,8EAA8E;AAC9E,cAAc,qCAAqC,CAAC;AACpD,cAAc,0CAA0C,CAAC;AACzD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yCAAyC,CAAC;AACxD,cAAc,sCAAsC,CAAC;AACrD,cAAc,qCAAqC,CAAC;AACpD,cAAc,0CAA0C,CAAC;AACzD,cAAc,sCAAsC,CAAC;AACrD,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAC9E,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,kCAAkC,CAAC;AACjD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,qCAAqC,CAAC;AACpD,cAAc,sCAAsC,CAAC;AACrD,cAAc,wCAAwC,CAAC;AACvD,cAAc,qCAAqC,CAAC;AACpD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uCAAuC,CAAC;AACtD,cAAc,oCAAoC,CAAC;AACnD,cAAc,wCAAwC,CAAC;AACvD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,oCAAoC,CAAC;AACnD,cAAc,iCAAiC,CAAC;AAChD,cAAc,kCAAkC,CAAC;AACjD,cAAc,qCAAqC,CAAC;AACpD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,mCAAmC,CAAC;AAClD,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,iCAAiC,CAAC;AAChD,8EAA8E;AAC9E,gEAAgE;AAChE,0EAA0E;AAC1E,YAAY;AACZ,8EAA8E;AAC9E,cAAc,gCAAgC,CAAC;AAC/C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iCAAiC,CAAC;AAChD,cAAc,mCAAmC,CAAC;AAElD,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAC9E,OAAO,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/lib/cn.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ClassValue } from "clsx";
|
|
2
|
+
/**
|
|
3
|
+
* Merge class names, resolving conflicting utilities so the *last* one
|
|
4
|
+
* wins — Tailwind's own, this design system's custom scales, and daisyUI's
|
|
5
|
+
* component modifiers alike.
|
|
6
|
+
*/
|
|
7
|
+
export declare function cn(...inputs: ClassValue[]): string;
|
|
8
|
+
/** The font-size steps this package registers with `tailwind-merge`. Exported
|
|
9
|
+
* for `theme-tokens.test.ts`, which checks them against `theme.css`. */
|
|
10
|
+
export declare const REGISTERED_FONT_SIZES: readonly string[];
|
|
11
|
+
//# sourceMappingURL=cn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cn.d.ts","sourceRoot":"","sources":["../../src/lib/cn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAQ,MAAM,MAAM,CAAC;AA4J7C;;;;GAIG;AACH,wBAAgB,EAAE,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,CAElD;AAED;wEACwE;AACxE,eAAO,MAAM,qBAAqB,EAAE,SAAS,MAAM,EAAe,CAAC"}
|
package/dist/lib/cn.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { clsx } from "clsx";
|
|
2
|
+
import { extendTailwindMerge } from "tailwind-merge";
|
|
3
|
+
/**
|
|
4
|
+
* The `--text-*` steps declared in `styles/theme.css`. Kept in sync with
|
|
5
|
+
* that file by `theme-tokens.test.ts`, which parses the stylesheet rather
|
|
6
|
+
* than trusting this list.
|
|
7
|
+
*/
|
|
8
|
+
const FONT_SIZES = [
|
|
9
|
+
"micro",
|
|
10
|
+
"caption",
|
|
11
|
+
"body",
|
|
12
|
+
"prose",
|
|
13
|
+
"title-sm",
|
|
14
|
+
"title",
|
|
15
|
+
"metric",
|
|
16
|
+
"metric-lg",
|
|
17
|
+
];
|
|
18
|
+
/** daisyUI's radius tiers, which its own components read from. */
|
|
19
|
+
const RADII = ["selector", "field", "box"];
|
|
20
|
+
const DAISY_COLOURS = [
|
|
21
|
+
"primary",
|
|
22
|
+
"secondary",
|
|
23
|
+
"accent",
|
|
24
|
+
"neutral",
|
|
25
|
+
"info",
|
|
26
|
+
"success",
|
|
27
|
+
"warning",
|
|
28
|
+
"error",
|
|
29
|
+
];
|
|
30
|
+
const DAISY_SIZES = ["xs", "sm", "md", "lg", "xl"];
|
|
31
|
+
const twMerge = extendTailwindMerge({
|
|
32
|
+
extend: {
|
|
33
|
+
theme: {
|
|
34
|
+
// Registering these as *font sizes* is what stops `text-caption`
|
|
35
|
+
// being mistaken for a colour and dropped. See point 1 above.
|
|
36
|
+
text: [...FONT_SIZES],
|
|
37
|
+
radius: [...RADII],
|
|
38
|
+
},
|
|
39
|
+
classGroups: {
|
|
40
|
+
"daisy-btn-variant": [{ btn: [...DAISY_COLOURS, "ghost", "link"] }],
|
|
41
|
+
"daisy-btn-style": [{ btn: ["outline", "soft", "dash"] }],
|
|
42
|
+
"daisy-btn-size": [{ btn: [...DAISY_SIZES, "wide", "block", "square", "circle"] }],
|
|
43
|
+
"daisy-badge-variant": [{ badge: [...DAISY_COLOURS, "ghost"] }],
|
|
44
|
+
"daisy-badge-style": [{ badge: ["outline", "soft", "dash"] }],
|
|
45
|
+
"daisy-badge-size": [{ badge: [...DAISY_SIZES] }],
|
|
46
|
+
"daisy-alert-variant": [{ alert: ["info", "success", "warning", "error"] }],
|
|
47
|
+
"daisy-alert-style": [{ alert: ["outline", "soft", "dash"] }],
|
|
48
|
+
"daisy-input-variant": [{ input: [...DAISY_COLOURS, "ghost"] }],
|
|
49
|
+
"daisy-select-variant": [{ select: [...DAISY_COLOURS, "ghost"] }],
|
|
50
|
+
"daisy-select-size": [{ select: [...DAISY_SIZES] }],
|
|
51
|
+
"daisy-checkbox-variant": [{ checkbox: [...DAISY_COLOURS] }],
|
|
52
|
+
"daisy-checkbox-size": [{ checkbox: [...DAISY_SIZES] }],
|
|
53
|
+
"daisy-radio-variant": [{ radio: [...DAISY_COLOURS] }],
|
|
54
|
+
"daisy-radio-size": [{ radio: [...DAISY_SIZES] }],
|
|
55
|
+
"daisy-toggle-variant": [{ toggle: [...DAISY_COLOURS] }],
|
|
56
|
+
"daisy-toggle-size": [{ toggle: [...DAISY_SIZES] }],
|
|
57
|
+
"daisy-card-size": [{ card: [...DAISY_SIZES] }],
|
|
58
|
+
"daisy-table-size": [{ table: [...DAISY_SIZES] }],
|
|
59
|
+
"daisy-tabs-style": [{ tabs: ["box", "border", "lift"] }],
|
|
60
|
+
"daisy-tabs-size": [{ tabs: [...DAISY_SIZES] }],
|
|
61
|
+
"daisy-progress-variant": [{ progress: [...DAISY_COLOURS] }],
|
|
62
|
+
"daisy-loading-type": [{ loading: ["spinner", "dots", "ring", "ball", "bars", "infinity"] }],
|
|
63
|
+
"daisy-loading-size": [{ loading: [...DAISY_SIZES] }],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
/**
|
|
68
|
+
* Merge class names, resolving conflicting utilities so the *last* one
|
|
69
|
+
* wins — Tailwind's own, this design system's custom scales, and daisyUI's
|
|
70
|
+
* component modifiers alike.
|
|
71
|
+
*/
|
|
72
|
+
export function cn(...inputs) {
|
|
73
|
+
return twMerge(clsx(inputs));
|
|
74
|
+
}
|
|
75
|
+
/** The font-size steps this package registers with `tailwind-merge`. Exported
|
|
76
|
+
* for `theme-tokens.test.ts`, which checks them against `theme.css`. */
|
|
77
|
+
export const REGISTERED_FONT_SIZES = FONT_SIZES;
|
|
78
|
+
//# sourceMappingURL=cn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cn.js","sourceRoot":"","sources":["../../src/lib/cn.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Money formatting from minor units, without losing precision.
|
|
3
|
+
*
|
|
4
|
+
* # Why this exists
|
|
5
|
+
*
|
|
6
|
+
* Money is the single most duplicated formatter in every application that
|
|
7
|
+
* handles it. The sibling payments codebase this package was generalised
|
|
8
|
+
* for had **four** independent implementations at the time of writing, in
|
|
9
|
+
* four packages, with four different signatures and three different
|
|
10
|
+
* hard-coded tables of which currencies have no decimal places — and one
|
|
11
|
+
* of them divided by a power of ten in floating point, which silently
|
|
12
|
+
* corrupts any amount past 2^53 minor units. All four also got the
|
|
13
|
+
* three-decimal currencies wrong, because every hand-written table
|
|
14
|
+
* remembers `JPY` and `XAF` and forgets `KWD` and `BHD`.
|
|
15
|
+
*
|
|
16
|
+
* # Three decisions, each load-bearing
|
|
17
|
+
*
|
|
18
|
+
* **Minor units in, never a decimal number.** An amount is an integer
|
|
19
|
+
* count of the currency's smallest unit — that is how it is stored, how
|
|
20
|
+
* it is transmitted, and the only representation that cannot be wrong by
|
|
21
|
+
* a rounding error. `bigint` and a decimal-digit `string` are accepted
|
|
22
|
+
* alongside `number` so an `i64` from a Rust backend survives the trip;
|
|
23
|
+
* JSON gives it to you as a string precisely because `number` cannot hold
|
|
24
|
+
* it.
|
|
25
|
+
*
|
|
26
|
+
* **The exponent comes from `Intl`, not from a table here.** `Intl`
|
|
27
|
+
* already ships the ISO 4217 exponent for every currency, so there is no
|
|
28
|
+
* list to maintain and no currency this gets wrong. Verified rather than
|
|
29
|
+
* assumed: `XAF`/`XOF`/`JPY`/`CLP`/`VND` resolve to 0 and `KWD`/`BHD` to
|
|
30
|
+
* 3, which is exactly the set hand-written tables miss.
|
|
31
|
+
*
|
|
32
|
+
* **The scaling is string surgery, never division.** The minor-unit
|
|
33
|
+
* integer is split into whole and fractional parts by digit position and
|
|
34
|
+
* handed to `Intl.NumberFormat` as a string, which accepts arbitrary
|
|
35
|
+
* precision. No value is ever converted to a float, so an amount larger
|
|
36
|
+
* than `Number.MAX_SAFE_INTEGER` formats exactly.
|
|
37
|
+
*/
|
|
38
|
+
/** An integer count of a currency's smallest unit. */
|
|
39
|
+
export type MinorUnits = number | bigint | string;
|
|
40
|
+
export interface FormatMoneyOptions {
|
|
41
|
+
/** BCP 47 tag. Defaults to the runtime's own locale.
|
|
42
|
+
*
|
|
43
|
+
* Pass one explicitly anywhere the output must be stable — a test, a
|
|
44
|
+
* server-rendered page whose HTML must match the client's hydration, or
|
|
45
|
+
* a value a human will compare against a bank statement. */
|
|
46
|
+
locale?: string | undefined;
|
|
47
|
+
/** `symbol` → `FCFA 5,000`; `code` → `XAF 5,000`; `none` → `5,000`.
|
|
48
|
+
*
|
|
49
|
+
* `code` is the honest default for an operator console: currency
|
|
50
|
+
* symbols are ambiguous across locales ($ alone names at least a dozen
|
|
51
|
+
* currencies) and an operator reconciling against a ledger needs the
|
|
52
|
+
* ISO code. */
|
|
53
|
+
display?: "symbol" | "code" | "none";
|
|
54
|
+
/** Force a sign even when positive, e.g. for a ledger delta. */
|
|
55
|
+
signDisplay?: Intl.NumberFormatOptions["signDisplay"];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* How many decimal places this currency has, per ISO 4217, as `Intl`
|
|
59
|
+
* knows it. `2` for a currency `Intl` has never heard of — the majority
|
|
60
|
+
* case, and the same fallback `Intl` itself uses.
|
|
61
|
+
*/
|
|
62
|
+
export declare function currencyExponent(currency: string, locale?: string): number;
|
|
63
|
+
/**
|
|
64
|
+
* `formatMoney(500000, "XAF")` → `"XAF 500,000"`.
|
|
65
|
+
* `formatMoney("1250", "USD")` → `"USD 12.50"`.
|
|
66
|
+
*
|
|
67
|
+
* @throws if `minorUnits` is not an integer. A non-integer minor-unit
|
|
68
|
+
* amount is always a bug — usually a caller that already divided — and
|
|
69
|
+
* silently rounding it here would hide the real error at the point where
|
|
70
|
+
* it is least visible.
|
|
71
|
+
*/
|
|
72
|
+
export declare function formatMoney(minorUnits: MinorUnits, currency: string, options?: FormatMoneyOptions): string;
|
|
73
|
+
//# sourceMappingURL=money.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../../src/lib/money.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,sDAAsD;AACtD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAElD,MAAM,WAAW,kBAAkB;IACjC;;;;gEAI4D;IAC5D,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B;;;;;mBAKe;IACf,OAAO,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;IACrC,gEAAgE;IAChE,WAAW,CAAC,EAAE,IAAI,CAAC,mBAAmB,CAAC,aAAa,CAAC,CAAC;CACvD;AAkBD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAK1E;AAmBD;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,kBAAuB,GAC/B,MAAM,CA8CR"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Money formatting from minor units, without losing precision.
|
|
3
|
+
*
|
|
4
|
+
* # Why this exists
|
|
5
|
+
*
|
|
6
|
+
* Money is the single most duplicated formatter in every application that
|
|
7
|
+
* handles it. The sibling payments codebase this package was generalised
|
|
8
|
+
* for had **four** independent implementations at the time of writing, in
|
|
9
|
+
* four packages, with four different signatures and three different
|
|
10
|
+
* hard-coded tables of which currencies have no decimal places — and one
|
|
11
|
+
* of them divided by a power of ten in floating point, which silently
|
|
12
|
+
* corrupts any amount past 2^53 minor units. All four also got the
|
|
13
|
+
* three-decimal currencies wrong, because every hand-written table
|
|
14
|
+
* remembers `JPY` and `XAF` and forgets `KWD` and `BHD`.
|
|
15
|
+
*
|
|
16
|
+
* # Three decisions, each load-bearing
|
|
17
|
+
*
|
|
18
|
+
* **Minor units in, never a decimal number.** An amount is an integer
|
|
19
|
+
* count of the currency's smallest unit — that is how it is stored, how
|
|
20
|
+
* it is transmitted, and the only representation that cannot be wrong by
|
|
21
|
+
* a rounding error. `bigint` and a decimal-digit `string` are accepted
|
|
22
|
+
* alongside `number` so an `i64` from a Rust backend survives the trip;
|
|
23
|
+
* JSON gives it to you as a string precisely because `number` cannot hold
|
|
24
|
+
* it.
|
|
25
|
+
*
|
|
26
|
+
* **The exponent comes from `Intl`, not from a table here.** `Intl`
|
|
27
|
+
* already ships the ISO 4217 exponent for every currency, so there is no
|
|
28
|
+
* list to maintain and no currency this gets wrong. Verified rather than
|
|
29
|
+
* assumed: `XAF`/`XOF`/`JPY`/`CLP`/`VND` resolve to 0 and `KWD`/`BHD` to
|
|
30
|
+
* 3, which is exactly the set hand-written tables miss.
|
|
31
|
+
*
|
|
32
|
+
* **The scaling is string surgery, never division.** The minor-unit
|
|
33
|
+
* integer is split into whole and fractional parts by digit position and
|
|
34
|
+
* handed to `Intl.NumberFormat` as a string, which accepts arbitrary
|
|
35
|
+
* precision. No value is ever converted to a float, so an amount larger
|
|
36
|
+
* than `Number.MAX_SAFE_INTEGER` formats exactly.
|
|
37
|
+
*/
|
|
38
|
+
const DIGITS_ONLY = /^-?\d+$/;
|
|
39
|
+
/** Cache: constructing an `Intl.NumberFormat` is the expensive part, and a
|
|
40
|
+
* table renders the same currency once per row. */
|
|
41
|
+
const formatters = new Map();
|
|
42
|
+
function formatter(key, options, locale) {
|
|
43
|
+
const cacheKey = `${locale ?? ""}|${key}`;
|
|
44
|
+
let found = formatters.get(cacheKey);
|
|
45
|
+
if (found === undefined) {
|
|
46
|
+
found = new Intl.NumberFormat(locale, options);
|
|
47
|
+
formatters.set(cacheKey, found);
|
|
48
|
+
}
|
|
49
|
+
return found;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* How many decimal places this currency has, per ISO 4217, as `Intl`
|
|
53
|
+
* knows it. `2` for a currency `Intl` has never heard of — the majority
|
|
54
|
+
* case, and the same fallback `Intl` itself uses.
|
|
55
|
+
*/
|
|
56
|
+
export function currencyExponent(currency, locale) {
|
|
57
|
+
return (formatter(`exp:${currency}`, { style: "currency", currency }, locale).resolvedOptions()
|
|
58
|
+
.maximumFractionDigits ?? 2);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Shifts a minor-unit integer right by `exponent` places, as a string.
|
|
62
|
+
*
|
|
63
|
+
* `"500"`, 2 → `"5.00"`; `"5"`, 2 → `"0.05"`; `"-5"`, 2 → `"-0.05"`;
|
|
64
|
+
* `"5000"`, 0 → `"5000"`.
|
|
65
|
+
*/
|
|
66
|
+
function shiftDecimal(digits, exponent) {
|
|
67
|
+
const negative = digits.startsWith("-");
|
|
68
|
+
const abs = negative ? digits.slice(1) : digits;
|
|
69
|
+
const sign = negative ? "-" : "";
|
|
70
|
+
if (exponent === 0)
|
|
71
|
+
return sign + abs;
|
|
72
|
+
const padded = abs.padStart(exponent + 1, "0");
|
|
73
|
+
const whole = padded.slice(0, padded.length - exponent);
|
|
74
|
+
const fraction = padded.slice(padded.length - exponent);
|
|
75
|
+
return `${sign}${whole}.${fraction}`;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* `formatMoney(500000, "XAF")` → `"XAF 500,000"`.
|
|
79
|
+
* `formatMoney("1250", "USD")` → `"USD 12.50"`.
|
|
80
|
+
*
|
|
81
|
+
* @throws if `minorUnits` is not an integer. A non-integer minor-unit
|
|
82
|
+
* amount is always a bug — usually a caller that already divided — and
|
|
83
|
+
* silently rounding it here would hide the real error at the point where
|
|
84
|
+
* it is least visible.
|
|
85
|
+
*/
|
|
86
|
+
export function formatMoney(minorUnits, currency, options = {}) {
|
|
87
|
+
const { locale, display = "code", signDisplay } = options;
|
|
88
|
+
let digits;
|
|
89
|
+
if (typeof minorUnits === "bigint") {
|
|
90
|
+
digits = minorUnits.toString();
|
|
91
|
+
}
|
|
92
|
+
else if (typeof minorUnits === "number") {
|
|
93
|
+
if (!Number.isInteger(minorUnits)) {
|
|
94
|
+
throw new TypeError(`formatMoney expects an integer count of minor units, got ${minorUnits} (${currency}). ` +
|
|
95
|
+
"Pass 1250 for USD 12.50, not 12.5.");
|
|
96
|
+
}
|
|
97
|
+
digits = minorUnits.toFixed(0);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
const trimmed = minorUnits.trim();
|
|
101
|
+
if (!DIGITS_ONLY.test(trimmed)) {
|
|
102
|
+
throw new TypeError(`formatMoney expects an integer count of minor units, got ${JSON.stringify(minorUnits)} (${currency}).`);
|
|
103
|
+
}
|
|
104
|
+
digits = trimmed;
|
|
105
|
+
}
|
|
106
|
+
const exponent = currencyExponent(currency, locale);
|
|
107
|
+
const value = shiftDecimal(digits, exponent);
|
|
108
|
+
const intlOptions = display === "none"
|
|
109
|
+
? {
|
|
110
|
+
style: "decimal",
|
|
111
|
+
minimumFractionDigits: exponent,
|
|
112
|
+
maximumFractionDigits: exponent,
|
|
113
|
+
}
|
|
114
|
+
: {
|
|
115
|
+
style: "currency",
|
|
116
|
+
currency,
|
|
117
|
+
currencyDisplay: display,
|
|
118
|
+
};
|
|
119
|
+
if (signDisplay !== undefined)
|
|
120
|
+
intlOptions.signDisplay = signDisplay;
|
|
121
|
+
return formatter(`fmt:${currency}:${display}:${signDisplay ?? ""}:${exponent}`, intlOptions, locale).format(value);
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=money.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"money.js","sourceRoot":"","sources":["../../src/lib/money.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strips every key whose value is explicitly `undefined`, and narrows the
|
|
3
|
+
* return type to match: `exactOptionalPropertyTypes` (this workspace's own
|
|
4
|
+
* `tsconfig`) makes `{ disabled?: boolean }` reject a spread whose *type*
|
|
5
|
+
* is `{ disabled?: boolean | undefined }` even when the value at runtime is
|
|
6
|
+
* always defined for the keys that matter — every `React.*HTMLAttributes`
|
|
7
|
+
* type declares its optional fields as `T | undefined` explicitly, but
|
|
8
|
+
* Headless UI's own prop types (`ListboxProps`, `MenuItemProps`, `LabelProps`,
|
|
9
|
+
* …) declare theirs as plain `T`. Spreading `...props` from a native-element
|
|
10
|
+
* prop type onto a Headless UI component hits this every time (found
|
|
11
|
+
* porting `select.tsx`/`dropdown-menu.tsx`/`label.tsx` off Radix, D3) —
|
|
12
|
+
* this is the one general fix rather than a one-off cast at each call site.
|
|
13
|
+
*/
|
|
14
|
+
export declare function omitUndefined<T extends Record<string, unknown>>(props: T): {
|
|
15
|
+
[K in keyof T]: Exclude<T[K], undefined>;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=omit-undefined.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"omit-undefined.d.ts","sourceRoot":"","sources":["../../src/lib/omit-undefined.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7D,KAAK,EAAE,CAAC,GACP;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC;CAAE,CAS9C"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strips every key whose value is explicitly `undefined`, and narrows the
|
|
3
|
+
* return type to match: `exactOptionalPropertyTypes` (this workspace's own
|
|
4
|
+
* `tsconfig`) makes `{ disabled?: boolean }` reject a spread whose *type*
|
|
5
|
+
* is `{ disabled?: boolean | undefined }` even when the value at runtime is
|
|
6
|
+
* always defined for the keys that matter — every `React.*HTMLAttributes`
|
|
7
|
+
* type declares its optional fields as `T | undefined` explicitly, but
|
|
8
|
+
* Headless UI's own prop types (`ListboxProps`, `MenuItemProps`, `LabelProps`,
|
|
9
|
+
* …) declare theirs as plain `T`. Spreading `...props` from a native-element
|
|
10
|
+
* prop type onto a Headless UI component hits this every time (found
|
|
11
|
+
* porting `select.tsx`/`dropdown-menu.tsx`/`label.tsx` off Radix, D3) —
|
|
12
|
+
* this is the one general fix rather than a one-off cast at each call site.
|
|
13
|
+
*/
|
|
14
|
+
export function omitUndefined(props) {
|
|
15
|
+
const result = {};
|
|
16
|
+
for (const key of Object.keys(props)) {
|
|
17
|
+
const value = props[key];
|
|
18
|
+
if (value !== undefined) {
|
|
19
|
+
result[key] = value;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=omit-undefined.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"omit-undefined.js","sourceRoot":"","sources":["../../src/lib/omit-undefined.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,aAAa,CAC3B,KAAQ;IAER,MAAM,MAAM,GAAG,EAAkD,CAAC;IAClE,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAgB,EAAE,CAAC;QACpD,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,CAAC,GAAG,CAAC,GAAG,KAAuC,CAAC;QACxD,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vaam-apps/ui",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A dark-first React component library for operator consoles — daisyUI styling, Headless UI behaviour, and a parameterised status-glyph system.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Vaam Apps",
|
|
7
|
+
"homepage": "https://github.com/vaam-apps/ui#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/vaam-apps/ui.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/vaam-apps/ui/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"react",
|
|
17
|
+
"ui",
|
|
18
|
+
"components",
|
|
19
|
+
"design-system",
|
|
20
|
+
"tailwindcss",
|
|
21
|
+
"daisyui",
|
|
22
|
+
"headlessui",
|
|
23
|
+
"dashboard",
|
|
24
|
+
"admin"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"sideEffects": [
|
|
28
|
+
"*.css"
|
|
29
|
+
],
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"default": "./dist/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./styles/theme.css": "./src/styles/theme.css",
|
|
36
|
+
"./package.json": "./package.json"
|
|
37
|
+
},
|
|
38
|
+
"types": "./dist/index.d.ts",
|
|
39
|
+
"main": "./dist/index.js",
|
|
40
|
+
"files": [
|
|
41
|
+
"dist",
|
|
42
|
+
"src",
|
|
43
|
+
"!src/**/*.test.ts",
|
|
44
|
+
"!src/**/*.test.tsx",
|
|
45
|
+
"!src/**/*.stories.tsx",
|
|
46
|
+
"README.md",
|
|
47
|
+
"CHANGELOG.md"
|
|
48
|
+
],
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"daisyui": "^5",
|
|
54
|
+
"react": "^18.3.0 || ^19",
|
|
55
|
+
"react-dom": "^18.3.0 || ^19",
|
|
56
|
+
"tailwindcss": "^4.1.0"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"@headlessui/react": "^2.2.10",
|
|
60
|
+
"@radix-ui/react-dialog": "^1.1.1",
|
|
61
|
+
"class-variance-authority": "^0.7.1",
|
|
62
|
+
"clsx": "^2.1.1",
|
|
63
|
+
"cmdk": "1.1.1",
|
|
64
|
+
"lucide-react": "^1.44.0",
|
|
65
|
+
"react-day-picker": "^10.0.1",
|
|
66
|
+
"tailwind-merge": "^3.6.0",
|
|
67
|
+
"vaul": "1.1.2"
|
|
68
|
+
},
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
"@biomejs/biome": "2.5.13",
|
|
71
|
+
"@storybook/addon-a11y": "10.6.0",
|
|
72
|
+
"@storybook/addon-docs": "10.6.0",
|
|
73
|
+
"@storybook/react-vite": "10.6.0",
|
|
74
|
+
"@tailwindcss/vite": "^4.3.3",
|
|
75
|
+
"@types/node": "^26",
|
|
76
|
+
"@types/react": "^19",
|
|
77
|
+
"@types/react-dom": "^19",
|
|
78
|
+
"daisyui": "^5.7.36",
|
|
79
|
+
"react": "^19",
|
|
80
|
+
"react-dom": "^19",
|
|
81
|
+
"storybook": "10.6.0",
|
|
82
|
+
"tailwindcss": "^4.3.3",
|
|
83
|
+
"typescript": "^7",
|
|
84
|
+
"vite": "^8",
|
|
85
|
+
"vitest": "^5"
|
|
86
|
+
},
|
|
87
|
+
"engines": {
|
|
88
|
+
"node": ">=18"
|
|
89
|
+
},
|
|
90
|
+
"scripts": {
|
|
91
|
+
"build": "tsc -p tsconfig.build.json && node scripts/specify-extensions.mjs dist",
|
|
92
|
+
"build-storybook": "storybook build -o storybook-static",
|
|
93
|
+
"clean": "rm -rf dist storybook-static",
|
|
94
|
+
"format": "biome check --write .",
|
|
95
|
+
"lint": "biome check .",
|
|
96
|
+
"storybook": "storybook dev -p 6006",
|
|
97
|
+
"test": "vitest run",
|
|
98
|
+
"typecheck": "tsc --noEmit"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { cn } from "../../lib/cn";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Inline monospace emphasis for a literal term — a config key, a role key,
|
|
6
|
+
* a job kind, a model or field name.
|
|
7
|
+
*
|
|
8
|
+
* **Not `IdDisplay`, and the distinction is load-bearing.** `IdDisplay` is
|
|
9
|
+
* documented as specifically for `cs_cuid()` values: it truncates to seven
|
|
10
|
+
* characters with no middle ellipsis and offers a copy button, both of
|
|
11
|
+
* which are correct for an opaque 23-character id and wrong for anything
|
|
12
|
+
* else. Three separate agents independently checked their own sites
|
|
13
|
+
* against `schemas/vsms.cstack` and reached the same conclusion — none of
|
|
14
|
+
* these are `Cuid` (`AppClient.clientId` is a plain bounded `String`,
|
|
15
|
+
* `Role.key` is `@regex`-constrained, a route predicate is an operator or
|
|
16
|
+
* prefix, `providerMessageRef` is an *external* provider's id) — so
|
|
17
|
+
* routing them through `IdDisplay` would truncate values that must be read
|
|
18
|
+
* in full.
|
|
19
|
+
*
|
|
20
|
+
* This exists because `font-mono text-foreground` was the most-repeated
|
|
21
|
+
* class string left after the R6 factorization (31 occurrences), and
|
|
22
|
+
* because three route groups had already each created a byte-identical
|
|
23
|
+
* local `Code` component for it. Those three copies are exactly the
|
|
24
|
+
* "same shape, different name" duplication that produced two
|
|
25
|
+
* `ScreenHeader`s and two `MESSAGE_CLASSES` earlier in this effort — the
|
|
26
|
+
* agents that wrote them flagged the risk themselves and could not fix it,
|
|
27
|
+
* because a cross-route duplicate is not a route-scoped change.
|
|
28
|
+
*
|
|
29
|
+
* Renders a `<span>`, not a `<code>`: several call sites sit inside a
|
|
30
|
+
* `<dd>` or a sentence where `<code>` would add semantics the content does
|
|
31
|
+
* not have (a role key in prose is emphasis, not a code sample). A caller
|
|
32
|
+
* that genuinely wants `<code>` semantics should say so rather than have
|
|
33
|
+
* this component guess.
|
|
34
|
+
*/
|
|
35
|
+
export function Code({
|
|
36
|
+
children,
|
|
37
|
+
className,
|
|
38
|
+
}: {
|
|
39
|
+
children: ReactNode;
|
|
40
|
+
className?: string | undefined;
|
|
41
|
+
}) {
|
|
42
|
+
return <span className={cn("font-mono text-foreground", className)}>{children}</span>;
|
|
43
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Check, Copy } from "lucide-react";
|
|
4
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
5
|
+
import { cn } from "../../lib/cn";
|
|
6
|
+
|
|
7
|
+
export interface CopyButtonProps {
|
|
8
|
+
/** The exact text put on the clipboard. Always the full, unformatted,
|
|
9
|
+
* machine-readable value — never what is displayed. A truncated id or a
|
|
10
|
+
* prettily-spaced phone number pasted into a query returns nothing. */
|
|
11
|
+
value: string;
|
|
12
|
+
/** Accessible name. Defaults to `Copy ${value}`, which is right for a
|
|
13
|
+
* short id and wrong for a paragraph — pass something shorter then. */
|
|
14
|
+
label?: string | undefined;
|
|
15
|
+
/** Hide until the containing `.group` is hovered or this button is
|
|
16
|
+
* focused. For dense tables, where a permanently visible icon on every
|
|
17
|
+
* row is noise. Keyboard users always reach it: focus reveals it. */
|
|
18
|
+
revealOnGroupHover?: boolean;
|
|
19
|
+
size?: 12 | 14 | 16;
|
|
20
|
+
className?: string | undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const CONFIRMATION_MS = 1500;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Copy-to-clipboard affordance: a click, then a checkmark for a moment.
|
|
27
|
+
*
|
|
28
|
+
* Extracted because `IdDisplay` and `MsisdnDisplay` each carried their own
|
|
29
|
+
* byte-identical copy — the same `useState`, the same 1500ms timeout, the
|
|
30
|
+
* same icon swap, the same class string — and any component that later
|
|
31
|
+
* wanted the behaviour would have written a third.
|
|
32
|
+
*
|
|
33
|
+
* Two things the duplicated versions got wrong, fixed here rather than
|
|
34
|
+
* copied a third time:
|
|
35
|
+
*
|
|
36
|
+
* - **The timeout was never cleared.** Unmounting within the confirmation
|
|
37
|
+
* window (a row scrolling out of a virtualised table, a drawer closing)
|
|
38
|
+
* left a timer that fired `setCopied` on a dead component. Now cleared
|
|
39
|
+
* on unmount, and any in-flight timer is cleared before a new one
|
|
40
|
+
* starts, so a double-click cannot end the confirmation early.
|
|
41
|
+
* - **A rejected `writeText` was unhandled.** `navigator.clipboard` is
|
|
42
|
+
* unavailable on an insecure origin and can be refused by permissions
|
|
43
|
+
* policy; the bare `await` meant an unhandled rejection and a button
|
|
44
|
+
* that silently did nothing. Failure now leaves the icon unchanged,
|
|
45
|
+
* which is at least honest, and reports the reason to the console.
|
|
46
|
+
*/
|
|
47
|
+
export function CopyButton({
|
|
48
|
+
value,
|
|
49
|
+
label,
|
|
50
|
+
revealOnGroupHover = false,
|
|
51
|
+
size = 12,
|
|
52
|
+
className,
|
|
53
|
+
}: CopyButtonProps) {
|
|
54
|
+
const [copied, setCopied] = useState(false);
|
|
55
|
+
const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
56
|
+
|
|
57
|
+
useEffect(
|
|
58
|
+
() => () => {
|
|
59
|
+
if (timer.current !== null) clearTimeout(timer.current);
|
|
60
|
+
},
|
|
61
|
+
[],
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const copy = useCallback(() => {
|
|
65
|
+
void navigator.clipboard.writeText(value).then(
|
|
66
|
+
() => {
|
|
67
|
+
setCopied(true);
|
|
68
|
+
if (timer.current !== null) clearTimeout(timer.current);
|
|
69
|
+
timer.current = setTimeout(() => setCopied(false), CONFIRMATION_MS);
|
|
70
|
+
},
|
|
71
|
+
(error: unknown) => {
|
|
72
|
+
// Insecure origin, or a permissions policy that forbids it. Leave
|
|
73
|
+
// the icon alone rather than claim a copy that did not happen.
|
|
74
|
+
console.error("Clipboard write refused", error);
|
|
75
|
+
},
|
|
76
|
+
);
|
|
77
|
+
}, [value]);
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<button
|
|
81
|
+
type="button"
|
|
82
|
+
onClick={copy}
|
|
83
|
+
aria-label={label ?? `Copy ${value}`}
|
|
84
|
+
className={cn(
|
|
85
|
+
"shrink-0 text-subtle-foreground transition-opacity hover:text-foreground",
|
|
86
|
+
revealOnGroupHover && "opacity-0 focus-visible:opacity-100 group-hover:opacity-100",
|
|
87
|
+
className,
|
|
88
|
+
)}
|
|
89
|
+
>
|
|
90
|
+
{copied ? (
|
|
91
|
+
<Check size={size} strokeWidth={1.5} className="text-state-success-fg" />
|
|
92
|
+
) : (
|
|
93
|
+
<Copy size={size} strokeWidth={1.5} />
|
|
94
|
+
)}
|
|
95
|
+
</button>
|
|
96
|
+
);
|
|
97
|
+
}
|