@elabs-ai/components-ui 4.0.0 → 4.2.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/README.md +10 -8
- package/dist/chunk-FCH4ZN6G.js +57 -0
- package/dist/chunk-FCH4ZN6G.js.map +1 -0
- package/dist/form.d.ts +28 -0
- package/dist/form.js +123 -0
- package/dist/form.js.map +1 -0
- package/dist/index.d.ts +2597 -188
- package/dist/index.js +12855 -3578
- package/dist/index.js.map +1 -1
- package/dist/lib/cn.js +10 -1
- package/dist/lib/cn.js.map +1 -1
- package/package.json +21 -7
- package/src/__contract__/attribution-panel.contract.test.tsx +49 -0
- package/src/__contract__/badge.contract.test.tsx +49 -0
- package/src/__contract__/bounded-number.contract.test.tsx +49 -0
- package/src/__contract__/button.contract.test.tsx +49 -0
- package/src/__contract__/color-picker.contract.test.tsx +49 -0
- package/src/__contract__/combobox.contract.test.tsx +49 -0
- package/src/__contract__/command-trigger.contract.test.tsx +49 -0
- package/src/__contract__/copyable-value.contract.test.tsx +49 -0
- package/src/__contract__/date-picker.contract.test.tsx +49 -0
- package/src/__contract__/date-range-picker.contract.test.tsx +49 -0
- package/src/__contract__/empty-state.contract.test.tsx +49 -0
- package/src/__contract__/error-state.contract.test.tsx +49 -0
- package/src/__contract__/icon-button.contract.test.tsx +49 -0
- package/src/__contract__/input.contract.test.tsx +49 -0
- package/src/__contract__/key-value-editor.contract.test.tsx +49 -0
- package/src/__contract__/list-editor.contract.test.tsx +49 -0
- package/src/__contract__/loading-state.contract.test.tsx +49 -0
- package/src/__contract__/metric-card.contract.test.tsx +49 -0
- package/src/__contract__/model-picker.contract.test.tsx +49 -0
- package/src/__contract__/nav-notifications.contract.test.tsx +49 -0
- package/src/__contract__/number-input.contract.test.tsx +49 -0
- package/src/__contract__/rating.contract.test.tsx +49 -0
- package/src/__contract__/schema-form.contract.test.tsx +49 -0
- package/src/__contract__/section-header.contract.test.tsx +49 -0
- package/src/__contract__/segmented-field.contract.test.tsx +49 -0
- package/src/__contract__/slider-number.contract.test.tsx +49 -0
- package/src/__contract__/status-badge.contract.test.tsx +49 -0
- package/src/__contract__/tag-input.contract.test.tsx +49 -0
- package/src/__contract__/text.contract.test.tsx +49 -0
- package/src/__contract__/textarea.contract.test.tsx +49 -0
- package/src/__contract__/timeline.contract.test.tsx +49 -0
- package/src/__contract__/toggle.contract.test.tsx +49 -0
- package/src/__contract__/transfer.contract.test.tsx +49 -0
- package/src/__contract__/tree-select.contract.test.tsx +49 -0
- package/src/__contract__/tree.contract.test.tsx +49 -0
- package/src/__contract__/virtual-select.contract.test.tsx +49 -0
- package/src/__contract__/workspace-picker.contract.test.tsx +49 -0
- package/src/_peer-deps.test.ts +59 -0
- package/src/blocks-comparison-table.stories.tsx +2 -2
- package/src/components/accordion/accordion.tsx +13 -4
- package/src/components/advanced-group/advanced-group.tsx +1 -1
- package/src/components/alert/alert.tsx +15 -4
- package/src/components/alert-dialog/alert-dialog.test.tsx +32 -0
- package/src/components/alert-dialog/alert-dialog.tsx +28 -6
- package/src/components/app-shell/app-shell.stories.tsx +44 -2
- package/src/components/app-shell/app-shell.test.tsx +99 -0
- package/src/components/app-shell/app-shell.tsx +56 -4
- package/src/components/app-sidebar/app-sidebar.stories.tsx +9 -1
- package/src/components/attribution-panel/attribution-panel.tsx +3 -1
- package/src/components/attribution-panel/attributions.generated.ts +83 -79
- package/src/components/avatar/avatar.tsx +4 -1
- package/src/components/badge/badge.stories.tsx +8 -0
- package/src/components/badge/badge.test.tsx +8 -0
- package/src/components/badge/badge.tsx +15 -5
- package/src/components/bento-grid/bento-grid.test.tsx +24 -3
- package/src/components/bento-grid/bento-grid.tsx +11 -1
- package/src/components/bounded-number/bounded-number.test.tsx +1 -1
- package/src/components/bounded-number/bounded-number.tsx +78 -75
- package/src/components/breadcrumb/breadcrumb.stories.tsx +38 -0
- package/src/components/breadcrumb/breadcrumb.test.tsx +24 -1
- package/src/components/breadcrumb/breadcrumb.tsx +10 -4
- package/src/components/button/button.stories.tsx +116 -8
- package/src/components/button/button.tsx +18 -12
- package/src/components/button-group/button-group.tsx +1 -1
- package/src/components/calendar/calendar.tsx +3 -2
- package/src/components/card/card.stories.tsx +11 -6
- package/src/components/card/card.test.tsx +24 -2
- package/src/components/card/card.tsx +48 -7
- package/src/components/carousel/carousel.test.tsx +169 -0
- package/src/components/carousel/carousel.tsx +93 -32
- package/src/components/change-review/change-review.stories.tsx +145 -1
- package/src/components/change-review/change-review.test.tsx +143 -0
- package/src/components/change-review/change-review.tsx +214 -24
- package/src/components/checkbox/checkbox.tsx +2 -1
- package/src/components/collapsible-panel/use-collapsible-panel.ts +0 -3
- package/src/components/color-picker/color-picker.tsx +22 -15
- package/src/components/combobox/combobox.stories.tsx +10 -0
- package/src/components/combobox/combobox.test.tsx +54 -0
- package/src/components/combobox/combobox.tsx +25 -11
- package/src/components/command/command.stories.tsx +16 -6
- package/src/components/command/command.test.tsx +88 -0
- package/src/components/command/command.tsx +38 -13
- package/src/components/command/index.ts +1 -0
- package/src/components/command-trigger/command-trigger.stories.tsx +39 -0
- package/src/components/command-trigger/command-trigger.test.tsx +18 -0
- package/src/components/command-trigger/command-trigger.tsx +70 -0
- package/src/components/command-trigger/index.ts +1 -0
- package/src/components/context-menu/context-menu.tsx +16 -7
- package/src/components/context-rail/context-rail.stories.tsx +285 -0
- package/src/components/context-rail/context-rail.test.tsx +263 -0
- package/src/components/context-rail/context-rail.tsx +678 -0
- package/src/components/context-rail/index.ts +1 -0
- package/src/components/copyable-value/copyable-value.stories.tsx +10 -0
- package/src/components/copyable-value/copyable-value.test.tsx +28 -1
- package/src/components/copyable-value/copyable-value.tsx +13 -2
- package/src/components/date-picker/date-picker.stories.tsx +9 -0
- package/src/components/date-picker/date-picker.test.tsx +52 -0
- package/src/components/date-picker/date-picker.tsx +13 -15
- package/src/components/date-range-picker/date-range-picker.stories.tsx +9 -0
- package/src/components/date-range-picker/date-range-picker.test.tsx +76 -1
- package/src/components/date-range-picker/date-range-picker.tsx +39 -28
- package/src/components/descriptions/descriptions.tsx +1 -1
- package/src/components/dialog/dialog.test.tsx +8 -3
- package/src/components/dialog/dialog.tsx +14 -6
- package/src/components/drawer/drawer.tsx +23 -6
- package/src/components/dropdown-menu/dropdown-menu.stories.tsx +1 -1
- package/src/components/dropdown-menu/dropdown-menu.tsx +4 -4
- package/src/components/empty-state/empty-state.test.tsx +26 -0
- package/src/components/empty-state/empty-state.tsx +6 -1
- package/src/components/expand-dialog/expand-dialog.tsx +1 -2
- package/src/components/field/field-context.ts +59 -0
- package/src/components/field/field.stories.tsx +142 -0
- package/src/components/field/field.test.tsx +415 -0
- package/src/components/field/field.tsx +342 -0
- package/src/components/field/index.ts +9 -0
- package/src/components/field-row/field-row.stories.tsx +85 -1
- package/src/components/field-row/field-row.test.tsx +145 -0
- package/src/components/field-row/field-row.tsx +50 -6
- package/src/components/file-upload/file-upload.stories.tsx +16 -0
- package/src/components/file-upload/file-upload.test.tsx +90 -12
- package/src/components/file-upload/file-upload.tsx +164 -31
- package/src/components/form/form.tsx +8 -3
- package/src/components/hover-card/hover-card.tsx +1 -0
- package/src/components/icon-button/icon-button.test.tsx +1 -1
- package/src/components/icon-button/icon-button.tsx +3 -3
- package/src/components/input/input.stories.tsx +121 -0
- package/src/components/input/input.test.tsx +13 -0
- package/src/components/input/input.tsx +8 -4
- package/src/components/input-group/input-group.stories.tsx +74 -0
- package/src/components/input-group/input-group.tsx +18 -9
- package/src/components/input-otp/input-otp.tsx +8 -2
- package/src/components/keyboard-shortcuts/index.ts +6 -0
- package/src/components/keyboard-shortcuts/keyboard-shortcuts.stories.tsx +177 -0
- package/src/components/keyboard-shortcuts/keyboard-shortcuts.test.tsx +73 -0
- package/src/components/keyboard-shortcuts/keyboard-shortcuts.tsx +203 -0
- package/src/components/label/label.tsx +1 -1
- package/src/components/link-preview/link-preview.tsx +1 -1
- package/src/components/list-editor/list-editor.test.tsx +24 -0
- package/src/components/list-editor/list-editor.tsx +126 -52
- package/src/components/locale-provider/index.ts +6 -1
- package/src/components/locale-provider/locale-keys.test.ts +134 -0
- package/src/components/locale-provider/locale-provider.stories.tsx +89 -1
- package/src/components/locale-provider/locale-provider.test.tsx +257 -1
- package/src/components/locale-provider/locale-provider.tsx +63 -8
- package/src/components/locale-provider/messages.ts +822 -2
- package/src/components/match-highlight/match-highlight.stories.tsx +1 -1
- package/src/components/mention-input/mention-input-mirror.stories.tsx +9 -1
- package/src/components/mention-input/mention-input.stories.tsx +3 -3
- package/src/components/mention-input/mention-input.test.tsx +51 -1
- package/src/components/mention-input/mention-input.tsx +66 -46
- package/src/components/mention-input/mention-value.ts +8 -2
- package/src/components/menubar/menubar.tsx +16 -6
- package/src/components/metric-card/metric-card.stories.tsx +5 -3
- package/src/components/metric-card/metric-card.tsx +21 -2
- package/src/components/model-picker/model-picker.stories.tsx +63 -3
- package/src/components/model-picker/model-picker.test.tsx +63 -0
- package/src/components/model-picker/model-picker.tsx +62 -36
- package/src/components/nav-main/nav-main.tsx +8 -4
- package/src/components/nav-notifications/nav-notifications.stories.tsx +110 -0
- package/src/components/nav-notifications/nav-notifications.test.tsx +11 -0
- package/src/components/nav-notifications/nav-notifications.tsx +67 -9
- package/src/components/nav-user/nav-user.tsx +5 -5
- package/src/components/navigation-menu/navigation-menu.stories.tsx +22 -0
- package/src/components/navigation-menu/navigation-menu.test.tsx +174 -0
- package/src/components/navigation-menu/navigation-menu.tsx +204 -15
- package/src/components/number-input/number-input.stories.tsx +7 -3
- package/src/components/number-input/number-input.test.tsx +47 -0
- package/src/components/number-input/number-input.tsx +65 -24
- package/src/components/page-shell/page-shell.stories.tsx +163 -0
- package/src/components/page-shell/page-shell.test.tsx +229 -0
- package/src/components/page-shell/page-shell.tsx +108 -25
- package/src/components/pagination/pagination.test.tsx +109 -0
- package/src/components/pagination/pagination.tsx +27 -19
- package/src/components/popover/popover.tsx +1 -0
- package/src/components/progress/progress.tsx +2 -0
- package/src/components/radio-group/radio-group.tsx +1 -1
- package/src/components/rating/rating.tsx +3 -1
- package/src/components/resizable/resizable.stories.tsx +114 -2
- package/src/components/resizable/resizable.test.tsx +76 -0
- package/src/components/resizable/resizable.tsx +32 -6
- package/src/components/revision-timeline/revision-timeline.stories.tsx +8 -1
- package/src/components/revision-timeline/revision-timeline.test.tsx +26 -0
- package/src/components/revision-timeline/revision-timeline.tsx +53 -5
- package/src/components/schema-form/from-json-schema.test.ts +390 -0
- package/src/components/schema-form/from-json-schema.ts +375 -0
- package/src/components/schema-form/index.ts +81 -0
- package/src/components/schema-form/schema-form-spec.ts +698 -0
- package/src/components/schema-form/schema-form-store.ts +506 -0
- package/src/components/schema-form/schema-form.stories.tsx +566 -0
- package/src/components/schema-form/schema-form.test.tsx +1021 -0
- package/src/components/schema-form/schema-form.tsx +1381 -0
- package/src/components/section-header/section-header.stories.tsx +37 -0
- package/src/components/section-header/section-header.tsx +14 -3
- package/src/components/select/select.tsx +13 -7
- package/src/components/separator/separator.tsx +1 -0
- package/src/components/sheet/sheet.test.tsx +39 -0
- package/src/components/sheet/sheet.tsx +40 -9
- package/src/components/side-dock/index.ts +1 -0
- package/src/components/side-dock/side-dock.stories.tsx +137 -0
- package/src/components/side-dock/side-dock.test.tsx +299 -0
- package/src/components/side-dock/side-dock.tsx +515 -0
- package/src/components/sidebar/index.ts +1 -0
- package/src/components/sidebar/sidebar-frame.test.tsx +162 -0
- package/src/components/sidebar/sidebar.stories.tsx +421 -1
- package/src/components/sidebar/sidebar.test.tsx +71 -2
- package/src/components/sidebar/sidebar.tsx +264 -38
- package/src/components/skeleton/skeleton.tsx +1 -0
- package/src/components/skip-link/index.ts +1 -0
- package/src/components/skip-link/skip-link.stories.tsx +106 -0
- package/src/components/skip-link/skip-link.test.tsx +20 -0
- package/src/components/skip-link/skip-link.tsx +34 -0
- package/src/components/slider/slider.tsx +1 -1
- package/src/components/slider-number/slider-number.stories.tsx +4 -1
- package/src/components/slider-number/slider-number.test.tsx +1 -1
- package/src/components/sonner/sonner.tsx +2 -0
- package/src/components/spinner/spinner.test.tsx +18 -0
- package/src/components/spinner/spinner.tsx +14 -4
- package/src/components/split-panel/index.ts +6 -1
- package/src/components/split-panel/split-panel.stories.tsx +18 -2
- package/src/components/split-panel/split-panel.test.tsx +20 -0
- package/src/components/split-panel/split-panel.tsx +28 -8
- package/src/components/state-panel/state-panel.stories.tsx +52 -0
- package/src/components/state-panel/state-panel.test.tsx +125 -0
- package/src/components/state-panel/state-panel.tsx +110 -24
- package/src/components/status-badge/status-badge.stories.tsx +8 -0
- package/src/components/switch/switch.tsx +3 -1
- package/src/components/table/table.stories.tsx +64 -0
- package/src/components/table/table.test.tsx +149 -1
- package/src/components/table/table.tsx +71 -2
- package/src/components/tabs/index.ts +10 -1
- package/src/components/tabs/tabs.stories.tsx +48 -0
- package/src/components/tabs/tabs.test.tsx +67 -0
- package/src/components/tabs/tabs.tsx +176 -72
- package/src/components/tag-input/tag-input.tsx +6 -3
- package/src/components/team-switcher/team-switcher.tsx +35 -6
- package/src/components/textarea/textarea.tsx +2 -2
- package/src/components/theme-switcher/theme-switcher.stories.tsx +76 -0
- package/src/components/theme-switcher/theme-switcher.test.tsx +182 -0
- package/src/components/theme-switcher/theme-switcher.tsx +140 -12
- package/src/components/theme-switcher/use-theme-transition.ts +2 -2
- package/src/components/timeline/timeline.stories.tsx +27 -0
- package/src/components/timeline/timeline.tsx +10 -3
- package/src/components/toggle/toggle.tsx +7 -2
- package/src/components/toggle-group/toggle-group.tsx +2 -0
- package/src/components/toolbar/toolbar.stories.tsx +10 -2
- package/src/components/tooltip/tooltip.tsx +2 -1
- package/src/components/top-nav/top-nav.tsx +1 -1
- package/src/components/transfer/transfer.tsx +35 -30
- package/src/components/tree/tree.stories.tsx +2 -2
- package/src/components/tree/tree.test.tsx +41 -0
- package/src/components/tree/tree.tsx +91 -31
- package/src/components/tree/use-tree-keyboard.ts +37 -14
- package/src/components/tree-select/tree-select.stories.tsx +9 -0
- package/src/components/tree-select/tree-select.test.tsx +5 -0
- package/src/components/tree-select/tree-select.tsx +11 -14
- package/src/components/typography/prose.test.tsx +3 -2
- package/src/components/typography/prose.tsx +3 -3
- package/src/components/typography/typography.stories.tsx +228 -0
- package/src/components/view-toolbar/view-toolbar.stories.tsx +41 -1
- package/src/components/view-toolbar/view-toolbar.test.tsx +33 -0
- package/src/components/view-toolbar/view-toolbar.tsx +37 -4
- package/src/components/virtual-select/virtual-select.stories.tsx +9 -0
- package/src/components/virtual-select/virtual-select.test.tsx +29 -3
- package/src/components/virtual-select/virtual-select.tsx +56 -18
- package/src/components/wizard/wizard.test.tsx +9 -0
- package/src/components/wizard/wizard.tsx +13 -5
- package/src/components/workspace-picker/index.ts +3 -0
- package/src/components/workspace-picker/workspace-picker-state.ts +50 -0
- package/src/components/workspace-picker/workspace-picker.stories.tsx +126 -0
- package/src/components/workspace-picker/workspace-picker.test.tsx +207 -0
- package/src/components/workspace-picker/workspace-picker.tsx +172 -0
- package/src/illustrations/empty-list-illustration.tsx +26 -0
- package/src/illustrations/error-illustration.tsx +60 -0
- package/src/illustrations/first-run-illustration.tsx +27 -0
- package/src/illustrations/illustration-base.tsx +108 -0
- package/src/illustrations/illustrations.stories.tsx +94 -0
- package/src/illustrations/illustrations.test.tsx +98 -0
- package/src/illustrations/index.ts +16 -0
- package/src/illustrations/no-access-illustration.tsx +45 -0
- package/src/illustrations/no-results-illustration.tsx +46 -0
- package/src/illustrations/offline-illustration.tsx +37 -0
- package/src/illustrations/success-illustration.tsx +40 -0
- package/src/index.ts +80 -2
- package/src/lib/agent-event-model.test.ts +12 -0
- package/src/lib/agent-event-model.ts +42 -0
- package/src/lib/approval-option.test.ts +17 -0
- package/src/lib/approval-option.ts +53 -0
- package/src/lib/check-result.ts +33 -0
- package/src/lib/cn.ts +9 -0
- package/src/lib/csv.ts +48 -0
- package/src/lib/diff-line.test.ts +36 -0
- package/src/lib/diff-line.ts +63 -0
- package/src/lib/diff-rows.test.ts +76 -0
- package/src/lib/diff-rows.ts +94 -0
- package/src/lib/format-duration.test.ts +22 -0
- package/src/lib/format-duration.ts +30 -0
- package/src/lib/has-renderable-content.test.ts +49 -0
- package/src/lib/has-renderable-content.ts +32 -0
- package/src/lib/merge-refs.ts +2 -0
- package/src/lib/operating-mode.test.ts +36 -0
- package/src/lib/operating-mode.ts +81 -0
- package/src/lib/optional-peer.ts +59 -0
- package/src/lib/session-launch.ts +37 -0
- package/src/lib/slash-command.test.ts +44 -0
- package/src/lib/slash-command.ts +45 -0
- package/src/lib/trigger-query.test.ts +94 -0
- package/src/lib/trigger-query.ts +88 -0
- package/src/lib/use-controllable-state.ts +47 -0
- package/src/lib/use-mobile.test.tsx +47 -0
- package/src/lib/use-mobile.ts +34 -12
- package/src/motion.stories.tsx +6 -0
- package/src/templates-object-detail-hub.stories.tsx +2 -2
- package/src/templates-screen-states.stories.tsx +5 -5
- package/src/templates-settings.stories.tsx +1 -1
- package/src/blocks/sidebar-02/app-sidebar.tsx +0 -160
- package/src/blocks/sidebar-02/logo.tsx +0 -18
- package/src/blocks/sidebar-02/nav-main.tsx +0 -13
- package/src/blocks/sidebar-02/nav-notifications.tsx +0 -7
- package/src/blocks/sidebar-02/sidebar-02.stories.tsx +0 -23
- package/src/blocks/sidebar-02/team-switcher.tsx +0 -7
- package/src/blocks/sidebar-04/app-sidebar.tsx +0 -236
- package/src/blocks/sidebar-04/mail-context.tsx +0 -29
- package/src/blocks/sidebar-04/nav-user.tsx +0 -6
- package/src/blocks/sidebar-04/sidebar-04.stories.tsx +0 -150
- package/src/blocks/sidebar-05/app-sidebar.tsx +0 -375
- package/src/blocks/sidebar-05/sidebar-05.stories.tsx +0 -18
- package/src/blocks/sidebar-05/team-switcher.tsx +0 -6
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// GENERATED by scripts/gen-attributions.mjs — DO NOT EDIT BY HAND.
|
|
2
|
-
// Run `pnpm gen
|
|
3
|
-
// scripts/attributions.sources.json. `pnpm
|
|
2
|
+
// Run `pnpm gen` after changing a dependency, a vendored font, or
|
|
3
|
+
// scripts/attributions.sources.json. `pnpm gen:check` fails on a stale copy.
|
|
4
4
|
//
|
|
5
|
-
// 116 entries (data: 2, source:
|
|
5
|
+
// 116 entries (data: 2, source: 18, font: 2, dependency: 94).
|
|
6
6
|
|
|
7
7
|
import type { Attribution } from "./attribution-types";
|
|
8
8
|
|
|
@@ -93,14 +93,14 @@ export const ATTRIBUTIONS: readonly Attribution[] = [
|
|
|
93
93
|
},
|
|
94
94
|
{
|
|
95
95
|
version: null,
|
|
96
|
-
note: "
|
|
97
|
-
id: "
|
|
96
|
+
note: "Agent-session UI logic re-expressed on our tokens: the line-level diff model and its screen-reader polarity prefixes, the scoped N-option permission anatomy, the slash-palette prefix-filter/clamp behaviour, the turn-status elapsed formatter, the hook/lifecycle event line, and the grouped keyboard-shortcuts sheet. No upstream code, styling or terminal palette is shipped.",
|
|
97
|
+
id: "brainless",
|
|
98
98
|
category: "source",
|
|
99
|
-
name: "
|
|
99
|
+
name: "brainless",
|
|
100
100
|
license: "MIT",
|
|
101
|
-
copyright: "Copyright (c)
|
|
102
|
-
url: "https://github.com/
|
|
103
|
-
usedBy: ["@elabs-ai/components-ui"],
|
|
101
|
+
copyright: "Copyright (c) 2026 Ben Swerdlow",
|
|
102
|
+
url: "https://github.com/theswerd/brainless",
|
|
103
|
+
usedBy: ["@elabs-ai/components-ai", "@elabs-ai/components-ui", "@elabs-ai/components-terminal"],
|
|
104
104
|
required: false,
|
|
105
105
|
},
|
|
106
106
|
{
|
|
@@ -115,6 +115,18 @@ export const ATTRIBUTIONS: readonly Attribution[] = [
|
|
|
115
115
|
usedBy: ["@elabs-ai/components-viewer"],
|
|
116
116
|
required: false,
|
|
117
117
|
},
|
|
118
|
+
{
|
|
119
|
+
version: null,
|
|
120
|
+
note: "The editorial chart gallery behind the 2026-09-04 charts gap analysis: 71 files across the charts and tokens packages cite it as the source of a value, a mark shape or a composition (unit decomposition, hairline furniture, the sequential/diverging ramps, the reveal timing), and three copy-own registry blocks (chart-editorial-hourglass, -patchwork, -almanac) reproduce three of its gallery cards' DATA-SHAPE DESCRIPTIONS from that same analysis' one-line prose. No code was copied verbatim — each was re-expressed onto this repo's tokens and primitives. Its LICENSE is PolyForm Noncommercial 1.0.0, read verbatim from the upstream file; GitHub's own detection reports NOASSERTION, which is why nothing flagged it earlier. The licence's Required Notice slot is left at the template's example value, so upstream states no copyright holder and none is claimed here. Noncommercial-only is unresolved for a repo headed for public release — a licensing decision for the maintainer, not something attribution settles.",
|
|
121
|
+
id: "lieflat-charts",
|
|
122
|
+
category: "source",
|
|
123
|
+
name: "lieflat-charts",
|
|
124
|
+
license: "PolyForm-Noncommercial-1.0.0",
|
|
125
|
+
copyright: null,
|
|
126
|
+
url: "https://github.com/larashero3-dotcom/lieflat-charts",
|
|
127
|
+
usedBy: ["@elabs-ai/components-charts", "@elabs-ai/components-tokens"],
|
|
128
|
+
required: false,
|
|
129
|
+
},
|
|
118
130
|
{
|
|
119
131
|
version: null,
|
|
120
132
|
note: "The live-axis tick-interval picker behind live-y-axis.tsx. Upstream now names that function niceTimeInterval.",
|
|
@@ -151,6 +163,18 @@ export const ATTRIBUTIONS: readonly Attribution[] = [
|
|
|
151
163
|
usedBy: ["@elabs-ai/components-editor"],
|
|
152
164
|
required: false,
|
|
153
165
|
},
|
|
166
|
+
{
|
|
167
|
+
version: null,
|
|
168
|
+
note: "Algorithm shape for the framework-free process-mining core: which aggregates a directly-follows graph carries (discoverGraph) and how traces are grouped into variants (extractVariants), with durationStats as their shared summary. Re-typed in TypeScript from the published algorithms, not copied.",
|
|
169
|
+
id: "pm4js",
|
|
170
|
+
category: "source",
|
|
171
|
+
name: "pm4js",
|
|
172
|
+
license: "BSD-3-Clause",
|
|
173
|
+
copyright: "Copyright 2021 Alessandro Berti",
|
|
174
|
+
url: "https://github.com/pm4js/pm4js-core",
|
|
175
|
+
usedBy: ["@elabs-ai/components-process"],
|
|
176
|
+
required: false,
|
|
177
|
+
},
|
|
154
178
|
{
|
|
155
179
|
version: null,
|
|
156
180
|
note: "Beyond the npm dependency below, the shipped dist of react-scroll-area and react-select is modified in patches/ — disclosed here so the change is not invisible.",
|
|
@@ -261,14 +285,38 @@ export const ATTRIBUTIONS: readonly Attribution[] = [
|
|
|
261
285
|
note: null,
|
|
262
286
|
},
|
|
263
287
|
{
|
|
264
|
-
id: "@
|
|
288
|
+
id: "@dnd-kit/core",
|
|
265
289
|
category: "dependency",
|
|
266
|
-
name: "@
|
|
267
|
-
version: "3.
|
|
290
|
+
name: "@dnd-kit/core",
|
|
291
|
+
version: "6.3.1",
|
|
268
292
|
license: "MIT",
|
|
269
|
-
copyright: "
|
|
270
|
-
url: "https://www.npmjs.com/package/@
|
|
271
|
-
usedBy: ["@elabs-ai/components-
|
|
293
|
+
copyright: "Claudéric Demers",
|
|
294
|
+
url: "https://www.npmjs.com/package/@dnd-kit/core",
|
|
295
|
+
usedBy: ["@elabs-ai/components-data"],
|
|
296
|
+
required: false,
|
|
297
|
+
note: null,
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
id: "@dnd-kit/sortable",
|
|
301
|
+
category: "dependency",
|
|
302
|
+
name: "@dnd-kit/sortable",
|
|
303
|
+
version: "10.0.0",
|
|
304
|
+
license: "MIT",
|
|
305
|
+
copyright: "Claudéric Demers",
|
|
306
|
+
url: "https://www.npmjs.com/package/@dnd-kit/sortable",
|
|
307
|
+
usedBy: ["@elabs-ai/components-data"],
|
|
308
|
+
required: false,
|
|
309
|
+
note: null,
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
id: "@dnd-kit/utilities",
|
|
313
|
+
category: "dependency",
|
|
314
|
+
name: "@dnd-kit/utilities",
|
|
315
|
+
version: "3.2.2",
|
|
316
|
+
license: "MIT",
|
|
317
|
+
copyright: "Claudéric Demers",
|
|
318
|
+
url: "https://www.npmjs.com/package/@dnd-kit/utilities",
|
|
319
|
+
usedBy: ["@elabs-ai/components-data"],
|
|
272
320
|
required: false,
|
|
273
321
|
note: null,
|
|
274
322
|
},
|
|
@@ -656,18 +704,6 @@ export const ATTRIBUTIONS: readonly Attribution[] = [
|
|
|
656
704
|
required: false,
|
|
657
705
|
note: null,
|
|
658
706
|
},
|
|
659
|
-
{
|
|
660
|
-
id: "@rive-app/react-webgl2",
|
|
661
|
-
category: "dependency",
|
|
662
|
-
name: "@rive-app/react-webgl2",
|
|
663
|
-
version: "4.28.6",
|
|
664
|
-
license: "MIT",
|
|
665
|
-
copyright: null,
|
|
666
|
-
url: "https://www.npmjs.com/package/@rive-app/react-webgl2",
|
|
667
|
-
usedBy: ["@elabs-ai/components-ai"],
|
|
668
|
-
required: false,
|
|
669
|
-
note: null,
|
|
670
|
-
},
|
|
671
707
|
{
|
|
672
708
|
id: "@streamdown/cjk",
|
|
673
709
|
category: "dependency",
|
|
@@ -901,30 +937,6 @@ export const ATTRIBUTIONS: readonly Attribution[] = [
|
|
|
901
937
|
required: false,
|
|
902
938
|
note: null,
|
|
903
939
|
},
|
|
904
|
-
{
|
|
905
|
-
id: "@xterm/addon-fit",
|
|
906
|
-
category: "dependency",
|
|
907
|
-
name: "@xterm/addon-fit",
|
|
908
|
-
version: "0.11.0",
|
|
909
|
-
license: "MIT",
|
|
910
|
-
copyright: "The xterm.js authors",
|
|
911
|
-
url: "https://www.npmjs.com/package/@xterm/addon-fit",
|
|
912
|
-
usedBy: ["@elabs-ai/components-ai"],
|
|
913
|
-
required: false,
|
|
914
|
-
note: null,
|
|
915
|
-
},
|
|
916
|
-
{
|
|
917
|
-
id: "@xterm/xterm",
|
|
918
|
-
category: "dependency",
|
|
919
|
-
name: "@xterm/xterm",
|
|
920
|
-
version: "6.0.0",
|
|
921
|
-
license: "MIT",
|
|
922
|
-
copyright: null,
|
|
923
|
-
url: "https://www.npmjs.com/package/@xterm/xterm",
|
|
924
|
-
usedBy: ["@elabs-ai/components-ai"],
|
|
925
|
-
required: false,
|
|
926
|
-
note: null,
|
|
927
|
-
},
|
|
928
940
|
{
|
|
929
941
|
id: "ansi-to-react",
|
|
930
942
|
category: "dependency",
|
|
@@ -933,7 +945,7 @@ export const ATTRIBUTIONS: readonly Attribution[] = [
|
|
|
933
945
|
license: "BSD-3-Clause",
|
|
934
946
|
copyright: "Kyle Kelley",
|
|
935
947
|
url: "https://www.npmjs.com/package/ansi-to-react",
|
|
936
|
-
usedBy: ["@elabs-ai/components-
|
|
948
|
+
usedBy: ["@elabs-ai/components-terminal"],
|
|
937
949
|
required: false,
|
|
938
950
|
note: null,
|
|
939
951
|
},
|
|
@@ -949,6 +961,8 @@ export const ATTRIBUTIONS: readonly Attribution[] = [
|
|
|
949
961
|
"@elabs-ai/components-ai",
|
|
950
962
|
"@elabs-ai/components-charts",
|
|
951
963
|
"@elabs-ai/components-editor",
|
|
964
|
+
"@elabs-ai/components-process",
|
|
965
|
+
"@elabs-ai/components-terminal",
|
|
952
966
|
"@elabs-ai/components-ui",
|
|
953
967
|
],
|
|
954
968
|
required: false,
|
|
@@ -998,7 +1012,7 @@ export const ATTRIBUTIONS: readonly Attribution[] = [
|
|
|
998
1012
|
license: "ISC",
|
|
999
1013
|
copyright: "Mike Bostock",
|
|
1000
1014
|
url: "https://www.npmjs.com/package/d3-force",
|
|
1001
|
-
usedBy: ["@elabs-ai/components-flow"],
|
|
1015
|
+
usedBy: ["@elabs-ai/components-charts", "@elabs-ai/components-flow"],
|
|
1002
1016
|
required: false,
|
|
1003
1017
|
note: null,
|
|
1004
1018
|
},
|
|
@@ -1014,6 +1028,18 @@ export const ATTRIBUTIONS: readonly Attribution[] = [
|
|
|
1014
1028
|
required: false,
|
|
1015
1029
|
note: null,
|
|
1016
1030
|
},
|
|
1031
|
+
{
|
|
1032
|
+
id: "d3-hierarchy",
|
|
1033
|
+
category: "dependency",
|
|
1034
|
+
name: "d3-hierarchy",
|
|
1035
|
+
version: "3.1.2",
|
|
1036
|
+
license: "ISC",
|
|
1037
|
+
copyright: "Mike Bostock",
|
|
1038
|
+
url: "https://www.npmjs.com/package/d3-hierarchy",
|
|
1039
|
+
usedBy: ["@elabs-ai/components-charts"],
|
|
1040
|
+
required: false,
|
|
1041
|
+
note: null,
|
|
1042
|
+
},
|
|
1017
1043
|
{
|
|
1018
1044
|
id: "d3-sankey",
|
|
1019
1045
|
category: "dependency",
|
|
@@ -1113,24 +1139,14 @@ export const ATTRIBUTIONS: readonly Attribution[] = [
|
|
|
1113
1139
|
"@elabs-ai/components-editor",
|
|
1114
1140
|
"@elabs-ai/components-flow",
|
|
1115
1141
|
"@elabs-ai/components-maps",
|
|
1142
|
+
"@elabs-ai/components-process",
|
|
1143
|
+
"@elabs-ai/components-terminal",
|
|
1116
1144
|
"@elabs-ai/components-ui",
|
|
1117
1145
|
"@elabs-ai/components-viewer",
|
|
1118
1146
|
],
|
|
1119
1147
|
required: false,
|
|
1120
1148
|
note: null,
|
|
1121
1149
|
},
|
|
1122
|
-
{
|
|
1123
|
-
id: "media-chrome",
|
|
1124
|
-
category: "dependency",
|
|
1125
|
-
name: "media-chrome",
|
|
1126
|
-
version: "4.19.1",
|
|
1127
|
-
license: "MIT",
|
|
1128
|
-
copyright: "@muxinc",
|
|
1129
|
-
url: "https://www.npmjs.com/package/media-chrome",
|
|
1130
|
-
usedBy: ["@elabs-ai/components-ai"],
|
|
1131
|
-
required: false,
|
|
1132
|
-
note: null,
|
|
1133
|
-
},
|
|
1134
1150
|
{
|
|
1135
1151
|
id: "mermaid",
|
|
1136
1152
|
category: "dependency",
|
|
@@ -1139,7 +1155,7 @@ export const ATTRIBUTIONS: readonly Attribution[] = [
|
|
|
1139
1155
|
license: "MIT",
|
|
1140
1156
|
copyright: "Knut Sveidqvist",
|
|
1141
1157
|
url: "https://www.npmjs.com/package/mermaid",
|
|
1142
|
-
usedBy: ["@elabs-ai/components-
|
|
1158
|
+
usedBy: ["@elabs-ai/components-editor"],
|
|
1143
1159
|
required: false,
|
|
1144
1160
|
note: null,
|
|
1145
1161
|
},
|
|
@@ -1179,18 +1195,6 @@ export const ATTRIBUTIONS: readonly Attribution[] = [
|
|
|
1179
1195
|
required: false,
|
|
1180
1196
|
note: null,
|
|
1181
1197
|
},
|
|
1182
|
-
{
|
|
1183
|
-
id: "react-hook-form",
|
|
1184
|
-
category: "dependency",
|
|
1185
|
-
name: "react-hook-form",
|
|
1186
|
-
version: "7.77.0",
|
|
1187
|
-
license: "MIT",
|
|
1188
|
-
copyright: "Beier(Bill) Luo",
|
|
1189
|
-
url: "https://www.npmjs.com/package/react-hook-form",
|
|
1190
|
-
usedBy: ["@elabs-ai/components-ui"],
|
|
1191
|
-
required: false,
|
|
1192
|
-
note: null,
|
|
1193
|
-
},
|
|
1194
1198
|
{
|
|
1195
1199
|
id: "react-jsx-parser",
|
|
1196
1200
|
category: "dependency",
|
|
@@ -1415,7 +1419,7 @@ export const ATTRIBUTIONS: readonly Attribution[] = [
|
|
|
1415
1419
|
license: "MIT",
|
|
1416
1420
|
copyright: "Colin McDonnell",
|
|
1417
1421
|
url: "https://www.npmjs.com/package/zod",
|
|
1418
|
-
usedBy: ["@elabs-ai/components-ai"
|
|
1422
|
+
usedBy: ["@elabs-ai/components-ai"],
|
|
1419
1423
|
required: false,
|
|
1420
1424
|
note: null,
|
|
1421
1425
|
},
|
|
@@ -9,6 +9,7 @@ export const Avatar = forwardRef<
|
|
|
9
9
|
return (
|
|
10
10
|
<AvatarPrimitive.Root
|
|
11
11
|
ref={ref}
|
|
12
|
+
data-slot="avatar"
|
|
12
13
|
className={cn("relative flex size-9 shrink-0 overflow-hidden rounded-full", className)}
|
|
13
14
|
{...props}
|
|
14
15
|
/>
|
|
@@ -21,6 +22,7 @@ export const AvatarImage = forwardRef<
|
|
|
21
22
|
return (
|
|
22
23
|
<AvatarPrimitive.Image
|
|
23
24
|
ref={ref}
|
|
25
|
+
data-slot="avatar-image"
|
|
24
26
|
className={cn(
|
|
25
27
|
"aspect-square size-full animate-in fade-in duration-base ease-entrance",
|
|
26
28
|
className,
|
|
@@ -36,8 +38,9 @@ export const AvatarFallback = forwardRef<
|
|
|
36
38
|
return (
|
|
37
39
|
<AvatarPrimitive.Fallback
|
|
38
40
|
ref={ref}
|
|
41
|
+
data-slot="avatar-fallback"
|
|
39
42
|
className={cn(
|
|
40
|
-
"flex size-full items-center justify-center rounded-full bg-muted text-
|
|
43
|
+
"flex size-full items-center justify-center rounded-full bg-muted text-meta font-medium text-muted-foreground",
|
|
41
44
|
className,
|
|
42
45
|
)}
|
|
43
46
|
{...props}
|
|
@@ -5,6 +5,14 @@ const meta = {
|
|
|
5
5
|
title: "Core/Badge",
|
|
6
6
|
component: Badge,
|
|
7
7
|
tags: ["autodocs"],
|
|
8
|
+
parameters: {
|
|
9
|
+
docs: {
|
|
10
|
+
description: {
|
|
11
|
+
component:
|
|
12
|
+
"A neutral label or count. The closed seven-state execution-status vocabulary — each state carrying its own glyph, so it survives greyscale — is `Core/StatusBadge`; see [Choosing between similar components](?path=/docs/docs-choosing-between-similar-components--docs). Reach for `Badge` when the text IS the meaning and the tone is decoration.",
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
8
16
|
args: { children: "Badge" },
|
|
9
17
|
argTypes: {
|
|
10
18
|
variant: {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createRef } from "react";
|
|
1
2
|
import { describe, expect, it } from "vitest";
|
|
2
3
|
import { render, screen } from "@testing-library/react";
|
|
3
4
|
import { Badge } from "./badge";
|
|
@@ -7,4 +8,11 @@ describe("Badge", () => {
|
|
|
7
8
|
render(<Badge>New</Badge>);
|
|
8
9
|
expect(screen.getByText("New")).toBeInTheDocument();
|
|
9
10
|
});
|
|
11
|
+
|
|
12
|
+
it("forwards its ref to the rendered <span>", () => {
|
|
13
|
+
const ref = createRef<HTMLSpanElement>();
|
|
14
|
+
render(<Badge ref={ref}>New</Badge>);
|
|
15
|
+
expect(ref.current).toBeInstanceOf(HTMLSpanElement);
|
|
16
|
+
expect(ref.current).toBe(screen.getByText("New"));
|
|
17
|
+
});
|
|
10
18
|
});
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { type HTMLAttributes } from "react";
|
|
1
|
+
import { forwardRef, type HTMLAttributes } from "react";
|
|
2
2
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
3
|
import { cn } from "../../lib/cn";
|
|
4
4
|
|
|
5
5
|
export const badgeVariants = cva(
|
|
6
|
-
"inline-flex items-center gap-1 rounded-full border px-2.5 py-0.5 text-meta font-medium transition-colors focus
|
|
6
|
+
"inline-flex items-center gap-1 rounded-full border px-2.5 py-0.5 text-meta font-medium transition-colors focus-ring focus-visible:ring-offset-2",
|
|
7
7
|
{
|
|
8
8
|
variants: {
|
|
9
9
|
variant: {
|
|
@@ -23,6 +23,16 @@ export const badgeVariants = cva(
|
|
|
23
23
|
export interface BadgeProps
|
|
24
24
|
extends HTMLAttributes<HTMLSpanElement>, VariantProps<typeof badgeVariants> {}
|
|
25
25
|
|
|
26
|
-
export
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
export const Badge = forwardRef<HTMLSpanElement, BadgeProps>(function Badge(
|
|
27
|
+
{ className, variant, ...props },
|
|
28
|
+
ref,
|
|
29
|
+
) {
|
|
30
|
+
return (
|
|
31
|
+
<span
|
|
32
|
+
ref={ref}
|
|
33
|
+
data-slot="badge"
|
|
34
|
+
className={cn(badgeVariants({ variant }), className)}
|
|
35
|
+
{...props}
|
|
36
|
+
/>
|
|
37
|
+
);
|
|
38
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { render, screen } from "@testing-library/react";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
1
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
3
|
|
|
4
4
|
import { BentoGrid, BentoGridItem } from "./bento-grid";
|
|
5
5
|
|
|
@@ -104,7 +104,7 @@ describe("BentoGridItem", () => {
|
|
|
104
104
|
// The tile stays a div; the inner anchor is the focusable element.
|
|
105
105
|
const item = screen.getByTestId("item");
|
|
106
106
|
expect(item.tagName).toBe("DIV");
|
|
107
|
-
expect(item.className).toMatch(/focus-within
|
|
107
|
+
expect(item.className).toMatch(/focus-ring-within/);
|
|
108
108
|
expect(screen.getByRole("link", { name: "go" })).toBeInTheDocument();
|
|
109
109
|
});
|
|
110
110
|
|
|
@@ -116,4 +116,25 @@ describe("BentoGridItem", () => {
|
|
|
116
116
|
);
|
|
117
117
|
expect(screen.getByTestId("item").className).toMatch(/from-primary/);
|
|
118
118
|
});
|
|
119
|
+
|
|
120
|
+
it("cancels a pending spotlight rAF on unmount", () => {
|
|
121
|
+
const rafSpy = vi.spyOn(window, "requestAnimationFrame").mockReturnValue(7);
|
|
122
|
+
const cafSpy = vi.spyOn(window, "cancelAnimationFrame").mockImplementation(() => {});
|
|
123
|
+
const { unmount } = render(
|
|
124
|
+
<BentoGridItem spotlight data-testid="item">
|
|
125
|
+
content
|
|
126
|
+
</BentoGridItem>,
|
|
127
|
+
);
|
|
128
|
+
const item = screen.getByTestId("item");
|
|
129
|
+
fireEvent.mouseMove(item, { clientX: 10, clientY: 10 });
|
|
130
|
+
expect(rafSpy).toHaveBeenCalled();
|
|
131
|
+
|
|
132
|
+
unmount();
|
|
133
|
+
// A mouse move right before the tile unmounts (removed from the grid
|
|
134
|
+
// while hovered) must not leave a scheduled frame pending after teardown.
|
|
135
|
+
expect(cafSpy).toHaveBeenCalledWith(7);
|
|
136
|
+
|
|
137
|
+
rafSpy.mockRestore();
|
|
138
|
+
cafSpy.mockRestore();
|
|
139
|
+
});
|
|
119
140
|
});
|
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
createContext,
|
|
35
35
|
forwardRef,
|
|
36
36
|
use,
|
|
37
|
+
useEffect,
|
|
37
38
|
useRef,
|
|
38
39
|
useCallback,
|
|
39
40
|
type CSSProperties,
|
|
@@ -178,7 +179,7 @@ export const bentoGridItemVariants = cva(
|
|
|
178
179
|
* (elevation + edge) is on the base — every tile lifts, clickable or not.
|
|
179
180
|
*/
|
|
180
181
|
interactive: {
|
|
181
|
-
true: "cursor-pointer focus-
|
|
182
|
+
true: "cursor-pointer focus-ring-within",
|
|
182
183
|
false: "",
|
|
183
184
|
},
|
|
184
185
|
},
|
|
@@ -244,6 +245,15 @@ export const BentoGridItem = forwardRef<HTMLDivElement, BentoGridItemProps>(func
|
|
|
244
245
|
// rAF ref so we only schedule one frame at a time.
|
|
245
246
|
const rafRef = useRef<number>(0);
|
|
246
247
|
|
|
248
|
+
// Cancel a pending frame on unmount — otherwise a mouse move right before
|
|
249
|
+
// the tile unmounts (removed from the grid while hovered) still fires the
|
|
250
|
+
// scheduled callback after teardown.
|
|
251
|
+
useEffect(() => {
|
|
252
|
+
return () => {
|
|
253
|
+
if (rafRef.current) cancelAnimationFrame(rafRef.current);
|
|
254
|
+
};
|
|
255
|
+
}, []);
|
|
256
|
+
|
|
247
257
|
const handleMouseMove = useCallback(
|
|
248
258
|
(e: React.MouseEvent<HTMLDivElement>) => {
|
|
249
259
|
if (!spotlightOn || !tileRef.current || !overlayRef.current) return;
|
|
@@ -18,7 +18,7 @@ describe("BoundedNumber", () => {
|
|
|
18
18
|
it("hides the empty label once a value is set", () => {
|
|
19
19
|
render(<BoundedNumber aria-label="Limit" defaultValue={250} />);
|
|
20
20
|
expect(screen.queryByText("No limit")).not.toBeInTheDocument();
|
|
21
|
-
expect(screen.getByRole("spinbutton", { name: "Limit" })).toHaveValue(250);
|
|
21
|
+
expect(screen.getByRole("spinbutton", { name: "Limit" })).toHaveValue("250");
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
it("hides the empty label while focused so the field reads as truly empty", async () => {
|
|
@@ -28,83 +28,86 @@ export interface BoundedNumberProps extends NumberInputProps {
|
|
|
28
28
|
* `null` AND the field is not focused, so the underlying input stays fully
|
|
29
29
|
* clickable/typable and still round-trips to `null` on an empty blur.
|
|
30
30
|
*/
|
|
31
|
-
export const BoundedNumber = forwardRef<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
ref,
|
|
43
|
-
) {
|
|
44
|
-
const { t } = useLocale();
|
|
45
|
-
const resolvedEmptyLabel = emptyLabel ?? t("ui.boundedNumber.emptyLabel");
|
|
46
|
-
const isControlled = valueProp !== undefined;
|
|
47
|
-
const [internalValue, setInternalValue] = useState<number | null>(
|
|
48
|
-
defaultValue !== undefined ? defaultValue : null,
|
|
49
|
-
);
|
|
50
|
-
const [focused, setFocused] = useState(false);
|
|
51
|
-
const currentValue = isControlled ? valueProp : internalValue;
|
|
52
|
-
|
|
53
|
-
const handleValueChange = useCallback(
|
|
54
|
-
(next: number | null) => {
|
|
55
|
-
if (!isControlled) setInternalValue(next);
|
|
56
|
-
onValueChange?.(next);
|
|
31
|
+
export const BoundedNumber = forwardRef<HTMLInputElement, BoundedNumberProps>(
|
|
32
|
+
function BoundedNumber(
|
|
33
|
+
{
|
|
34
|
+
value: valueProp,
|
|
35
|
+
defaultValue,
|
|
36
|
+
onValueChange,
|
|
37
|
+
emptyLabel,
|
|
38
|
+
onFocus,
|
|
39
|
+
onBlur,
|
|
40
|
+
className,
|
|
41
|
+
...props
|
|
57
42
|
},
|
|
58
|
-
|
|
59
|
-
)
|
|
43
|
+
ref,
|
|
44
|
+
) {
|
|
45
|
+
const { t } = useLocale();
|
|
46
|
+
const resolvedEmptyLabel = emptyLabel ?? t("ui.boundedNumber.emptyLabel");
|
|
47
|
+
const isControlled = valueProp !== undefined;
|
|
48
|
+
const [internalValue, setInternalValue] = useState<number | null>(
|
|
49
|
+
defaultValue !== undefined ? defaultValue : null,
|
|
50
|
+
);
|
|
51
|
+
const [focused, setFocused] = useState(false);
|
|
52
|
+
const currentValue = isControlled ? valueProp : internalValue;
|
|
60
53
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
54
|
+
const handleValueChange = useCallback(
|
|
55
|
+
(next: number | null) => {
|
|
56
|
+
if (!isControlled) setInternalValue(next);
|
|
57
|
+
onValueChange?.(next);
|
|
58
|
+
},
|
|
59
|
+
[isControlled, onValueChange],
|
|
60
|
+
);
|
|
68
61
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
62
|
+
const handleFocus = useCallback(
|
|
63
|
+
(event: FocusEvent<HTMLDivElement>) => {
|
|
64
|
+
setFocused(true);
|
|
65
|
+
onFocus?.(event);
|
|
66
|
+
},
|
|
67
|
+
[onFocus],
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const handleBlur = useCallback(
|
|
71
|
+
(event: FocusEvent<HTMLDivElement>) => {
|
|
72
|
+
setFocused(false);
|
|
73
|
+
onBlur?.(event);
|
|
74
|
+
},
|
|
75
|
+
[onBlur],
|
|
76
|
+
);
|
|
76
77
|
|
|
77
|
-
|
|
78
|
+
const showEmptyLabel = currentValue == null && !focused;
|
|
78
79
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
80
|
+
return (
|
|
81
|
+
// `className` lands on the ROOT (the positioning context), not on the
|
|
82
|
+
// inner `NumberInput`: a width utility on the inner control would resolve
|
|
83
|
+
// against a shrink-to-fit wrapper and silently do nothing, so
|
|
84
|
+
// `className="w-full"` could never widen the field. `w-36` is this
|
|
85
|
+
// preset's own default width (`NumberInput` itself is unconstrained and
|
|
86
|
+
// fills its container) — `cn()` lets the caller override it, and the
|
|
87
|
+
// inner `NumberInput` simply fills the root via `className="w-full"`.
|
|
88
|
+
<div className={cn("relative inline-block w-36", className)} data-slot="bounded-number">
|
|
89
|
+
<NumberInput
|
|
90
|
+
ref={ref}
|
|
91
|
+
value={currentValue}
|
|
92
|
+
onValueChange={handleValueChange}
|
|
93
|
+
onFocus={handleFocus}
|
|
94
|
+
onBlur={handleBlur}
|
|
95
|
+
className="w-full"
|
|
96
|
+
{...props}
|
|
97
|
+
/>
|
|
98
|
+
{showEmptyLabel && (
|
|
99
|
+
<span
|
|
100
|
+
aria-hidden="true"
|
|
101
|
+
data-slot="bounded-number-empty-label"
|
|
102
|
+
className={cn(
|
|
103
|
+
"pointer-events-none absolute inset-0 flex items-center justify-center",
|
|
104
|
+
"text-body text-muted-foreground",
|
|
105
|
+
)}
|
|
106
|
+
>
|
|
107
|
+
{resolvedEmptyLabel}
|
|
108
|
+
</span>
|
|
109
|
+
)}
|
|
110
|
+
</div>
|
|
111
|
+
);
|
|
112
|
+
},
|
|
113
|
+
);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { expect } from "storybook/test";
|
|
2
3
|
import {
|
|
3
4
|
Breadcrumb,
|
|
4
5
|
BreadcrumbItem,
|
|
@@ -45,3 +46,40 @@ export const Default: Story = {
|
|
|
45
46
|
</Breadcrumb>
|
|
46
47
|
),
|
|
47
48
|
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* #310 — `BreadcrumbLink` had no focus-visible styling of its own, so keyboard
|
|
52
|
+
* focus fell straight through to the browser's own default ring instead of
|
|
53
|
+
* the library's token-driven indicator (`focus-ring`, ADR 0027) every other
|
|
54
|
+
* interactive control carries. A class-string assertion would be worthless
|
|
55
|
+
* (it wouldn't prove anything actually paints) so this locks the RENDERED
|
|
56
|
+
* indicator: Tab to the first link and assert a real box-shadow exists,
|
|
57
|
+
* mirroring `Button`'s `CompoundFocusIndicator` lock.
|
|
58
|
+
*/
|
|
59
|
+
export const FocusIndicator: Story = {
|
|
60
|
+
name: "Focus indicator (#310)",
|
|
61
|
+
render: () => (
|
|
62
|
+
<Breadcrumb>
|
|
63
|
+
<BreadcrumbList>
|
|
64
|
+
<BreadcrumbItem>
|
|
65
|
+
<BreadcrumbLink href="#">Home</BreadcrumbLink>
|
|
66
|
+
</BreadcrumbItem>
|
|
67
|
+
<BreadcrumbSeparator />
|
|
68
|
+
<BreadcrumbItem>
|
|
69
|
+
<BreadcrumbPage>brand-ui</BreadcrumbPage>
|
|
70
|
+
</BreadcrumbItem>
|
|
71
|
+
</BreadcrumbList>
|
|
72
|
+
</Breadcrumb>
|
|
73
|
+
),
|
|
74
|
+
play: async ({ canvas, userEvent }) => {
|
|
75
|
+
const link = canvas.getByRole("link", { name: "Home" });
|
|
76
|
+
|
|
77
|
+
// Tab rather than .focus(): `:focus-visible` is what `focus-ring` keys
|
|
78
|
+
// on, and a programmatic focus does not reliably match it.
|
|
79
|
+
await userEvent.tab();
|
|
80
|
+
await expect(link).toHaveFocus();
|
|
81
|
+
|
|
82
|
+
const focused = getComputedStyle(link);
|
|
83
|
+
await expect(focused.boxShadow).not.toBe("none");
|
|
84
|
+
},
|
|
85
|
+
};
|