@nadicodeai/ui 12.1.101 → 12.1.103
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/accordion.d.ts +38 -12
- package/dist/components/accordion.d.ts.map +1 -1
- package/dist/components/accordion.js +36 -15
- package/dist/components/account-menu.d.ts +18 -18
- package/dist/components/account-menu.d.ts.map +1 -1
- package/dist/components/account-menu.js +17 -13
- package/dist/components/action-menu.d.ts +20 -19
- package/dist/components/action-menu.d.ts.map +1 -1
- package/dist/components/action-menu.js +22 -15
- package/dist/components/action-status.d.ts +14 -10
- package/dist/components/action-status.d.ts.map +1 -1
- package/dist/components/action-status.js +14 -10
- package/dist/components/activity-calendar.d.ts +3 -1
- package/dist/components/activity-calendar.d.ts.map +1 -1
- package/dist/components/activity-calendar.js +16 -2
- package/dist/components/activity-feed.d.ts +2 -2
- package/dist/components/activity-feed.d.ts.map +1 -1
- package/dist/components/activity-feed.js +14 -5
- package/dist/components/agent-card.d.ts +11 -5
- package/dist/components/agent-card.d.ts.map +1 -1
- package/dist/components/agent-card.js +13 -7
- package/dist/components/agent-list-row.js +1 -1
- package/dist/components/agent-orb-appearance.d.ts +7 -1
- package/dist/components/agent-orb-appearance.d.ts.map +1 -1
- package/dist/components/agent-orb-appearance.js +26 -0
- package/dist/components/agent-profile.d.ts +8 -7
- package/dist/components/agent-profile.d.ts.map +1 -1
- package/dist/components/agent-profile.js +6 -6
- package/dist/components/alert-dialog.d.ts +25 -16
- package/dist/components/alert-dialog.d.ts.map +1 -1
- package/dist/components/alert-dialog.js +30 -18
- package/dist/components/alert.d.ts +1 -1
- package/dist/components/avatar.d.ts +83 -17
- package/dist/components/avatar.d.ts.map +1 -1
- package/dist/components/avatar.js +89 -23
- package/dist/components/badge.d.ts +59 -8
- package/dist/components/badge.d.ts.map +1 -1
- package/dist/components/badge.js +57 -14
- package/dist/components/breadcrumb.d.ts +18 -7
- package/dist/components/breadcrumb.d.ts.map +1 -1
- package/dist/components/breadcrumb.js +37 -13
- package/dist/components/button-group.d.ts +12 -15
- package/dist/components/button-group.d.ts.map +1 -1
- package/dist/components/button-group.js +13 -17
- package/dist/components/button.d.ts +126 -23
- package/dist/components/button.d.ts.map +1 -1
- package/dist/components/button.js +164 -39
- package/dist/components/calendar.d.ts +26 -15
- package/dist/components/calendar.d.ts.map +1 -1
- package/dist/components/calendar.js +60 -46
- package/dist/components/card-row.d.ts +13 -5
- package/dist/components/card-row.d.ts.map +1 -1
- package/dist/components/card-row.js +24 -10
- package/dist/components/card.d.ts +6 -12
- package/dist/components/card.d.ts.map +1 -1
- package/dist/components/card.js +11 -16
- package/dist/components/carousel.d.ts +3 -3
- package/dist/components/carousel.d.ts.map +1 -1
- package/dist/components/carousel.js +7 -7
- package/dist/components/catalog-drawer.d.ts +7 -7
- package/dist/components/catalog-drawer.d.ts.map +1 -1
- package/dist/components/catalog-drawer.js +6 -6
- package/dist/components/chart-area-stacked.d.ts +1 -1
- package/dist/components/chart-area-stacked.d.ts.map +1 -1
- package/dist/components/chart-area-stacked.js +4 -4
- package/dist/components/chart-axis.d.ts +14 -1
- package/dist/components/chart-axis.d.ts.map +1 -1
- package/dist/components/chart-axis.js +11 -1
- package/dist/components/chart-bar-horizontal.d.ts +2 -2
- package/dist/components/chart-bar-horizontal.d.ts.map +1 -1
- package/dist/components/chart-bar-horizontal.js +14 -5
- package/dist/components/chart-bar-multiple.d.ts +1 -1
- package/dist/components/chart-bar-multiple.d.ts.map +1 -1
- package/dist/components/chart-bar-multiple.js +3 -3
- package/dist/components/chart-bar-stacked.d.ts +1 -1
- package/dist/components/chart-bar-stacked.d.ts.map +1 -1
- package/dist/components/chart-bar-stacked.js +3 -3
- package/dist/components/chart-card.d.ts +17 -6
- package/dist/components/chart-card.d.ts.map +1 -1
- package/dist/components/chart-card.js +11 -4
- package/dist/components/chart-date-range-control.d.ts +18 -9
- package/dist/components/chart-date-range-control.d.ts.map +1 -1
- package/dist/components/chart-date-range-control.js +43 -47
- package/dist/components/chart-sparkline.d.ts +4 -3
- package/dist/components/chart-sparkline.d.ts.map +1 -1
- package/dist/components/chart-sparkline.js +6 -5
- package/dist/components/check-counts.d.ts +44 -0
- package/dist/components/check-counts.d.ts.map +1 -0
- package/dist/components/check-counts.js +51 -0
- package/dist/components/checkbox.d.ts +39 -11
- package/dist/components/checkbox.d.ts.map +1 -1
- package/dist/components/checkbox.js +38 -11
- package/dist/components/choicebox.d.ts +96 -0
- package/dist/components/choicebox.d.ts.map +1 -0
- package/dist/components/choicebox.js +113 -0
- package/dist/components/clearable-input.d.ts +57 -0
- package/dist/components/clearable-input.d.ts.map +1 -0
- package/dist/components/clearable-input.js +58 -0
- package/dist/components/code-block.d.ts +77 -17
- package/dist/components/code-block.d.ts.map +1 -1
- package/dist/components/code-block.js +85 -15
- package/dist/components/collapsible.d.ts +27 -10
- package/dist/components/collapsible.d.ts.map +1 -1
- package/dist/components/collapsible.js +24 -9
- package/dist/components/collection-filters.d.ts +3 -1
- package/dist/components/collection-filters.d.ts.map +1 -1
- package/dist/components/collection-filters.js +7 -9
- package/dist/components/collection-page.d.ts +31 -10
- package/dist/components/collection-page.d.ts.map +1 -1
- package/dist/components/collection-page.js +14 -13
- package/dist/components/collection-search.d.ts +32 -10
- package/dist/components/collection-search.d.ts.map +1 -1
- package/dist/components/collection-search.js +24 -13
- package/dist/components/collection-sort.d.ts.map +1 -1
- package/dist/components/collection-sort.js +2 -2
- package/dist/components/collection-toolbar.d.ts +5 -2
- package/dist/components/collection-toolbar.d.ts.map +1 -1
- package/dist/components/collection-toolbar.js +7 -4
- package/dist/components/combobox.d.ts +97 -22
- package/dist/components/combobox.d.ts.map +1 -1
- package/dist/components/combobox.js +80 -28
- package/dist/components/command.d.ts +34 -21
- package/dist/components/command.d.ts.map +1 -1
- package/dist/components/command.js +33 -25
- package/dist/components/confirm-dialog.d.ts +23 -6
- package/dist/components/confirm-dialog.d.ts.map +1 -1
- package/dist/components/confirm-dialog.js +32 -13
- package/dist/components/context-menu.d.ts +14 -10
- package/dist/components/context-menu.d.ts.map +1 -1
- package/dist/components/context-menu.js +17 -12
- package/dist/components/copy-button.d.ts +5 -2
- package/dist/components/copy-button.d.ts.map +1 -1
- package/dist/components/copy-button.js +16 -8
- package/dist/components/data-table.d.ts +120 -54
- package/dist/components/data-table.d.ts.map +1 -1
- package/dist/components/data-table.js +163 -92
- package/dist/components/desktop-shell.d.ts +4 -2
- package/dist/components/desktop-shell.d.ts.map +1 -1
- package/dist/components/desktop-shell.js +3 -3
- package/dist/components/dialog.d.ts +63 -24
- package/dist/components/dialog.d.ts.map +1 -1
- package/dist/components/dialog.js +52 -31
- package/dist/components/disclosure-row.d.ts +1 -2
- package/dist/components/disclosure-row.d.ts.map +1 -1
- package/dist/components/disclosure-row.js +1 -2
- package/dist/components/disclosure.d.ts +1 -1
- package/dist/components/disclosure.js +1 -1
- package/dist/components/drawer.d.ts +33 -12
- package/dist/components/drawer.d.ts.map +1 -1
- package/dist/components/drawer.js +47 -35
- package/dist/components/dropdown-menu.d.ts +59 -14
- package/dist/components/dropdown-menu.d.ts.map +1 -1
- package/dist/components/dropdown-menu.js +58 -26
- package/dist/components/empty-state.d.ts +35 -12
- package/dist/components/empty-state.d.ts.map +1 -1
- package/dist/components/empty-state.js +35 -17
- package/dist/components/entry-scene.d.ts +4 -4
- package/dist/components/entry-scene.d.ts.map +1 -1
- package/dist/components/entry-scene.js +2 -2
- package/dist/components/field.d.ts +44 -14
- package/dist/components/field.d.ts.map +1 -1
- package/dist/components/field.js +49 -20
- package/dist/components/file-diff.d.ts.map +1 -1
- package/dist/components/file-diff.js +3 -4
- package/dist/components/form-dialog.d.ts +7 -7
- package/dist/components/form-dialog.d.ts.map +1 -1
- package/dist/components/form-dialog.js +13 -11
- package/dist/components/form-drawer.d.ts +10 -7
- package/dist/components/form-drawer.d.ts.map +1 -1
- package/dist/components/form-drawer.js +11 -10
- package/dist/components/gauge.d.ts +64 -0
- package/dist/components/gauge.d.ts.map +1 -0
- package/dist/components/gauge.js +73 -0
- package/dist/components/hover-card.d.ts +72 -14
- package/dist/components/hover-card.d.ts.map +1 -1
- package/dist/components/hover-card.js +51 -17
- package/dist/components/identity-mark.d.ts +8 -1
- package/dist/components/identity-mark.d.ts.map +1 -1
- package/dist/components/identity-mark.js +31 -5
- package/dist/components/input-group.d.ts +44 -21
- package/dist/components/input-group.d.ts.map +1 -1
- package/dist/components/input-group.js +74 -27
- package/dist/components/input.d.ts +64 -12
- package/dist/components/input.d.ts.map +1 -1
- package/dist/components/input.js +51 -15
- package/dist/components/item.d.ts +37 -15
- package/dist/components/item.d.ts.map +1 -1
- package/dist/components/item.js +41 -22
- package/dist/components/kbd.d.ts +49 -13
- package/dist/components/kbd.d.ts.map +1 -1
- package/dist/components/kbd.js +115 -16
- package/dist/components/kpi-card.d.ts +12 -4
- package/dist/components/kpi-card.d.ts.map +1 -1
- package/dist/components/kpi-card.js +13 -4
- package/dist/components/label.d.ts +17 -10
- package/dist/components/label.d.ts.map +1 -1
- package/dist/components/label.js +18 -11
- package/dist/components/library-shelf.d.ts +6 -6
- package/dist/components/library-shelf.d.ts.map +1 -1
- package/dist/components/library-shelf.js +5 -5
- package/dist/components/load-more-button.d.ts +35 -0
- package/dist/components/load-more-button.d.ts.map +1 -0
- package/dist/components/load-more-button.js +26 -0
- package/dist/components/menu-choice-row.d.ts +6 -8
- package/dist/components/menu-choice-row.d.ts.map +1 -1
- package/dist/components/menu-choice-row.js +8 -8
- package/dist/components/message-notice.d.ts +8 -13
- package/dist/components/message-notice.d.ts.map +1 -1
- package/dist/components/message-notice.js +25 -22
- package/dist/components/middle-truncate.d.ts +33 -0
- package/dist/components/middle-truncate.d.ts.map +1 -0
- package/dist/components/middle-truncate.js +72 -0
- package/dist/components/multi-select.d.ts +94 -0
- package/dist/components/multi-select.d.ts.map +1 -0
- package/dist/components/multi-select.js +132 -0
- package/dist/components/native-select.d.ts +37 -13
- package/dist/components/native-select.d.ts.map +1 -1
- package/dist/components/native-select.js +27 -11
- package/dist/components/notice-banner.d.ts +17 -10
- package/dist/components/notice-banner.d.ts.map +1 -1
- package/dist/components/notice-banner.js +12 -11
- package/dist/components/numbered-steps.d.ts +10 -4
- package/dist/components/numbered-steps.d.ts.map +1 -1
- package/dist/components/numbered-steps.js +9 -7
- package/dist/components/object-page.d.ts +40 -14
- package/dist/components/object-page.d.ts.map +1 -1
- package/dist/components/object-page.js +12 -10
- package/dist/components/object-workspace.d.ts +29 -10
- package/dist/components/object-workspace.d.ts.map +1 -1
- package/dist/components/object-workspace.js +18 -8
- package/dist/components/overview-page.d.ts +2 -2
- package/dist/components/overview-page.d.ts.map +1 -1
- package/dist/components/overview-page.js +8 -7
- package/dist/components/page-actions.d.ts +28 -8
- package/dist/components/page-actions.d.ts.map +1 -1
- package/dist/components/page-actions.js +22 -25
- package/dist/components/page-header.d.ts +11 -4
- package/dist/components/page-header.d.ts.map +1 -1
- package/dist/components/page-header.js +10 -5
- package/dist/components/pending-label.d.ts +6 -5
- package/dist/components/pending-label.d.ts.map +1 -1
- package/dist/components/pending-label.js +14 -6
- package/dist/components/picture-label.d.ts +2 -2
- package/dist/components/picture-label.d.ts.map +1 -1
- package/dist/components/picture-label.js +3 -3
- package/dist/components/portal-sidebar-frame.d.ts +9 -21
- package/dist/components/portal-sidebar-frame.d.ts.map +1 -1
- package/dist/components/portal-sidebar-frame.js +13 -27
- package/dist/components/progress-checklist.d.ts +46 -0
- package/dist/components/progress-checklist.d.ts.map +1 -0
- package/dist/components/progress-checklist.js +46 -0
- package/dist/components/progress.d.ts +48 -8
- package/dist/components/progress.d.ts.map +1 -1
- package/dist/components/progress.js +92 -16
- package/dist/components/prose.d.ts +19 -0
- package/dist/components/prose.d.ts.map +1 -0
- package/dist/components/prose.js +29 -0
- package/dist/components/radio-group.d.ts +51 -12
- package/dist/components/radio-group.d.ts.map +1 -1
- package/dist/components/radio-group.js +42 -12
- package/dist/components/reading-column.js +1 -1
- package/dist/components/record-identity.d.ts +6 -5
- package/dist/components/record-identity.d.ts.map +1 -1
- package/dist/components/record-identity.js +12 -3
- package/dist/components/record-list.d.ts +117 -27
- package/dist/components/record-list.d.ts.map +1 -1
- package/dist/components/record-list.js +185 -55
- package/dist/components/record-time.d.ts +20 -7
- package/dist/components/record-time.d.ts.map +1 -1
- package/dist/components/record-time.js +38 -19
- package/dist/components/resizable.d.ts +7 -5
- package/dist/components/resizable.d.ts.map +1 -1
- package/dist/components/resizable.js +6 -6
- package/dist/components/scope-switcher.d.ts +2 -0
- package/dist/components/scope-switcher.d.ts.map +1 -1
- package/dist/components/scope-switcher.js +2 -2
- package/dist/components/scroll-area.d.ts +47 -11
- package/dist/components/scroll-area.d.ts.map +1 -1
- package/dist/components/scroll-area.js +72 -14
- package/dist/components/segmented-control.d.ts +82 -0
- package/dist/components/segmented-control.d.ts.map +1 -0
- package/dist/components/segmented-control.js +90 -0
- package/dist/components/select.d.ts +55 -15
- package/dist/components/select.d.ts.map +1 -1
- package/dist/components/select.js +62 -19
- package/dist/components/separator.d.ts +12 -5
- package/dist/components/separator.d.ts.map +1 -1
- package/dist/components/separator.js +11 -6
- package/dist/components/settings-facts.d.ts +34 -14
- package/dist/components/settings-facts.d.ts.map +1 -1
- package/dist/components/settings-facts.js +21 -12
- package/dist/components/settings-page.d.ts +4 -4
- package/dist/components/settings-page.js +5 -5
- package/dist/components/settings-section.d.ts +160 -34
- package/dist/components/settings-section.d.ts.map +1 -1
- package/dist/components/settings-section.js +92 -39
- package/dist/components/sheet.d.ts +47 -16
- package/dist/components/sheet.d.ts.map +1 -1
- package/dist/components/sheet.js +40 -19
- package/dist/components/sidebar-sections.d.ts +16 -12
- package/dist/components/sidebar-sections.d.ts.map +1 -1
- package/dist/components/sidebar-sections.js +30 -15
- package/dist/components/sidebar.d.ts +117 -21
- package/dist/components/sidebar.d.ts.map +1 -1
- package/dist/components/sidebar.js +180 -55
- package/dist/components/skeleton.d.ts +54 -8
- package/dist/components/skeleton.d.ts.map +1 -1
- package/dist/components/skeleton.js +44 -9
- package/dist/components/slider.d.ts +53 -11
- package/dist/components/slider.d.ts.map +1 -1
- package/dist/components/slider.js +71 -17
- package/dist/components/snippet.d.ts +57 -0
- package/dist/components/snippet.d.ts.map +1 -0
- package/dist/components/snippet.js +60 -0
- package/dist/components/sonner.d.ts +13 -14
- package/dist/components/sonner.d.ts.map +1 -1
- package/dist/components/sonner.js +25 -22
- package/dist/components/status-badge.d.ts +37 -11
- package/dist/components/status-badge.d.ts.map +1 -1
- package/dist/components/status-badge.js +63 -15
- package/dist/components/status-dot.d.ts +42 -11
- package/dist/components/status-dot.d.ts.map +1 -1
- package/dist/components/status-dot.js +40 -11
- package/dist/components/switch.d.ts +43 -12
- package/dist/components/switch.d.ts.map +1 -1
- package/dist/components/switch.js +42 -12
- package/dist/components/table-card.d.ts +21 -12
- package/dist/components/table-card.d.ts.map +1 -1
- package/dist/components/table-card.js +17 -9
- package/dist/components/table-layouts.d.ts +40 -8
- package/dist/components/table-layouts.d.ts.map +1 -1
- package/dist/components/table-layouts.js +52 -8
- package/dist/components/table-value.d.ts +4 -3
- package/dist/components/table-value.d.ts.map +1 -1
- package/dist/components/table-value.js +6 -5
- package/dist/components/table.d.ts +62 -25
- package/dist/components/table.d.ts.map +1 -1
- package/dist/components/table.js +62 -26
- package/dist/components/tabs.d.ts +86 -17
- package/dist/components/tabs.d.ts.map +1 -1
- package/dist/components/tabs.js +93 -22
- package/dist/components/text-link.d.ts +4 -1
- package/dist/components/text-link.d.ts.map +1 -1
- package/dist/components/text-link.js +4 -1
- package/dist/components/text-with-copy-button.d.ts +30 -0
- package/dist/components/text-with-copy-button.d.ts.map +1 -0
- package/dist/components/text-with-copy-button.js +32 -0
- package/dist/components/textarea.d.ts +40 -10
- package/dist/components/textarea.d.ts.map +1 -1
- package/dist/components/textarea.js +50 -11
- package/dist/components/theme-mode-switcher.d.ts +20 -3
- package/dist/components/theme-mode-switcher.d.ts.map +1 -1
- package/dist/components/theme-mode-switcher.js +23 -6
- package/dist/components/toggle-group.d.ts +28 -8
- package/dist/components/toggle-group.d.ts.map +1 -1
- package/dist/components/toggle-group.js +12 -6
- package/dist/components/toggle.d.ts +6 -3
- package/dist/components/toggle.d.ts.map +1 -1
- package/dist/components/toggle.js +10 -7
- package/dist/components/tooltip.d.ts +43 -12
- package/dist/components/tooltip.d.ts.map +1 -1
- package/dist/components/tooltip.js +45 -16
- package/dist/components/trend-badge.js +1 -1
- package/dist/components/update-card.js +1 -1
- package/dist/eslint/index.js +82 -0
- package/dist/eslint/rules/badge-is-static-label.js +93 -0
- package/dist/eslint/rules/button-icon-slot.js +102 -0
- package/dist/eslint/rules/button-link-for-navigation.js +146 -0
- package/dist/eslint/rules/button-no-aria-label-with-text.js +75 -0
- package/dist/eslint/rules/button-type-roles-only-in-buttons.js +83 -0
- package/dist/eslint/rules/choice-control-has-a-name.js +98 -0
- package/dist/eslint/rules/choice-group-in-fieldset.js +86 -0
- package/dist/eslint/rules/command-shortcut-single-owner.js +118 -0
- package/dist/eslint/rules/confirm-dialog-words.js +63 -0
- package/dist/eslint/rules/data-table-one-unsized-column.js +96 -0
- package/dist/eslint/rules/destructive-action-placement.js +69 -0
- package/dist/eslint/rules/disabled-choice-says-why.js +71 -0
- package/dist/eslint/rules/ellipsis-opens-a-dialog.js +110 -0
- package/dist/eslint/rules/input-group-owners.js +155 -0
- package/dist/eslint/rules/kbd-children-no-modifier-text.js +128 -0
- package/dist/eslint/rules/literal-text.js +41 -0
- package/dist/eslint/rules/middle-truncate-placement.js +69 -0
- package/dist/eslint/rules/no-bare-button-label.js +55 -0
- package/dist/eslint/rules/no-control-in-read-only-surface.js +106 -0
- package/dist/eslint/rules/no-hand-built-busy.js +104 -0
- package/dist/eslint/rules/no-hand-built-theme-picker.js +144 -0
- package/dist/eslint/rules/no-hand-built-tile.js +41 -0
- package/dist/eslint/rules/no-hand-formatted-date.js +87 -0
- package/dist/eslint/rules/no-hand-stacked-avatars.js +67 -0
- package/dist/eslint/rules/no-nested-collapse.js +60 -0
- package/dist/eslint/rules/no-search-in-form.js +47 -0
- package/dist/eslint/rules/no-shadcn-appearance-override.js +2 -16
- package/dist/eslint/rules/no-shell-prompt-in-snippet.js +74 -0
- package/dist/eslint/rules/no-stock-error-words.js +56 -0
- package/dist/eslint/rules/no-tooltip-around-labelled-field.js +72 -0
- package/dist/eslint/rules/no-weight-class-on-strong-in-type-role.js +57 -0
- package/dist/eslint/rules/one-submenu-level.js +43 -0
- package/dist/eslint/rules/one-toast-per-flow.js +91 -0
- package/dist/eslint/rules/option-count.js +167 -0
- package/dist/eslint/rules/page-command-link-not-render.js +43 -0
- package/dist/eslint/rules/progress-needs-name.js +57 -0
- package/dist/eslint/rules/settings-limit-in-footer.js +94 -0
- package/dist/eslint/rules/skeleton-in-region.js +80 -0
- package/dist/eslint/rules/sortable-heading-uses-sort.js +79 -0
- package/dist/eslint/rules/table-cell-inset-owner.js +107 -0
- package/dist/eslint/rules/tabs-selection-is-controlled.js +47 -0
- package/dist/eslint/rules/tooltip-only-arrow.js +38 -0
- package/dist/eslint/rules/tooltip-trigger-accessible-name.js +114 -0
- package/dist/eslint/strict-design-system.js +21 -7
- package/dist/index.d.ts +13 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -13
- package/dist/internal/assistant-ui/attachment.d.ts.map +1 -1
- package/dist/internal/assistant-ui/attachment.js +2 -2
- package/dist/internal/assistant-ui/reasoning.d.ts +1 -1
- package/dist/internal/assistant-ui/reasoning.d.ts.map +1 -1
- package/dist/internal/assistant-ui/tool-fallback.js +1 -1
- package/dist/internal/assistant-ui/tool-group.d.ts +1 -1
- package/dist/internal/assistant-ui/tool-group.d.ts.map +1 -1
- package/dist/internal/assistant-ui/tooltip-icon-button.d.ts +2 -3
- package/dist/internal/assistant-ui/tooltip-icon-button.d.ts.map +1 -1
- package/dist/internal/assistant-ui/tooltip-icon-button.js +1 -1
- package/dist/internal/catalog-list.js +2 -2
- package/dist/internal/collection-note.d.ts +23 -0
- package/dist/internal/collection-note.d.ts.map +1 -0
- package/dist/internal/collection-note.js +17 -0
- package/dist/internal/collection-rail.d.ts +57 -0
- package/dist/internal/collection-rail.d.ts.map +1 -0
- package/dist/internal/collection-rail.js +33 -0
- package/dist/internal/company-logo.d.ts +18 -0
- package/dist/internal/company-logo.d.ts.map +1 -0
- package/dist/internal/company-logo.js +14 -0
- package/dist/internal/control-name.d.ts +12 -0
- package/dist/internal/control-name.d.ts.map +1 -0
- package/dist/internal/control-name.js +1 -0
- package/dist/internal/diff-line.d.ts +9 -0
- package/dist/internal/diff-line.d.ts.map +1 -0
- package/dist/internal/diff-line.js +8 -0
- package/dist/internal/empty.d.ts +8 -4
- package/dist/internal/empty.d.ts.map +1 -1
- package/dist/internal/empty.js +14 -8
- package/dist/internal/field-frame.d.ts +33 -0
- package/dist/internal/field-frame.d.ts.map +1 -0
- package/dist/internal/field-frame.js +20 -0
- package/dist/internal/final-focus.d.ts +8 -0
- package/dist/internal/final-focus.d.ts.map +1 -0
- package/dist/internal/final-focus.js +10 -0
- package/dist/internal/footer-strip.d.ts.map +1 -1
- package/dist/internal/footer-strip.js +3 -1
- package/dist/internal/form-submit.d.ts +9 -5
- package/dist/internal/form-submit.d.ts.map +1 -1
- package/dist/internal/form-submit.js +31 -2
- package/dist/internal/input-box.d.ts +26 -0
- package/dist/internal/input-box.d.ts.map +1 -0
- package/dist/internal/input-box.js +59 -0
- package/dist/internal/menu-surface.d.ts +10 -3
- package/dist/internal/menu-surface.d.ts.map +1 -1
- package/dist/internal/menu-surface.js +10 -3
- package/dist/internal/notice.d.ts +25 -0
- package/dist/internal/notice.d.ts.map +1 -0
- package/dist/internal/notice.js +33 -0
- package/dist/internal/overlay-stem.d.ts +5 -0
- package/dist/internal/overlay-stem.d.ts.map +1 -0
- package/dist/internal/overlay-stem.js +7 -0
- package/dist/internal/phone-sheet.d.ts +4 -0
- package/dist/internal/phone-sheet.d.ts.map +1 -0
- package/dist/internal/phone-sheet.js +2 -0
- package/dist/internal/present-content.d.ts +3 -0
- package/dist/internal/present-content.d.ts.map +1 -0
- package/dist/internal/present-content.js +1 -0
- package/dist/internal/record-menu-items.d.ts +4 -3
- package/dist/internal/record-menu-items.d.ts.map +1 -1
- package/dist/internal/record-menu-items.js +21 -6
- package/dist/internal/settings-save.d.ts +9 -2
- package/dist/internal/settings-save.d.ts.map +1 -1
- package/dist/internal/settings-save.js +9 -6
- package/dist/internal/table-compact-line.d.ts +38 -0
- package/dist/internal/table-compact-line.d.ts.map +1 -0
- package/dist/internal/table-compact-line.js +35 -0
- package/dist/internal/title-when-cut.d.ts +8 -0
- package/dist/internal/title-when-cut.d.ts.map +1 -0
- package/dist/internal/title-when-cut.js +22 -0
- package/dist/internal/unsaved-guard.d.ts +18 -0
- package/dist/internal/unsaved-guard.d.ts.map +1 -0
- package/dist/internal/unsaved-guard.js +31 -0
- package/dist/lib/merge-tokens.generated.js +2 -2
- package/dist/showcase/foundations.d.ts.map +1 -1
- package/dist/showcase/foundations.js +10 -2
- package/dist/showcase/specimens/actions.d.ts +2 -0
- package/dist/showcase/specimens/actions.d.ts.map +1 -1
- package/dist/showcase/specimens/actions.js +30 -5
- package/dist/showcase/specimens/charts.d.ts.map +1 -1
- package/dist/showcase/specimens/charts.js +22 -13
- package/dist/showcase/specimens/choices.d.ts +14 -3
- package/dist/showcase/specimens/choices.d.ts.map +1 -1
- package/dist/showcase/specimens/choices.js +93 -13
- package/dist/showcase/specimens/compositions.d.ts.map +1 -1
- package/dist/showcase/specimens/compositions.js +136 -23
- package/dist/showcase/specimens/data.d.ts +12 -7
- package/dist/showcase/specimens/data.d.ts.map +1 -1
- package/dist/showcase/specimens/data.js +139 -35
- package/dist/showcase/specimens/feedback.d.ts +2 -0
- package/dist/showcase/specimens/feedback.d.ts.map +1 -1
- package/dist/showcase/specimens/feedback.js +39 -15
- package/dist/showcase/specimens/index.d.ts +20 -12
- package/dist/showcase/specimens/index.d.ts.map +1 -1
- package/dist/showcase/specimens/inputs.d.ts +4 -2
- package/dist/showcase/specimens/inputs.d.ts.map +1 -1
- package/dist/showcase/specimens/inputs.js +62 -19
- package/dist/showcase/specimens/menus.d.ts +0 -1
- package/dist/showcase/specimens/menus.d.ts.map +1 -1
- package/dist/showcase/specimens/menus.js +47 -18
- package/dist/showcase/specimens/navigation.d.ts +6 -4
- package/dist/showcase/specimens/navigation.d.ts.map +1 -1
- package/dist/showcase/specimens/navigation.js +61 -32
- package/dist/showcase/specimens/other.d.ts +1 -3
- package/dist/showcase/specimens/other.d.ts.map +1 -1
- package/dist/showcase/specimens/other.js +17 -13
- package/dist/showcase/specimens/status.d.ts +4 -1
- package/dist/showcase/specimens/status.d.ts.map +1 -1
- package/dist/showcase/specimens/status.js +64 -11
- package/dist/showcase/usage.generated.d.ts +1 -1
- package/dist/showcase/usage.generated.d.ts.map +1 -1
- package/dist/showcase/usage.generated.js +637 -549
- package/dist/standalone/index.js +1 -1
- package/dist/styles/components.css +13 -13
- package/dist/styles/globals.css +25 -0
- package/package.json +2 -3
- package/dist/components/approval-row.d.ts +0 -34
- package/dist/components/approval-row.d.ts.map +0 -1
- package/dist/components/approval-row.js +0 -27
- package/dist/components/channel-view.d.ts +0 -112
- package/dist/components/channel-view.d.ts.map +0 -1
- package/dist/components/channel-view.js +0 -84
- package/dist/components/dashboard-shell.d.ts +0 -51
- package/dist/components/dashboard-shell.d.ts.map +0 -1
- package/dist/components/dashboard-shell.js +0 -89
- package/dist/components/direction.d.ts +0 -2
- package/dist/components/direction.d.ts.map +0 -1
- package/dist/components/direction.js +0 -1
- package/dist/components/expandable-block.d.ts +0 -20
- package/dist/components/expandable-block.d.ts.map +0 -1
- package/dist/components/expandable-block.js +0 -32
- package/dist/components/form.d.ts +0 -42
- package/dist/components/form.d.ts.map +0 -1
- package/dist/components/form.js +0 -91
- package/dist/components/label-system.d.ts +0 -17
- package/dist/components/label-system.d.ts.map +0 -1
- package/dist/components/label-system.js +0 -21
- package/dist/components/memory-item.d.ts +0 -21
- package/dist/components/memory-item.d.ts.map +0 -1
- package/dist/components/memory-item.js +0 -18
- package/dist/components/menubar.d.ts +0 -43
- package/dist/components/menubar.d.ts.map +0 -1
- package/dist/components/menubar.js +0 -69
- package/dist/components/navigation-menu.d.ts +0 -26
- package/dist/components/navigation-menu.d.ts.map +0 -1
- package/dist/components/navigation-menu.js +0 -45
- package/dist/components/pagination.d.ts +0 -33
- package/dist/components/pagination.d.ts.map +0 -1
- package/dist/components/pagination.js +0 -41
- package/dist/components/plugin-slot-boundary.d.ts +0 -42
- package/dist/components/plugin-slot-boundary.d.ts.map +0 -1
- package/dist/components/plugin-slot-boundary.js +0 -41
- package/dist/components/status-row.d.ts +0 -32
- package/dist/components/status-row.d.ts.map +0 -1
- package/dist/components/status-row.js +0 -35
|
@@ -1,35 +1,38 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx,
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { OctagonAlert } from "lucide-react";
|
|
4
|
+
import { Alert } from "./alert.js";
|
|
5
|
+
import { NOTICE_EDGE, NoticeLink, NoticeMark, } from "../internal/notice.js";
|
|
3
6
|
import { cn } from "../lib/utils.js";
|
|
4
|
-
import { NoticeBanner } from "./notice-banner.js";
|
|
5
7
|
import { PageCommandButton } from "./page-actions.js";
|
|
6
8
|
import { bodyBeyondTitle } from "./product-message.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
9
|
+
const TONE_TEXT = {
|
|
10
|
+
neutral: "text-muted-foreground",
|
|
11
|
+
info: "text-info-soft-foreground",
|
|
12
|
+
warning: "text-warning-soft-foreground",
|
|
13
|
+
destructive: "text-destructive-soft-foreground",
|
|
14
|
+
};
|
|
15
|
+
const LEAD = { action: "font-medium", section: "font-semibold" };
|
|
16
|
+
const closedSentence = (title) => (/[.!?:…]$/u.test(title) ? title : `${title}.`);
|
|
13
17
|
/**
|
|
14
18
|
* A catalogue message about one thing on the screen.
|
|
15
19
|
*
|
|
16
20
|
* @family Feedback
|
|
17
|
-
* @when
|
|
18
|
-
*
|
|
19
|
-
* @
|
|
20
|
-
*
|
|
21
|
-
* @
|
|
22
|
-
* Long words wrap, nothing truncates. A successful retry removes it. A `section` message with no
|
|
23
|
-
* action is drawn as the passage under a control, because a banner always carries its fix.
|
|
24
|
-
* @words From the catalogue only. Title says what happened to which object; body only when it adds
|
|
25
|
-
* something. Action labelled with its result: *Try again*, *Sign in*.
|
|
26
|
-
* @access `action`: `role="alert"` (`status` for info). `section`: `role="status"`,
|
|
27
|
-
* `aria-live="polite"`, atomic. Focus stays where the person was.
|
|
21
|
+
* @when "Display text that requires attention or provides additional information." (https://vercel.com/geist/note) "Use a Note for inline contextual feedback next to the field, card, or section it describes: a region change warning above a region picker, a rate-limit notice next to a usage gauge." (https://vercel.com/geist/note) "Choose `variant` by meaning: `error` for a problem the user must fix, `warning` for a consequence to acknowledge, `success` for a passed check, `secondary` for neutral information." (https://vercel.com/geist/note) Ours: a message beside the field, card or section it describes, under the control whose write failed (`action`), or atop the section whose condition lasts (`section`). The `tone` is the `variant`: `destructive` is `error`, `warning` is `warning`, `info` is `success` (a passed check or a state the product manages for the reader, drawn in the accent blue) and `neutral` is `secondary`; there is no violet or cyan.
|
|
22
|
+
* @instead "Pick `Banner` when the message is page-level or system-wide and needs a CTA, `Toast` for transient acknowledgments, `Modal` for destructive confirmations." (https://vercel.com/geist/note) Ours: `Banner` is `NoticeBanner`, `Toast` is `showMessageToast` and `Modal` is `ConfirmDialog`. For a field's wrong value use `FieldError`. For a failed read use `EmptyState failed`.
|
|
23
|
+
* @behaviour "A Note is persistent until the underlying state changes; don’t add an ad hoc dismiss control because it competes with the message." (https://vercel.com/geist/note) "One Note per concept. Stacking three Notes on a card means the page architecture, not the Note copy, is wrong." (https://vercel.com/geist/note) "The optional `NoteAction` holds a single inline CTA. Don’t pair it with a second button." (https://vercel.com/geist/note) Ours: tone `destructive` by default, `warning`, `info` or `neutral`. Colour sits on the line, the text and the mark; the ground stays clear, and the action stays neutral. `section` is the framed note, a line 36 px high with its mark and sentence on one row and the action at its trailing edge (under 768 px the action takes its own row, full width), with or without an action; `action` is the unframed passage, its mark and sentence on one row with the action link inside the sentence, after a space. The title and the body run on as one passage, the title closed by a full stop when it has none of its own, so the two never read as one sentence; the title leads in bold (semibold in a note, medium in a passage) only when a body follows it, and stands at regular weight alone. A message worded for a notice has one action, a small default button in a section and an underlined 14 px link in the passage, and at most a second one after it (Nadia's *Ask Nadia* after *Try again*), an outline button in a section and a second link in the passage; a third is not shown. Long words wrap, nothing truncates. A successful retry removes it. Required: the catalogue `message`, so the sentence and its mark are always drawn.
|
|
24
|
+
* @words "`NoteLabel` is a 1–2 word Title Case prefix that names the topic: `Region Change`, `Rate Limit`, `Plan Limit`. Cut hedges like `Heads Up`, `FYI`, and `Note`." (https://vercel.com/geist/note) "`NoteContent` is one sentence in active voice that names the impact: `Changing this region restarts all functions.`" (https://vercel.com/geist/note) "No `variant="info"` exists; omit `variant` for the default info icon or use `variant="secondary"` for neutral copy." (https://vercel.com/geist/note) "Single-fragment labels take no period; full descriptive sentences in the body do." (https://vercel.com/geist/note) Ours: words come from the catalogue only. The message's title stands in for `NoteLabel` and `NoteContent`, in sentence case, and `info` exists as Geist's blue `success`. The action is labelled with its result: *Try again*, *Sign in*. When the message carries an action, its body does not repeat it: the link is the step, so the body ends before "Try again".
|
|
25
|
+
* @access `action`: `role="alert"` (`status` for info and neutral). `section`: `role="status"`, `aria-live="polite"`, atomic. The mark is hidden. Focus stays where the person was.
|
|
28
26
|
*/
|
|
29
27
|
export function MessageNotice({ message, placement, tone = "destructive", className, }) {
|
|
30
28
|
const body = bodyBeyondTitle(message);
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
const [action, secondAction] = message.actions;
|
|
30
|
+
const mark = _jsx(NoticeMark, { icon: tone === "destructive" ? OctagonAlert : undefined, tone: tone });
|
|
31
|
+
const sentence = (_jsxs("p", { className: "m-0 min-w-0 break-words", children: [_jsx("span", { className: body ? LEAD[placement] : undefined, children: body ? closedSentence(message.title) : message.title }), body ? ` ${body}` : null, placement === "action"
|
|
32
|
+
? [action, secondAction].map((command) => command ? (_jsxs("span", { children: [" ", _jsx(NoticeLink, { command: command, place: "inline" })] }, command.label)) : null)
|
|
33
|
+
: null] }));
|
|
34
|
+
if (placement === "section") {
|
|
35
|
+
return (_jsxs(Alert, { "aria-atomic": "true", "aria-live": "polite", className: cn("flex min-h-9 flex-wrap items-center gap-3 bg-transparent px-3 py-1.5 text-body-sm", action && "rounded-md max-md:py-3 md:pr-2", NOTICE_EDGE[tone], TONE_TEXT[tone], className), "data-message-code": message.code, "data-message-notice": "section", "data-tone": tone, role: "status", variant: "default", children: [_jsxs("div", { className: "flex min-w-0 flex-1 items-start gap-3", children: [_jsx("span", { className: "flex h-5 shrink-0 items-center [&_svg]:size-3.5", children: mark }), sentence] }), action ? (_jsxs("div", { className: "flex shrink-0 gap-2 self-center max-md:basis-full max-md:*:flex-1", "data-notice-action": true, children: [_jsx(PageCommandButton, { command: action, size: "sm", variant: "default" }), secondAction ? (_jsx(PageCommandButton, { command: secondAction, size: "sm", variant: "outline" })) : null] })) : null] }));
|
|
33
36
|
}
|
|
34
|
-
return (_jsxs("div", { className: cn("flex min-w-0
|
|
37
|
+
return (_jsxs("div", { className: cn("flex min-w-0 items-start gap-2 text-body-sm", TONE_TEXT[tone], className), "data-message-code": message.code, "data-message-notice": "action", role: tone === "info" || tone === "neutral" ? "status" : "alert", children: [_jsx("span", { className: "flex h-5 shrink-0 items-center [&_svg]:size-4", children: mark }), sentence] }));
|
|
35
38
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* A technical string cut in the middle, so its start and its end both stay readable.
|
|
4
|
+
*
|
|
5
|
+
* @family Data
|
|
6
|
+
* @when “Use Middle Truncate for strings whose head and tail both carry information: file paths (`apps/…/page.tsx`), URLs, deployment IDs (`dpl_…abc123`), commit hashes, branch names with prefixes.” (https://vercel.com/geist/middle-truncate)
|
|
7
|
+
* Ours: an environment variable is one of these too.
|
|
8
|
+
* @instead “For prose, descriptions, and headings, use end-truncation with `…` instead; cutting the middle of a sentence destroys meaning.” (https://vercel.com/geist/middle-truncate)
|
|
9
|
+
* “For any truncated value the user might need verbatim, pair with a `Tooltip` showing the full string or a copy-on-click affordance.” (https://vercel.com/geist/middle-truncate)
|
|
10
|
+
* Ours: a value someone may need exactly is `TextWithCopyButton` or sits beside a `CopyButton`; while
|
|
11
|
+
* the middle is cut, the whole value is also the element's `title`.
|
|
12
|
+
* @behaviour “Middle Truncate renders a single ellipsis glyph (`…`) rather than three periods. This keeps monospace values, including environment variable keys, IDs, hashes, and paths, from reserving three character cells for the truncation marker.” (https://vercel.com/geist/middle-truncate)
|
|
13
|
+
* “The component listens to its container width, so layouts that change width on hover (expanding cards, animated rows) cause the truncation point to jitter. Lock the width during interaction.” (https://vercel.com/geist/middle-truncate)
|
|
14
|
+
* “Copying truncated text yields the full original string from the underlying value, not the visible ellipsis form. Confirm this stays true if wrapping with custom `onCopy`.” (https://vercel.com/geist/middle-truncate)
|
|
15
|
+
* “Don’t wrap Middle Truncate in another `text-overflow: ellipsis` container; the two strategies fight each other and the inner ellipsis wins inconsistently.” (https://vercel.com/geist/middle-truncate)
|
|
16
|
+
* “Keep the visible string long enough on small viewports that the head still identifies the resource (path segment, ID prefix).” (https://vercel.com/geist/middle-truncate)
|
|
17
|
+
* Ours: it fills the width its parent gives and shows the whole value when it fits. Too narrow, the
|
|
18
|
+
* last third of the value, at most 16 characters, stays whole. While the middle is cut, copying a
|
|
19
|
+
* selection made inside it puts the whole value on the clipboard; a value shown in full copies only
|
|
20
|
+
* what is selected. A custom `onCopy` runs first, and `preventDefault` in it keeps the value as
|
|
21
|
+
* selected.
|
|
22
|
+
* @words Ours: the value as the system writes it, unchanged; the ellipsis comes from the layout, never from
|
|
23
|
+
* the text.
|
|
24
|
+
* @access “Expose the full string to assistive tech via the wrapping element’s accessible name (the component already keeps the full value in the DOM for copy).” (https://vercel.com/geist/middle-truncate)
|
|
25
|
+
* “Avoid Middle Truncate inside focusable controls without an explicit `aria-label`; the ellipsis on its own gives screen readers nothing to announce.” (https://vercel.com/geist/middle-truncate)
|
|
26
|
+
* Ours: the whole value is the element's text, read in full; inside a link or a button, the control
|
|
27
|
+
* carries the `aria-label` that names the target.
|
|
28
|
+
*/
|
|
29
|
+
declare function MiddleTruncate({ className, onCopy, value, ...props }: Omit<React.ComponentProps<"span">, "children"> & {
|
|
30
|
+
value: string;
|
|
31
|
+
}): React.JSX.Element;
|
|
32
|
+
export { MiddleTruncate };
|
|
33
|
+
//# sourceMappingURL=middle-truncate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middle-truncate.d.ts","sourceRoot":"","sources":["../../src/components/middle-truncate.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAmC/B;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,iBAAS,cAAc,CAAC,EACtB,SAAS,EACT,MAAM,EACN,KAAK,EACL,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,qBA0BpE;AAED,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "../lib/utils.js";
|
|
5
|
+
const TAIL_SHARE = 3;
|
|
6
|
+
const TAIL_MAX = 16;
|
|
7
|
+
function copiesWholeValue(root) {
|
|
8
|
+
const head = root.firstElementChild;
|
|
9
|
+
const selection = window.getSelection();
|
|
10
|
+
return (head !== null &&
|
|
11
|
+
head.scrollWidth > head.clientWidth &&
|
|
12
|
+
selection !== null &&
|
|
13
|
+
!selection.isCollapsed &&
|
|
14
|
+
root.contains(selection.anchorNode) &&
|
|
15
|
+
root.contains(selection.focusNode));
|
|
16
|
+
}
|
|
17
|
+
function useIsCut(root, value) {
|
|
18
|
+
const [cut, setCut] = React.useState(false);
|
|
19
|
+
React.useEffect(() => {
|
|
20
|
+
const element = root.current;
|
|
21
|
+
const head = element?.firstElementChild;
|
|
22
|
+
if (!element || !head || typeof ResizeObserver === "undefined")
|
|
23
|
+
return;
|
|
24
|
+
const measure = () => setCut(head.scrollWidth > head.clientWidth);
|
|
25
|
+
measure();
|
|
26
|
+
const observer = new ResizeObserver(measure);
|
|
27
|
+
observer.observe(element);
|
|
28
|
+
return () => observer.disconnect();
|
|
29
|
+
}, [root, value]);
|
|
30
|
+
return cut;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A technical string cut in the middle, so its start and its end both stay readable.
|
|
34
|
+
*
|
|
35
|
+
* @family Data
|
|
36
|
+
* @when “Use Middle Truncate for strings whose head and tail both carry information: file paths (`apps/…/page.tsx`), URLs, deployment IDs (`dpl_…abc123`), commit hashes, branch names with prefixes.” (https://vercel.com/geist/middle-truncate)
|
|
37
|
+
* Ours: an environment variable is one of these too.
|
|
38
|
+
* @instead “For prose, descriptions, and headings, use end-truncation with `…` instead; cutting the middle of a sentence destroys meaning.” (https://vercel.com/geist/middle-truncate)
|
|
39
|
+
* “For any truncated value the user might need verbatim, pair with a `Tooltip` showing the full string or a copy-on-click affordance.” (https://vercel.com/geist/middle-truncate)
|
|
40
|
+
* Ours: a value someone may need exactly is `TextWithCopyButton` or sits beside a `CopyButton`; while
|
|
41
|
+
* the middle is cut, the whole value is also the element's `title`.
|
|
42
|
+
* @behaviour “Middle Truncate renders a single ellipsis glyph (`…`) rather than three periods. This keeps monospace values, including environment variable keys, IDs, hashes, and paths, from reserving three character cells for the truncation marker.” (https://vercel.com/geist/middle-truncate)
|
|
43
|
+
* “The component listens to its container width, so layouts that change width on hover (expanding cards, animated rows) cause the truncation point to jitter. Lock the width during interaction.” (https://vercel.com/geist/middle-truncate)
|
|
44
|
+
* “Copying truncated text yields the full original string from the underlying value, not the visible ellipsis form. Confirm this stays true if wrapping with custom `onCopy`.” (https://vercel.com/geist/middle-truncate)
|
|
45
|
+
* “Don’t wrap Middle Truncate in another `text-overflow: ellipsis` container; the two strategies fight each other and the inner ellipsis wins inconsistently.” (https://vercel.com/geist/middle-truncate)
|
|
46
|
+
* “Keep the visible string long enough on small viewports that the head still identifies the resource (path segment, ID prefix).” (https://vercel.com/geist/middle-truncate)
|
|
47
|
+
* Ours: it fills the width its parent gives and shows the whole value when it fits. Too narrow, the
|
|
48
|
+
* last third of the value, at most 16 characters, stays whole. While the middle is cut, copying a
|
|
49
|
+
* selection made inside it puts the whole value on the clipboard; a value shown in full copies only
|
|
50
|
+
* what is selected. A custom `onCopy` runs first, and `preventDefault` in it keeps the value as
|
|
51
|
+
* selected.
|
|
52
|
+
* @words Ours: the value as the system writes it, unchanged; the ellipsis comes from the layout, never from
|
|
53
|
+
* the text.
|
|
54
|
+
* @access “Expose the full string to assistive tech via the wrapping element’s accessible name (the component already keeps the full value in the DOM for copy).” (https://vercel.com/geist/middle-truncate)
|
|
55
|
+
* “Avoid Middle Truncate inside focusable controls without an explicit `aria-label`; the ellipsis on its own gives screen readers nothing to announce.” (https://vercel.com/geist/middle-truncate)
|
|
56
|
+
* Ours: the whole value is the element's text, read in full; inside a link or a button, the control
|
|
57
|
+
* carries the `aria-label` that names the target.
|
|
58
|
+
*/
|
|
59
|
+
function MiddleTruncate({ className, onCopy, value, ...props }) {
|
|
60
|
+
const root = React.useRef(null);
|
|
61
|
+
const cut = useIsCut(root, value);
|
|
62
|
+
const characters = Array.from(value);
|
|
63
|
+
const tailStart = characters.length - Math.min(Math.ceil(characters.length / TAIL_SHARE), TAIL_MAX);
|
|
64
|
+
return (_jsxs("span", { ref: root, "data-slot": "middle-truncate", title: cut ? value : undefined, className: cn("inline-flex max-w-full min-w-0 overflow-hidden", className), onCopy: (event) => {
|
|
65
|
+
onCopy?.(event);
|
|
66
|
+
if (event.defaultPrevented || !copiesWholeValue(event.currentTarget))
|
|
67
|
+
return;
|
|
68
|
+
event.clipboardData.setData("text/plain", value);
|
|
69
|
+
event.preventDefault();
|
|
70
|
+
}, ...props, children: [_jsx("span", { className: "min-w-0 overflow-hidden text-ellipsis whitespace-pre", children: characters.slice(0, tailStart).join("") }), _jsx("span", { className: "shrink-0 whitespace-pre", children: characters.slice(tailStart).join("") })] }));
|
|
71
|
+
}
|
|
72
|
+
export { MiddleTruncate };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { type FieldName } from "../internal/field-frame.js";
|
|
3
|
+
import { type FieldSize } from "../internal/input-box.js";
|
|
4
|
+
import { Popover, PopoverContent, PopoverTrigger } from "./popover.js";
|
|
5
|
+
type MultiSelectContextValue = {
|
|
6
|
+
selectedCount: number;
|
|
7
|
+
totalCount: number;
|
|
8
|
+
onSelectAll: () => void;
|
|
9
|
+
selectOnlyLabel: string;
|
|
10
|
+
selectAllLabel: string;
|
|
11
|
+
};
|
|
12
|
+
type MultiSelectProps = React.ComponentProps<typeof Popover> & MultiSelectContextValue;
|
|
13
|
+
/**
|
|
14
|
+
* A menu of checkbox rows that chooses several values from a known list, with the count in its
|
|
15
|
+
* trigger.
|
|
16
|
+
*
|
|
17
|
+
* @family Choices
|
|
18
|
+
* @when "A keyboard-navigable dropdown for selecting multiple items with advanced focus management."
|
|
19
|
+
* (https://vercel.com/geist/multi-select) "Pick `<MultiSelect>` when users pick more than one value
|
|
20
|
+
* from a known list (regions, scopes, tags)." (https://vercel.com/geist/multi-select) Ours: also the facets of a collection.
|
|
21
|
+
* @instead "For a single value from a short list, use `Select`."
|
|
22
|
+
* (https://vercel.com/geist/multi-select) "When typing to filter is more important than seeing
|
|
23
|
+
* every option at once, use `Combobox`." (https://vercel.com/geist/multi-select) "Skip
|
|
24
|
+
* `<MultiSelect>` for boolean settings; `Toggle` handles those."
|
|
25
|
+
* (https://vercel.com/geist/multi-select) Ours: `Combobox` takes several values with `multiple`; a
|
|
26
|
+
* yes-or-no setting is a `Switch`, never a one-row multi-select.
|
|
27
|
+
* @behaviour "Use controlled mode when state lives in the URL or syncs to the server so the trigger
|
|
28
|
+
* label and stored value stay in lockstep." (https://vercel.com/geist/multi-select) "Use controlled
|
|
29
|
+
* state to manage selections programmatically." (https://vercel.com/geist/multi-select) Ours: the
|
|
30
|
+
* caller always owns the selection and passes it back through each row, so the trigger and the
|
|
31
|
+
* stored value (the URL, the server) never disagree; the root takes the same `selectedCount` and
|
|
32
|
+
* `totalCount` the trigger's summary is built from. "Show the selected count in the trigger (`3
|
|
33
|
+
* regions selected`); show the single name when only one is picked."
|
|
34
|
+
* (https://vercel.com/geist/multi-select) The trigger is `Input`'s box at its default size, in
|
|
35
|
+
* regular weight, with the chevron at the end (it turns up while the list is open); it keeps the
|
|
36
|
+
* hover edge while the list is open and takes the page's outline for keyboard focus, as a button
|
|
37
|
+
* does. Everything is neutral: a checked box is ink, never blue, and an error turns only the
|
|
38
|
+
* trigger's ring red. `label` and `error` go on `MultiSelectTrigger`, and a string `label` requires
|
|
39
|
+
* `id`; a trigger with no name does not compile. The list opens 8 px under the trigger at least as
|
|
40
|
+
* wide as it, in an 8 px inset, with rows 28 px tall and no gap, and stops 16 px short of the
|
|
41
|
+
* screen's edge (at most 24rem tall, then it scrolls). A row is the checkbox in a 28 px square that
|
|
42
|
+
* fills on hover, then a button holding the row's name; pressing the name runs the button, pressing
|
|
43
|
+
* the box toggles the row. "**Checkbox focus + Enter/Space**: Toggle individual item"
|
|
44
|
+
* (https://vercel.com/geist/multi-select) "**Button focus + Enter/Space**: Smart selection (Select
|
|
45
|
+
* Only, Select All, or Toggle based on context)" (https://vercel.com/geist/multi-select) "**Hidden
|
|
46
|
+
* action labels**: Appear on hover/focus to show available actions"
|
|
47
|
+
* (https://vercel.com/geist/multi-select) Ours: the button's label reads *Select all* on a chosen
|
|
48
|
+
* row while some rows are unchosen, and *Select only* on every other row; it shows on hover and
|
|
49
|
+
* focus of the row, and always on touch. "Keep checkbox focus and button focus distinct so Up/Down
|
|
50
|
+
* navigates rows and Left/Right toggles between the row’s checkbox and action button."
|
|
51
|
+
* (https://vercel.com/geist/multi-select) "**Up/Down arrows**: Navigate between rows while
|
|
52
|
+
* maintaining checkbox/button focus state" (https://vercel.com/geist/multi-select) "**Left/Right
|
|
53
|
+
* arrows**: Switch between checkbox and button focus within the current row"
|
|
54
|
+
* (https://vercel.com/geist/multi-select) "**Tab**: Focus away from the menu (natural tab
|
|
55
|
+
* behavior)" (https://vercel.com/geist/multi-select) "**Enter/Space**: Execute actions based on
|
|
56
|
+
* current focus (checkbox toggle or button click)" (https://vercel.com/geist/multi-select) "Trap
|
|
57
|
+
* focus inside the menu while it’s open and return focus to the trigger on close."
|
|
58
|
+
* (https://vercel.com/geist/multi-select) Ours: focus moves into the list when it opens; Tab leaves
|
|
59
|
+
* it in order. "For empty filters, render `No {items} match "{query}"` rather than `No results`."
|
|
60
|
+
* (https://vercel.com/geist/multi-select) Ours: there is no filter box, so a list that needs one is
|
|
61
|
+
* a `Combobox` with `multiple`.
|
|
62
|
+
* @words The trigger says how many are chosen in the product's words: the name when exactly one is
|
|
63
|
+
* chosen, *No regions selected* when none. It is never empty. Rows are sentence case names.
|
|
64
|
+
* "Announce bulk actions (`Select All`, `Select Only`) through the visible button label so the
|
|
65
|
+
* action matches what the screen reader speaks." (https://vercel.com/geist/multi-select) Ours: the
|
|
66
|
+
* two button labels come from the caller, in sentence case.
|
|
67
|
+
* @access "Each row checkbox needs an `aria-label` that names the item (`Select us-east-1`); a bare
|
|
68
|
+
* `Select` is unanchored for screen readers." (https://vercel.com/geist/multi-select) Ours: each
|
|
69
|
+
* row's checkbox is named by the row's name; each button is named by its label and the row (*Select
|
|
70
|
+
* only us-east-1*). "The trigger button needs a stable accessible name even when zero items are
|
|
71
|
+
* selected; don’t rely on the placeholder alone." (https://vercel.com/geist/multi-select) Ours: the
|
|
72
|
+
* trigger is a button with `aria-expanded`, named by its visible label tied by `id`, or by
|
|
73
|
+
* `aria-label` or `aria-labelledby`; the summary describes it.
|
|
74
|
+
*/
|
|
75
|
+
declare function MultiSelect({ selectedCount, totalCount, onSelectAll, selectOnlyLabel, selectAllLabel, ...props }: MultiSelectProps): React.JSX.Element;
|
|
76
|
+
declare function MultiSelectTrigger({ className, children, size, label, error, ...props }: Omit<React.ComponentProps<typeof PopoverTrigger>, "children"> & {
|
|
77
|
+
/** The summary of the choice: a count, a name, or that nothing is chosen. It describes the trigger. */
|
|
78
|
+
children: string;
|
|
79
|
+
size?: FieldSize;
|
|
80
|
+
/** The message under the box, in the product's words; it also marks the box invalid. */
|
|
81
|
+
error?: string;
|
|
82
|
+
} & FieldName): React.JSX.Element;
|
|
83
|
+
declare function MultiSelectContent({ className, align, sideOffset, onKeyDown, ...props }: React.ComponentProps<typeof PopoverContent>): React.JSX.Element;
|
|
84
|
+
declare function MultiSelectRow({ name, checked, onCheckedChange, onSelectOnly, disabled, className, }: {
|
|
85
|
+
name: string;
|
|
86
|
+
checked: boolean;
|
|
87
|
+
onCheckedChange: (checked: boolean) => void;
|
|
88
|
+
/** Chooses this row and clears the rest. */
|
|
89
|
+
onSelectOnly: () => void;
|
|
90
|
+
disabled?: boolean;
|
|
91
|
+
className?: string;
|
|
92
|
+
}): React.JSX.Element;
|
|
93
|
+
export { MultiSelect, MultiSelectContent, MultiSelectRow, MultiSelectTrigger };
|
|
94
|
+
//# sourceMappingURL=multi-select.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multi-select.d.ts","sourceRoot":"","sources":["../../src/components/multi-select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,OAAO,EAAc,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAE1E,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG/E,KAAK,uBAAuB,GAAG;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAUF,KAAK,gBAAgB,GAAG,KAAK,CAAC,cAAc,CAAC,OAAO,OAAO,CAAC,GAAG,uBAAuB,CAAC;AAEvF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AACH,iBAAS,WAAW,CAAC,EACnB,aAAa,EACb,UAAU,EACV,WAAW,EACX,eAAe,EACf,cAAc,EACd,GAAG,KAAK,EACT,EAAE,gBAAgB,qBAUlB;AAED,iBAAS,kBAAkB,CAAC,EAC1B,SAAS,EACT,QAAQ,EACR,IAAgB,EAChB,KAAK,EACL,KAAK,EACL,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,cAAc,CAAC,EAAE,UAAU,CAAC,GAAG;IACjE,uGAAuG;IACvG,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,wFAAwF;IACxF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,SAAS,qBAmCZ;AAwBD,iBAAS,kBAAkB,CAAC,EAC1B,SAAS,EACT,KAAe,EACf,UAAc,EACd,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,cAAc,CAAC,qBAiB7C;AAED,iBAAS,cAAc,CAAC,EACtB,IAAI,EACJ,OAAO,EACP,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,SAAS,GACV,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,4CAA4C;IAC5C,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,qBAuDA;AAED,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { ChevronDownIcon, ChevronUpIcon } from "lucide-react";
|
|
5
|
+
import { cn } from "../lib/utils.js";
|
|
6
|
+
import { Button } from "./button.js";
|
|
7
|
+
import { Checkbox } from "./checkbox.js";
|
|
8
|
+
import { FieldFrame } from "../internal/field-frame.js";
|
|
9
|
+
import { fieldTriggerStates } from "../internal/input-box.js";
|
|
10
|
+
import { Label } from "./label.js";
|
|
11
|
+
import { Popover, PopoverContent, PopoverTrigger } from "./popover.js";
|
|
12
|
+
import { selectTriggerVariants } from "./select.js";
|
|
13
|
+
const MultiSelectContext = React.createContext(null);
|
|
14
|
+
function useMultiSelect() {
|
|
15
|
+
const context = React.useContext(MultiSelectContext);
|
|
16
|
+
if (!context)
|
|
17
|
+
throw new Error("MultiSelectRow must be inside <MultiSelect>");
|
|
18
|
+
return context;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A menu of checkbox rows that chooses several values from a known list, with the count in its
|
|
22
|
+
* trigger.
|
|
23
|
+
*
|
|
24
|
+
* @family Choices
|
|
25
|
+
* @when "A keyboard-navigable dropdown for selecting multiple items with advanced focus management."
|
|
26
|
+
* (https://vercel.com/geist/multi-select) "Pick `<MultiSelect>` when users pick more than one value
|
|
27
|
+
* from a known list (regions, scopes, tags)." (https://vercel.com/geist/multi-select) Ours: also the facets of a collection.
|
|
28
|
+
* @instead "For a single value from a short list, use `Select`."
|
|
29
|
+
* (https://vercel.com/geist/multi-select) "When typing to filter is more important than seeing
|
|
30
|
+
* every option at once, use `Combobox`." (https://vercel.com/geist/multi-select) "Skip
|
|
31
|
+
* `<MultiSelect>` for boolean settings; `Toggle` handles those."
|
|
32
|
+
* (https://vercel.com/geist/multi-select) Ours: `Combobox` takes several values with `multiple`; a
|
|
33
|
+
* yes-or-no setting is a `Switch`, never a one-row multi-select.
|
|
34
|
+
* @behaviour "Use controlled mode when state lives in the URL or syncs to the server so the trigger
|
|
35
|
+
* label and stored value stay in lockstep." (https://vercel.com/geist/multi-select) "Use controlled
|
|
36
|
+
* state to manage selections programmatically." (https://vercel.com/geist/multi-select) Ours: the
|
|
37
|
+
* caller always owns the selection and passes it back through each row, so the trigger and the
|
|
38
|
+
* stored value (the URL, the server) never disagree; the root takes the same `selectedCount` and
|
|
39
|
+
* `totalCount` the trigger's summary is built from. "Show the selected count in the trigger (`3
|
|
40
|
+
* regions selected`); show the single name when only one is picked."
|
|
41
|
+
* (https://vercel.com/geist/multi-select) The trigger is `Input`'s box at its default size, in
|
|
42
|
+
* regular weight, with the chevron at the end (it turns up while the list is open); it keeps the
|
|
43
|
+
* hover edge while the list is open and takes the page's outline for keyboard focus, as a button
|
|
44
|
+
* does. Everything is neutral: a checked box is ink, never blue, and an error turns only the
|
|
45
|
+
* trigger's ring red. `label` and `error` go on `MultiSelectTrigger`, and a string `label` requires
|
|
46
|
+
* `id`; a trigger with no name does not compile. The list opens 8 px under the trigger at least as
|
|
47
|
+
* wide as it, in an 8 px inset, with rows 28 px tall and no gap, and stops 16 px short of the
|
|
48
|
+
* screen's edge (at most 24rem tall, then it scrolls). A row is the checkbox in a 28 px square that
|
|
49
|
+
* fills on hover, then a button holding the row's name; pressing the name runs the button, pressing
|
|
50
|
+
* the box toggles the row. "**Checkbox focus + Enter/Space**: Toggle individual item"
|
|
51
|
+
* (https://vercel.com/geist/multi-select) "**Button focus + Enter/Space**: Smart selection (Select
|
|
52
|
+
* Only, Select All, or Toggle based on context)" (https://vercel.com/geist/multi-select) "**Hidden
|
|
53
|
+
* action labels**: Appear on hover/focus to show available actions"
|
|
54
|
+
* (https://vercel.com/geist/multi-select) Ours: the button's label reads *Select all* on a chosen
|
|
55
|
+
* row while some rows are unchosen, and *Select only* on every other row; it shows on hover and
|
|
56
|
+
* focus of the row, and always on touch. "Keep checkbox focus and button focus distinct so Up/Down
|
|
57
|
+
* navigates rows and Left/Right toggles between the row’s checkbox and action button."
|
|
58
|
+
* (https://vercel.com/geist/multi-select) "**Up/Down arrows**: Navigate between rows while
|
|
59
|
+
* maintaining checkbox/button focus state" (https://vercel.com/geist/multi-select) "**Left/Right
|
|
60
|
+
* arrows**: Switch between checkbox and button focus within the current row"
|
|
61
|
+
* (https://vercel.com/geist/multi-select) "**Tab**: Focus away from the menu (natural tab
|
|
62
|
+
* behavior)" (https://vercel.com/geist/multi-select) "**Enter/Space**: Execute actions based on
|
|
63
|
+
* current focus (checkbox toggle or button click)" (https://vercel.com/geist/multi-select) "Trap
|
|
64
|
+
* focus inside the menu while it’s open and return focus to the trigger on close."
|
|
65
|
+
* (https://vercel.com/geist/multi-select) Ours: focus moves into the list when it opens; Tab leaves
|
|
66
|
+
* it in order. "For empty filters, render `No {items} match "{query}"` rather than `No results`."
|
|
67
|
+
* (https://vercel.com/geist/multi-select) Ours: there is no filter box, so a list that needs one is
|
|
68
|
+
* a `Combobox` with `multiple`.
|
|
69
|
+
* @words The trigger says how many are chosen in the product's words: the name when exactly one is
|
|
70
|
+
* chosen, *No regions selected* when none. It is never empty. Rows are sentence case names.
|
|
71
|
+
* "Announce bulk actions (`Select All`, `Select Only`) through the visible button label so the
|
|
72
|
+
* action matches what the screen reader speaks." (https://vercel.com/geist/multi-select) Ours: the
|
|
73
|
+
* two button labels come from the caller, in sentence case.
|
|
74
|
+
* @access "Each row checkbox needs an `aria-label` that names the item (`Select us-east-1`); a bare
|
|
75
|
+
* `Select` is unanchored for screen readers." (https://vercel.com/geist/multi-select) Ours: each
|
|
76
|
+
* row's checkbox is named by the row's name; each button is named by its label and the row (*Select
|
|
77
|
+
* only us-east-1*). "The trigger button needs a stable accessible name even when zero items are
|
|
78
|
+
* selected; don’t rely on the placeholder alone." (https://vercel.com/geist/multi-select) Ours: the
|
|
79
|
+
* trigger is a button with `aria-expanded`, named by its visible label tied by `id`, or by
|
|
80
|
+
* `aria-label` or `aria-labelledby`; the summary describes it.
|
|
81
|
+
*/
|
|
82
|
+
function MultiSelect({ selectedCount, totalCount, onSelectAll, selectOnlyLabel, selectAllLabel, ...props }) {
|
|
83
|
+
const value = React.useMemo(() => ({ selectedCount, totalCount, onSelectAll, selectOnlyLabel, selectAllLabel }), [selectedCount, totalCount, onSelectAll, selectOnlyLabel, selectAllLabel]);
|
|
84
|
+
return (_jsx(MultiSelectContext.Provider, { value: value, children: _jsx(Popover, { ...props }) }));
|
|
85
|
+
}
|
|
86
|
+
function MultiSelectTrigger({ className, children, size = "default", label, error, ...props }) {
|
|
87
|
+
const summaryId = React.useId();
|
|
88
|
+
return (_jsx(FieldFrame, { label: label, error: error, size: size, control: props, children: (aria) => (_jsxs(PopoverTrigger, { "data-slot": "multi-select-trigger", "data-size": size, "data-field-frame": "", className: cn(selectTriggerVariants({ size }), fieldTriggerStates, "group/multi-select-trigger pr-3.25 pl-2.5", size !== "lg" && "leading-5.25", className), ...props, ...aria, "aria-describedby": [aria["aria-describedby"], summaryId].filter(Boolean).join(" "), children: [_jsx("span", { id: summaryId, className: "flex-1 truncate px-1.5 text-left", children: children }), _jsx(ChevronDownIcon, { "aria-hidden": "true", className: "size-3.5 text-muted-foreground group-data-popup-open/multi-select-trigger:hidden" }), _jsx(ChevronUpIcon, { "aria-hidden": "true", className: "hidden size-3.5 text-muted-foreground group-data-popup-open/multi-select-trigger:block" })] })) }));
|
|
89
|
+
}
|
|
90
|
+
const ROW = "[data-slot=multi-select-row]";
|
|
91
|
+
function moveFocus(event) {
|
|
92
|
+
const target = event.target;
|
|
93
|
+
if (!(target instanceof HTMLElement))
|
|
94
|
+
return;
|
|
95
|
+
const column = target.closest("[data-multi-select]")?.dataset.multiSelect;
|
|
96
|
+
const row = target.closest(ROW);
|
|
97
|
+
if (!column || !row)
|
|
98
|
+
return;
|
|
99
|
+
const rows = [...event.currentTarget.querySelectorAll(ROW)];
|
|
100
|
+
const index = rows.indexOf(row);
|
|
101
|
+
const focusIn = (next, name) => {
|
|
102
|
+
const control = next?.querySelector(`[data-multi-select=${name}]`);
|
|
103
|
+
if (!control)
|
|
104
|
+
return;
|
|
105
|
+
event.preventDefault();
|
|
106
|
+
control.focus();
|
|
107
|
+
};
|
|
108
|
+
if (event.key === "ArrowDown")
|
|
109
|
+
focusIn(rows[index + 1], column);
|
|
110
|
+
else if (event.key === "ArrowUp")
|
|
111
|
+
focusIn(rows[index - 1], column);
|
|
112
|
+
else if (event.key === "ArrowRight" && column === "check")
|
|
113
|
+
focusIn(row, "action");
|
|
114
|
+
else if (event.key === "ArrowLeft" && column === "action")
|
|
115
|
+
focusIn(row, "check");
|
|
116
|
+
}
|
|
117
|
+
function MultiSelectContent({ className, align = "start", sideOffset = 8, onKeyDown, ...props }) {
|
|
118
|
+
return (_jsx(PopoverContent, { "data-slot": "multi-select-content", align: align, sideOffset: sideOffset, className: cn("max-h-[min(calc(var(--available-height)---spacing(4)),24rem)] w-auto min-w-(--anchor-width) gap-0 overflow-y-auto p-2", className), onKeyDown: (event) => {
|
|
119
|
+
onKeyDown?.(event);
|
|
120
|
+
if (!event.defaultPrevented)
|
|
121
|
+
moveFocus(event);
|
|
122
|
+
}, ...props }));
|
|
123
|
+
}
|
|
124
|
+
function MultiSelectRow({ name, checked, onCheckedChange, onSelectOnly, disabled, className, }) {
|
|
125
|
+
const { selectedCount, totalCount, onSelectAll, selectOnlyLabel, selectAllLabel } = useMultiSelect();
|
|
126
|
+
const id = React.useId();
|
|
127
|
+
const actionId = `${id}-action`;
|
|
128
|
+
const nameId = `${id}-name`;
|
|
129
|
+
const selectsAll = checked && selectedCount < totalCount;
|
|
130
|
+
return (_jsxs("div", { "data-slot": "multi-select-row", className: cn("group/multi-select-row flex min-h-touch-target items-center text-sm [@media(hover:hover)_and_(pointer:fine)]:min-h-7", className), children: [_jsx(Label, { className: "size-7 shrink-0 justify-center gap-0 rounded-xs-plus hover:bg-accent active:bg-border has-data-disabled:pointer-events-none", htmlFor: id, children: _jsx(Checkbox, { id: id, "aria-labelledby": nameId, checked: checked, "data-multi-select": "check", disabled: disabled, onCheckedChange: onCheckedChange }) }), totalCount > 1 ? (_jsxs(Button, { "aria-labelledby": `${actionId} ${nameId}`, className: "h-7 flex-1 justify-between gap-0 rounded-xs-plus p-1 text-sm font-normal hover:bg-accent focus-visible:bg-accent active:bg-border disabled:text-placeholder disabled:opacity-100", "data-multi-select": "action", disabled: disabled, onClick: selectsAll ? onSelectAll : onSelectOnly, size: "xs", variant: "ghost", children: [_jsx("span", { className: "min-w-0 truncate text-left", id: nameId, children: name }), _jsx("span", { className: "min-w-16 rounded-xs-plus px-1 py-0.5 text-right text-xs font-normal text-muted-foreground opacity-0 group-focus-within/multi-select-row:opacity-100 group-hover/multi-select-row:opacity-100 [@media(hover:none)]:opacity-100", id: actionId, children: selectsAll ? selectAllLabel : selectOnlyLabel })] })) : (_jsx(Label, { className: "flex-1 truncate px-1 text-sm leading-5", htmlFor: id, id: nameId, children: name }))] }));
|
|
131
|
+
}
|
|
132
|
+
export { MultiSelect, MultiSelectContent, MultiSelectRow, MultiSelectTrigger };
|
|
@@ -1,24 +1,48 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
type
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { type FieldName } from "../internal/field-frame.js";
|
|
3
|
+
import { type FieldSize } from "../internal/input-box.js";
|
|
4
|
+
type NativeSelectProps = Omit<React.ComponentProps<"select">, "prefix" | "size"> & {
|
|
5
|
+
size?: FieldSize;
|
|
6
|
+
/** A mark before the value, such as an icon. */
|
|
7
|
+
prefix?: React.ReactNode;
|
|
8
|
+
/** A mark at the end, in place of the chevron. */
|
|
9
|
+
suffix?: React.ReactNode;
|
|
10
|
+
/** One muted sentence under the box that says what to enter, read with the box. */
|
|
11
|
+
description?: string;
|
|
12
|
+
/** The message under the box, in the product's words; it also marks the box invalid. */
|
|
13
|
+
error?: string;
|
|
14
|
+
} & FieldName;
|
|
5
15
|
/**
|
|
6
16
|
* The platform's own select, drawn as our input box.
|
|
7
17
|
*
|
|
8
18
|
* @family Inputs
|
|
9
19
|
* @when A long fixed list inside a native form, where the platform's own picker serves best: a time
|
|
10
20
|
* zone, a country.
|
|
11
|
-
* @instead Two or three options are radios or a segmented control.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
21
|
+
* @instead Two or three options are radios or a segmented control. "Pick `<Select>` for short,
|
|
22
|
+
* fixed lists (under ~10 items) where typing adds nothing; switch to `Combobox` once filtering
|
|
23
|
+
* helps." (https://vercel.com/geist/select) Ours: `Select` draws the short list in our style.
|
|
24
|
+
* @behaviour Same box, sizes, ring, hover, focus halo and disabled as `Input`, filling its
|
|
25
|
+
* container, the chevron in the muted grey with room reserved for it, after an optional `prefix`
|
|
26
|
+
* mark (a `suffix` mark takes the chevron's place). Colour sits in one place: an error turns the
|
|
27
|
+
* ring red and fills the halo, and colours the message; the fill, the value and the label stay
|
|
28
|
+
* neutral. `label` and `error` draw the `Field` parts around the box, and a string `label` requires
|
|
29
|
+
* `id`; a box with no name does not compile. It opens the system's list, with native keyboard and
|
|
30
|
+
* typing. As an uncontrolled field it keeps a saved value instead of snapping back to the page's
|
|
31
|
+
* first default. "Validate on blur and pass a string to `error`; messages name the field and end in
|
|
32
|
+
* a period (`Select a framework.`)." (https://vercel.com/geist/select) Other form rules are
|
|
33
|
+
* `Field`'s.
|
|
34
|
+
* @words "Label is a short Title Case noun (`Framework`, `Region`); accept the prop directly on
|
|
35
|
+
* `<Select>`." (https://vercel.com/geist/select) "Options are Title Case when short and match
|
|
36
|
+
* canonical branding (`Next.js`, not `NextJS`); keep the same register across the list."
|
|
37
|
+
* (https://vercel.com/geist/select) Ours: the label names the thing chosen (*Time zone*) and
|
|
38
|
+
* options are sentence case. A placeholder option is distinct from a real one and never preselected
|
|
39
|
+
* as if chosen.
|
|
40
|
+
* @access A real `select` with a visible label tied by `id`, or `aria-label`. "Group options past
|
|
41
|
+
* ~10 items with native `<optgroup>`; Geist `<Select>` has no `.Group` static."
|
|
42
|
+
* (https://vercel.com/geist/select) Ours: option groups are `optgroup`. The chevron, the prefix and
|
|
43
|
+
* the suffix are decorative.
|
|
20
44
|
*/
|
|
21
|
-
declare function NativeSelect({ className, size, ref, ...props }: NativeSelectProps): React.JSX.Element;
|
|
45
|
+
declare function NativeSelect({ className, size, prefix, suffix, label, description, error, ref, ...props }: NativeSelectProps): React.JSX.Element;
|
|
22
46
|
declare function NativeSelectOption({ className, ...props }: React.ComponentProps<"option">): React.JSX.Element;
|
|
23
47
|
declare function NativeSelectOptGroup({ className, ...props }: React.ComponentProps<"optgroup">): React.JSX.Element;
|
|
24
48
|
export { NativeSelect, NativeSelectOptGroup, NativeSelectOption };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-select.d.ts","sourceRoot":"","sources":["../../src/components/native-select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"native-select.d.ts","sourceRoot":"","sources":["../../src/components/native-select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,EAAc,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EAAyC,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAG7F,KAAK,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC,GAAG;IACjF,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,gDAAgD;IAChD,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,kDAAkD;IAClD,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,mFAAmF;IACnF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wFAAwF;IACxF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,SAAS,CAAC;AAEd;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,iBAAS,YAAY,CAAC,EACpB,SAAS,EACT,IAAgB,EAChB,MAAM,EACN,MAAM,EACN,KAAK,EACL,WAAW,EACX,KAAK,EACL,GAAG,EACH,GAAG,KAAK,EACT,EAAE,iBAAiB,qBA6FnB;AAED,iBAAS,kBAAkB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,qBAQlF;AAED,iBAAS,oBAAoB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,qBAQtF;AAED,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,CAAC"}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cn } from "../lib/utils.js";
|
|
5
|
+
import { FieldFrame } from "../internal/field-frame.js";
|
|
6
|
+
import { fieldDisabled, fieldRing, fieldStates } from "../internal/input-box.js";
|
|
5
7
|
import { ChevronDownIcon } from "lucide-react";
|
|
6
8
|
/**
|
|
7
9
|
* The platform's own select, drawn as our input box.
|
|
@@ -9,17 +11,31 @@ import { ChevronDownIcon } from "lucide-react";
|
|
|
9
11
|
* @family Inputs
|
|
10
12
|
* @when A long fixed list inside a native form, where the platform's own picker serves best: a time
|
|
11
13
|
* zone, a country.
|
|
12
|
-
* @instead Two or three options are radios or a segmented control.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
14
|
+
* @instead Two or three options are radios or a segmented control. "Pick `<Select>` for short,
|
|
15
|
+
* fixed lists (under ~10 items) where typing adds nothing; switch to `Combobox` once filtering
|
|
16
|
+
* helps." (https://vercel.com/geist/select) Ours: `Select` draws the short list in our style.
|
|
17
|
+
* @behaviour Same box, sizes, ring, hover, focus halo and disabled as `Input`, filling its
|
|
18
|
+
* container, the chevron in the muted grey with room reserved for it, after an optional `prefix`
|
|
19
|
+
* mark (a `suffix` mark takes the chevron's place). Colour sits in one place: an error turns the
|
|
20
|
+
* ring red and fills the halo, and colours the message; the fill, the value and the label stay
|
|
21
|
+
* neutral. `label` and `error` draw the `Field` parts around the box, and a string `label` requires
|
|
22
|
+
* `id`; a box with no name does not compile. It opens the system's list, with native keyboard and
|
|
23
|
+
* typing. As an uncontrolled field it keeps a saved value instead of snapping back to the page's
|
|
24
|
+
* first default. "Validate on blur and pass a string to `error`; messages name the field and end in
|
|
25
|
+
* a period (`Select a framework.`)." (https://vercel.com/geist/select) Other form rules are
|
|
26
|
+
* `Field`'s.
|
|
27
|
+
* @words "Label is a short Title Case noun (`Framework`, `Region`); accept the prop directly on
|
|
28
|
+
* `<Select>`." (https://vercel.com/geist/select) "Options are Title Case when short and match
|
|
29
|
+
* canonical branding (`Next.js`, not `NextJS`); keep the same register across the list."
|
|
30
|
+
* (https://vercel.com/geist/select) Ours: the label names the thing chosen (*Time zone*) and
|
|
31
|
+
* options are sentence case. A placeholder option is distinct from a real one and never preselected
|
|
32
|
+
* as if chosen.
|
|
33
|
+
* @access A real `select` with a visible label tied by `id`, or `aria-label`. "Group options past
|
|
34
|
+
* ~10 items with native `<optgroup>`; Geist `<Select>` has no `.Group` static."
|
|
35
|
+
* (https://vercel.com/geist/select) Ours: option groups are `optgroup`. The chevron, the prefix and
|
|
36
|
+
* the suffix are decorative.
|
|
21
37
|
*/
|
|
22
|
-
function NativeSelect({ className, size = "default", ref, ...props }) {
|
|
38
|
+
function NativeSelect({ className, size = "default", prefix, suffix, label, description, error, ref, ...props }) {
|
|
23
39
|
const select = React.useRef(null);
|
|
24
40
|
const setRef = React.useCallback((node) => {
|
|
25
41
|
select.current = node;
|
|
@@ -63,7 +79,7 @@ function NativeSelect({ className, size = "default", ref, ...props }) {
|
|
|
63
79
|
option.selected = shown.includes(option.value);
|
|
64
80
|
appliedDefault.current = defaultKey;
|
|
65
81
|
}, [defaultKey]);
|
|
66
|
-
return (_jsxs("div", { className: cn("group/native-select relative w-
|
|
82
|
+
return (_jsx(FieldFrame, { label: label, description: description, error: error, size: size, control: props, children: (aria) => (_jsxs("div", { className: cn("group/native-select relative w-full", className), "data-slot": "native-select-wrapper", "data-size": size, children: [prefix ? (_jsx("span", { "data-slot": "native-select-prefix", "aria-hidden": "true", className: "pointer-events-none absolute top-1/2 left-control-padding-inline flex -translate-y-1/2 items-center text-muted-foreground [&_svg:not([class*='size-'])]:size-4", children: prefix })) : null, _jsx("select", { ref: setRef, "data-slot": "native-select", "data-size": size, "data-field-frame": "", className: cn("h-control-height min-h-touch-target w-full min-w-0 cursor-pointer appearance-none rounded-sm bg-(--nc-field,var(--background)) py-0 pr-9 pl-control-padding-inline text-sm select-none selection:bg-primary selection:text-primary-foreground data-[size=lg]:h-nadicode-control-height-lg data-[size=lg]:rounded-md data-[size=lg]:text-base data-[size=sm]:h-nadicode-control-height-sm [@media(hover:hover)_and_(pointer:fine)]:min-h-0", fieldRing, fieldStates, fieldDisabled, prefix ? "pl-10" : undefined), ...props, ...aria }), suffix ? (_jsx("span", { "data-slot": "native-select-suffix", "aria-hidden": "true", className: "pointer-events-none absolute top-1/2 right-3 flex -translate-y-1/2 items-center text-muted-foreground select-none [&_svg:not([class*='size-'])]:size-4", children: suffix })) : (_jsx(ChevronDownIcon, { className: "pointer-events-none absolute top-1/2 right-3 size-3.5 -translate-y-1/2 text-muted-foreground select-none", "aria-hidden": "true", "data-slot": "native-select-icon" }))] })) }));
|
|
67
83
|
}
|
|
68
84
|
function NativeSelectOption({ className, ...props }) {
|
|
69
85
|
return (_jsx("option", { "data-slot": "native-select-option", className: cn("bg-[Canvas] text-[CanvasText]", className), ...props }));
|