@nocoo/basalt 2.1.8 → 3.0.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 +2 -0
- package/ai/COMPATIBILITY.md +11 -1
- package/ai/DESIGN.md +342 -0
- package/ai/INTEGRATION.md +239 -152
- package/ai/RECIPES.md +96 -26
- package/ai/USAGE.md +5 -5
- package/ai/registry.json +2883 -275
- package/dist/assets/brand-mark.js.map +1 -1
- package/dist/charts/area.js.map +1 -1
- package/dist/charts/bar.js.map +1 -1
- package/dist/charts/bullet.js.map +1 -1
- package/dist/charts/charts.js.map +1 -1
- package/dist/charts/config.d.ts +11 -11
- package/dist/charts/config.js +4 -4
- package/dist/charts/config.js.map +1 -1
- package/dist/charts/custom-chart.js.map +1 -1
- package/dist/charts/date-navigation.js +10 -10
- package/dist/charts/date-navigation.js.map +1 -1
- package/dist/charts/donut.js +1 -1
- package/dist/charts/donut.js.map +1 -1
- package/dist/charts/frame.d.ts +1 -1
- package/dist/charts/frame.js +4 -4
- package/dist/charts/frame.js.map +1 -1
- package/dist/charts/funnel.js.map +1 -1
- package/dist/charts/gauge.js +5 -5
- package/dist/charts/gauge.js.map +1 -1
- package/dist/charts/grouped-bar.js.map +1 -1
- package/dist/charts/heatmap-calendar.js +11 -11
- package/dist/charts/heatmap-calendar.js.map +1 -1
- package/dist/charts/heatmap-matrix.js +8 -8
- package/dist/charts/heatmap-matrix.js.map +1 -1
- package/dist/charts/item-list.js +1 -1
- package/dist/charts/item-list.js.map +1 -1
- package/dist/charts/legend.js +19 -19
- package/dist/charts/legend.js.map +1 -1
- package/dist/charts/line.js.map +1 -1
- package/dist/charts/palette.js +2 -2
- package/dist/charts/palette.js.map +1 -1
- package/dist/charts/radar.js.map +1 -1
- package/dist/charts/sample.js.map +1 -1
- package/dist/charts/sankey.js.map +1 -1
- package/dist/charts/series.js.map +1 -1
- package/dist/charts/slot-bar.js +2 -2
- package/dist/charts/slot-bar.js.map +1 -1
- package/dist/charts/sparkline.js +38 -23
- package/dist/charts/sparkline.js.map +1 -1
- package/dist/charts/stacked-bar.js.map +1 -1
- package/dist/charts/stat-card.d.ts +2 -2
- package/dist/charts/stat-card.js +61 -58
- package/dist/charts/stat-card.js.map +1 -1
- package/dist/charts/timeline.js +18 -9
- package/dist/charts/timeline.js.map +1 -1
- package/dist/charts/timeseries.js.map +1 -1
- package/dist/charts/tooltip.js +16 -16
- package/dist/charts/tooltip.js.map +1 -1
- package/dist/components/accordion.js +1 -1
- package/dist/components/accordion.js.map +1 -1
- package/dist/components/alert-dialog.js +4 -4
- package/dist/components/alert-dialog.js.map +1 -1
- package/dist/components/app-header.d.ts +5 -1
- package/dist/components/app-header.js +19 -11
- package/dist/components/app-header.js.map +1 -1
- package/dist/components/app-shell.d.ts +4 -1
- package/dist/components/app-shell.js +8 -5
- package/dist/components/app-shell.js.map +1 -1
- package/dist/components/approval-card.d.ts +13 -0
- package/dist/components/approval-card.js +161 -0
- package/dist/components/approval-card.js.map +1 -0
- package/dist/components/autocomplete.js.map +1 -1
- package/dist/components/avatar.d.ts +12 -0
- package/dist/components/avatar.js +39 -18
- package/dist/components/avatar.js.map +1 -1
- package/dist/components/badge.js +8 -8
- package/dist/components/badge.js.map +1 -1
- package/dist/components/banner.d.ts +2 -2
- package/dist/components/banner.js +9 -8
- package/dist/components/banner.js.map +1 -1
- package/dist/components/basalt-mark.js +1 -1
- package/dist/components/basalt-mark.js.map +1 -1
- package/dist/components/battery-meter.js +5 -5
- package/dist/components/battery-meter.js.map +1 -1
- package/dist/components/breadcrumbs.js +6 -6
- package/dist/components/breadcrumbs.js.map +1 -1
- package/dist/components/button.js +10 -10
- package/dist/components/button.js.map +1 -1
- package/dist/components/chat-bubble.js +4 -3
- package/dist/components/chat-bubble.js.map +1 -1
- package/dist/components/chat-composer.d.ts +21 -3
- package/dist/components/chat-composer.js +126 -60
- package/dist/components/chat-composer.js.map +1 -1
- package/dist/components/chat-header.js +6 -6
- package/dist/components/chat-header.js.map +1 -1
- package/dist/components/chat-inbox.js +34 -30
- package/dist/components/chat-inbox.js.map +1 -1
- package/dist/components/chat-markdown.d.ts +8 -0
- package/dist/components/chat-markdown.js +122 -0
- package/dist/components/chat-markdown.js.map +1 -0
- package/dist/components/chat-message.d.ts +26 -0
- package/dist/components/chat-message.js +145 -0
- package/dist/components/chat-message.js.map +1 -0
- package/dist/components/checkbox.js +11 -11
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/clipboard-text.js +3 -3
- package/dist/components/clipboard-text.js.map +1 -1
- package/dist/components/code.d.ts +22 -20
- package/dist/components/code.js +94 -32
- package/dist/components/code.js.map +1 -1
- package/dist/components/collapsible.js +8 -4
- package/dist/components/collapsible.js.map +1 -1
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/command-palette.js +73 -47
- package/dist/components/command-palette.js.map +1 -1
- package/dist/components/confirm-dialog.d.ts +4 -1
- package/dist/components/confirm-dialog.js +20 -18
- package/dist/components/confirm-dialog.js.map +1 -1
- package/dist/components/context-cards.d.ts +15 -0
- package/dist/components/context-cards.js +148 -0
- package/dist/components/context-cards.js.map +1 -0
- package/dist/components/context-menu.d.ts +1 -1
- package/dist/components/context-menu.js +18 -14
- package/dist/components/context-menu.js.map +1 -1
- package/dist/components/data-table.d.ts +8 -2
- package/dist/components/data-table.js +161 -106
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/date-picker.js +13 -13
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/delete-resource.js +1 -1
- package/dist/components/delete-resource.js.map +1 -1
- package/dist/components/description-list.js +3 -3
- package/dist/components/description-list.js.map +1 -1
- package/dist/components/dialog.d.ts +2 -2
- package/dist/components/dialog.js +8 -8
- package/dist/components/dialog.js.map +1 -1
- package/dist/components/diff-table.d.ts +15 -0
- package/dist/components/diff-table.js +146 -0
- package/dist/components/diff-table.js.map +1 -0
- package/dist/components/dock.js +2 -2
- package/dist/components/dock.js.map +1 -1
- package/dist/components/dropdown-menu.js +23 -18
- package/dist/components/dropdown-menu.js.map +1 -1
- package/dist/components/editable-nav-item.js +63 -58
- package/dist/components/editable-nav-item.js.map +1 -1
- package/dist/components/empty.js +6 -6
- package/dist/components/empty.js.map +1 -1
- package/dist/components/fab.js +1 -1
- package/dist/components/fab.js.map +1 -1
- package/dist/components/field.js +3 -3
- package/dist/components/field.js.map +1 -1
- package/dist/components/file-dropzone.js +7 -7
- package/dist/components/file-dropzone.js.map +1 -1
- package/dist/components/filter-bar.js +6 -6
- package/dist/components/filter-bar.js.map +1 -1
- package/dist/components/flow.js +4 -3
- package/dist/components/flow.js.map +1 -1
- package/dist/components/grid.d.ts +3 -1
- package/dist/components/grid.js +15 -8
- package/dist/components/grid.js.map +1 -1
- package/dist/components/hover-card.js +1 -1
- package/dist/components/hover-card.js.map +1 -1
- package/dist/components/icon-picker.d.ts +8 -2
- package/dist/components/icon-picker.js +75 -47
- package/dist/components/icon-picker.js.map +1 -1
- package/dist/components/inline-editable.js +6 -6
- package/dist/components/inline-editable.js.map +1 -1
- package/dist/components/input-area.js +3 -3
- package/dist/components/input-area.js.map +1 -1
- package/dist/components/input-group.js +5 -5
- package/dist/components/input-group.js.map +1 -1
- package/dist/components/input.js +4 -4
- package/dist/components/input.js.map +1 -1
- package/dist/components/label.js +3 -3
- package/dist/components/label.js.map +1 -1
- package/dist/components/layer-card.d.ts +9 -4
- package/dist/components/layer-card.js +72 -67
- package/dist/components/layer-card.js.map +1 -1
- package/dist/components/link.js.map +1 -1
- package/dist/components/loader.d.ts +16 -10
- package/dist/components/loader.js +46 -53
- package/dist/components/loader.js.map +1 -1
- package/dist/components/loading-screen.js +3 -3
- package/dist/components/loading-screen.js.map +1 -1
- package/dist/components/menu-bar.js +32 -27
- package/dist/components/menu-bar.js.map +1 -1
- package/dist/components/meter.d.ts +5 -5
- package/dist/components/meter.js +25 -12
- package/dist/components/meter.js.map +1 -1
- package/dist/components/multi-select.js +66 -62
- package/dist/components/multi-select.js.map +1 -1
- package/dist/components/navigation-menu.d.ts +2 -2
- package/dist/components/navigation-menu.js +22 -8
- package/dist/components/navigation-menu.js.map +1 -1
- package/dist/components/overlay.js +2 -2
- package/dist/components/overlay.js.map +1 -1
- package/dist/components/page-header.d.ts +3 -1
- package/dist/components/page-header.js +12 -12
- package/dist/components/page-header.js.map +1 -1
- package/dist/components/pagination.js +3 -3
- package/dist/components/pagination.js.map +1 -1
- package/dist/components/popover.js +18 -23
- package/dist/components/popover.js.map +1 -1
- package/dist/components/prompt-bar.d.ts +16 -0
- package/dist/components/prompt-bar.js +49 -0
- package/dist/components/prompt-bar.js.map +1 -0
- package/dist/components/radio.js +9 -9
- package/dist/components/radio.js.map +1 -1
- package/dist/components/recommendation-card.d.ts +13 -0
- package/dist/components/recommendation-card.js +123 -0
- package/dist/components/recommendation-card.js.map +1 -0
- package/dist/components/resource-list.js +1 -1
- package/dist/components/resource-list.js.map +1 -1
- package/dist/components/responsive-master-detail.js +7 -7
- package/dist/components/responsive-master-detail.js.map +1 -1
- package/dist/components/scroll-area.js +3 -3
- package/dist/components/scroll-area.js.map +1 -1
- package/dist/components/section-rule.d.ts +5 -3
- package/dist/components/section-rule.js +12 -12
- package/dist/components/section-rule.js.map +1 -1
- package/dist/components/segment-control.js +3 -3
- package/dist/components/segment-control.js.map +1 -1
- package/dist/components/select.js +47 -32
- package/dist/components/select.js.map +1 -1
- package/dist/components/sensitive-input.js +17 -19
- package/dist/components/sensitive-input.js.map +1 -1
- package/dist/components/separator.js.map +1 -1
- package/dist/components/sheet.d.ts +4 -4
- package/dist/components/sheet.js +12 -10
- package/dist/components/sheet.js.map +1 -1
- package/dist/components/sidebar.d.ts +3 -1
- package/dist/components/sidebar.js +150 -138
- package/dist/components/sidebar.js.map +1 -1
- package/dist/components/skeleton-line.d.ts +1 -1
- package/dist/components/skeleton-line.js +2 -2
- package/dist/components/skeleton-line.js.map +1 -1
- package/dist/components/slider.js +2 -2
- package/dist/components/slider.js.map +1 -1
- package/dist/components/stat-strip.js +5 -4
- package/dist/components/stat-strip.js.map +1 -1
- package/dist/components/switch.js +6 -6
- package/dist/components/switch.js.map +1 -1
- package/dist/components/table-of-contents.js +5 -5
- package/dist/components/table-of-contents.js.map +1 -1
- package/dist/components/table-pager.js +2 -2
- package/dist/components/table-pager.js.map +1 -1
- package/dist/components/table.js +4 -4
- package/dist/components/table.js.map +1 -1
- package/dist/components/tabs.js +7 -6
- package/dist/components/tabs.js.map +1 -1
- package/dist/components/tag-badge.d.ts +10 -10
- package/dist/components/tag-badge.js +11 -11
- package/dist/components/tag-badge.js.map +1 -1
- package/dist/components/tag-color-picker.d.ts +8 -1
- package/dist/components/tag-color-picker.js +33 -25
- package/dist/components/tag-color-picker.js.map +1 -1
- package/dist/components/text.js +5 -5
- package/dist/components/text.js.map +1 -1
- package/dist/components/theme-toggle.js +1 -1
- package/dist/components/theme-toggle.js.map +1 -1
- package/dist/components/thinking.d.ts +16 -0
- package/dist/components/thinking.js +85 -0
- package/dist/components/thinking.js.map +1 -0
- package/dist/components/toast.js +25 -20
- package/dist/components/toast.js.map +1 -1
- package/dist/components/toggle-group.js +6 -5
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/toggle.js +6 -6
- package/dist/components/toggle.js.map +1 -1
- package/dist/components/tool-chips.d.ts +12 -0
- package/dist/components/tool-chips.js +138 -0
- package/dist/components/tool-chips.js.map +1 -0
- package/dist/components/toolbar.js +3 -3
- package/dist/components/toolbar.js.map +1 -1
- package/dist/components/tooltip.js +1 -1
- package/dist/components/tooltip.js.map +1 -1
- package/dist/components/typeahead-field.js +59 -56
- package/dist/components/typeahead-field.js.map +1 -1
- package/dist/components/upload-queue.js +15 -14
- package/dist/components/upload-queue.js.map +1 -1
- package/dist/models/agent-feedback.d.ts +44 -0
- package/dist/models/agent-feedback.js +28 -0
- package/dist/models/agent-feedback.js.map +1 -0
- package/dist/models/avatar.d.ts +2 -0
- package/dist/models/avatar.js +15 -0
- package/dist/models/avatar.js.map +1 -0
- package/dist/models/code.d.ts +6 -0
- package/dist/models/code.js +24 -0
- package/dist/models/code.js.map +1 -0
- package/dist/models/context-cards.d.ts +13 -0
- package/dist/models/context-cards.js +17 -0
- package/dist/models/context-cards.js.map +1 -0
- package/dist/models/diff-table.d.ts +12 -0
- package/dist/models/diff-table.js +9 -0
- package/dist/models/diff-table.js.map +1 -0
- package/dist/models/recommendation.d.ts +26 -0
- package/dist/models/recommendation.js +23 -0
- package/dist/models/recommendation.js.map +1 -0
- package/dist/providers/accent.js +1 -1
- package/dist/providers/accent.js.map +1 -1
- package/dist/providers/link.js.map +1 -1
- package/dist/providers/theme.js +1 -1
- package/dist/providers/theme.js.map +1 -1
- package/dist/styles/base.css +128 -0
- package/dist/styles/standalone.css +1994 -937
- package/dist/styles/tailwind.css +179 -13
- package/dist/styles/tokens.css +679 -20
- package/dist/utils/cn.js +25 -4
- package/dist/utils/cn.js.map +1 -1
- package/dist/utils/control-surface.d.ts +1 -1
- package/dist/utils/control-surface.js +1 -1
- package/dist/utils/control-surface.js.map +1 -1
- package/dist/utils/navigation.d.ts +3 -0
- package/dist/utils/navigation.js +8 -0
- package/dist/utils/navigation.js.map +1 -0
- package/dist/utils/preference-store.js +1 -1
- package/dist/utils/preference-store.js.map +1 -1
- package/dist/utils/selection-indicator.d.ts +1 -1
- package/dist/utils/selection-indicator.js +49 -72
- package/dist/utils/selection-indicator.js.map +1 -1
- package/dist/utils/use-checkable-group.js.map +1 -1
- package/dist/utils/use-hover-highlight.d.ts +4 -0
- package/dist/utils/use-hover-highlight.js +80 -0
- package/dist/utils/use-hover-highlight.js.map +1 -0
- package/dist/viewmodels/use-approval-card.d.ts +20 -0
- package/dist/viewmodels/use-approval-card.js +83 -0
- package/dist/viewmodels/use-approval-card.js.map +1 -0
- package/dist/viewmodels/use-chat-composer.d.ts +16 -0
- package/dist/viewmodels/use-chat-composer.js +34 -0
- package/dist/viewmodels/use-chat-composer.js.map +1 -0
- package/dist/viewmodels/use-chat-message.d.ts +16 -0
- package/dist/viewmodels/use-chat-message.js +26 -0
- package/dist/viewmodels/use-chat-message.js.map +1 -0
- package/dist/viewmodels/use-code-block.d.ts +8 -0
- package/dist/viewmodels/use-code-block.js +38 -0
- package/dist/viewmodels/use-code-block.js.map +1 -0
- package/dist/viewmodels/use-context-cards.d.ts +17 -0
- package/dist/viewmodels/use-context-cards.js +18 -0
- package/dist/viewmodels/use-context-cards.js.map +1 -0
- package/dist/viewmodels/use-diff-table.d.ts +16 -0
- package/dist/viewmodels/use-diff-table.js +42 -0
- package/dist/viewmodels/use-diff-table.js.map +1 -0
- package/dist/viewmodels/use-loader.d.ts +4 -0
- package/dist/viewmodels/use-loader.js +22 -0
- package/dist/viewmodels/use-loader.js.map +1 -0
- package/dist/viewmodels/use-recommendation-card.d.ts +17 -0
- package/dist/viewmodels/use-recommendation-card.js +41 -0
- package/dist/viewmodels/use-recommendation-card.js.map +1 -0
- package/dist/viewmodels/use-thinking.d.ts +13 -0
- package/dist/viewmodels/use-thinking.js +18 -0
- package/dist/viewmodels/use-thinking.js.map +1 -0
- package/dist/viewmodels/use-tool-chips.d.ts +10 -0
- package/dist/viewmodels/use-tool-chips.js +20 -0
- package/dist/viewmodels/use-tool-chips.js.map +1 -0
- package/package.json +3 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"menu-bar.js","names":[],"sources":["../../src/components/menu-bar.tsx"],"sourcesContent":["import * as MenubarPrimitive from \"@radix-ui/react-menubar\";\nimport * as React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { BASALT_UI_CLASS } from \"../utils/control-surface\";\nimport { MENU_GAP, overlayItemClass, overlayPanelClass } from \"./overlay\";\n\ntype RadixMenubarProps = React.ComponentProps<typeof MenubarPrimitive.Root>;\ntype RadixMenubarMenuProps = React.ComponentProps<typeof MenubarPrimitive.Menu>;\ntype RadixMenubarTriggerProps = React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Trigger>;\ntype RadixMenubarContentProps = React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Content>;\ntype RadixMenubarItemProps = React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Item>;\n\nexport interface MenuBarProps\n\textends Omit<\n\t\tRadixMenubarProps,\n\t\t\"value\" | \"defaultValue\" | \"onValueChange\" | \"loop\" | \"dir\" | \"asChild\"\n\t> {\n\t/**\n\t * Controlled value of the currently open menu.\n\t */\n\tvalue?: RadixMenubarProps[\"value\"];\n\t/**\n\t * Uncontrolled initial value of the open menu.\n\t * @default \"\"\n\t */\n\tdefaultValue?: RadixMenubarProps[\"defaultValue\"];\n\t/**\n\t * Callback invoked when the open menu value changes.\n\t */\n\tonValueChange?: RadixMenubarProps[\"onValueChange\"];\n\t/**\n\t * Whether keyboard navigation loops around when reaching ends.\n\t * @default true\n\t */\n\tloop?: RadixMenubarProps[\"loop\"];\n\t/**\n\t * Reading direction of the menubar.\n\t * @default \"ltr\"\n\t */\n\tdir?: RadixMenubarProps[\"dir\"];\n\t/**\n\t * Change the default rendered div element to the child element, merging props and behavior.\n\t * MenuBar forwards ref to HTMLDivElement and inherits native div attributes.\n\t * @default false\n\t */\n\tasChild?: RadixMenubarProps[\"asChild\"];\n}\n\nexport function MenuBar(props: MenuBarProps) {\n\treturn <MenuBarRoot {...props} />;\n}\n\nexport interface MenuBarMenuProps extends Omit<RadixMenubarMenuProps, \"value\"> {\n\t/**\n\t * Unique identifier value for this menu within the menubar.\n\t */\n\tvalue?: RadixMenubarMenuProps[\"value\"];\n}\n\nexport function MenuBarMenu(props: MenuBarMenuProps) {\n\treturn <MenubarPrimitive.Menu {...props} />;\n}\n\nexport interface MenuBarTriggerProps\n\textends Omit<RadixMenubarTriggerProps, \"disabled\" | \"asChild\"> {\n\t/**\n\t * Disable the menubar trigger button.\n\t * @default false\n\t */\n\tdisabled?: RadixMenubarTriggerProps[\"disabled\"];\n\t/**\n\t * Change the default rendered button element to the child element, merging props and behavior.\n\t * Trigger forwards ref to HTMLButtonElement and inherits native button attributes.\n\t * @default false\n\t */\n\tasChild?: RadixMenubarTriggerProps[\"asChild\"];\n}\n\nexport const MenuBarTrigger = React.forwardRef<\n\tReact.ElementRef<typeof MenubarPrimitive.Trigger>,\n\tMenuBarTriggerProps\n>(({ className, ...props }, ref) => (\n\t<MenubarPrimitive.Trigger\n\t\tref={ref}\n\t\tclassName={cn(\n\t\t\tBASALT_UI_CLASS,\n\t\t\t\"inline-flex h-8 items-center rounded-basalt-sm px-3 text-sm font-medium outline-hidden hover:bg-basalt-accent data-[state=open]:bg-basalt-accent\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n));\nMenuBarTrigger.displayName = MenubarPrimitive.Trigger.displayName;\n\nexport interface MenuBarContentProps\n\textends Omit<\n\t\tRadixMenubarContentProps,\n\t\t| \"loop\"\n\t\t| \"side\"\n\t\t| \"sideOffset\"\n\t\t| \"align\"\n\t\t| \"alignOffset\"\n\t\t| \"avoidCollisions\"\n\t\t| \"collisionBoundary\"\n\t\t| \"collisionPadding\"\n\t\t| \"arrowPadding\"\n\t\t| \"sticky\"\n\t\t| \"hideWhenDetached\"\n\t\t| \"forceMount\"\n\t\t| \"asChild\"\n\t\t| \"onCloseAutoFocus\"\n\t\t| \"onEscapeKeyDown\"\n\t\t| \"onPointerDownOutside\"\n\t\t| \"onFocusOutside\"\n\t\t| \"onInteractOutside\"\n\t> {\n\t/**\n\t * Whether keyboard navigation loops around when reaching the start or end item.\n\t * @default false\n\t */\n\tloop?: RadixMenubarContentProps[\"loop\"];\n\t/**\n\t * Preferred placement side relative to the menu trigger.\n\t * @default \"bottom\"\n\t */\n\tside?: RadixMenubarContentProps[\"side\"];\n\t/**\n\t * Distance in pixels between trigger and floating content panel.\n\t * @default 4\n\t */\n\tsideOffset?: RadixMenubarContentProps[\"sideOffset\"];\n\t/**\n\t * Preferred alignment along the side axis.\n\t * @default \"start\"\n\t */\n\talign?: RadixMenubarContentProps[\"align\"];\n\t/**\n\t * Offset in pixels from the start or end alignment position.\n\t * @default 0\n\t */\n\talignOffset?: RadixMenubarContentProps[\"alignOffset\"];\n\t/**\n\t * Whether to reposition content to avoid viewport boundary collisions.\n\t * @default true\n\t */\n\tavoidCollisions?: RadixMenubarContentProps[\"avoidCollisions\"];\n\t/**\n\t * Element or elements bounding boundary collision calculations.\n\t * @default []\n\t */\n\tcollisionBoundary?: RadixMenubarContentProps[\"collisionBoundary\"];\n\t/**\n\t * Virtual padding from collision boundaries in pixels.\n\t * @default 0\n\t */\n\tcollisionPadding?: RadixMenubarContentProps[\"collisionPadding\"];\n\t/**\n\t * Padding in pixels between popper arrow and floating panel edge.\n\t * @default 0\n\t */\n\tarrowPadding?: RadixMenubarContentProps[\"arrowPadding\"];\n\t/**\n\t * Sticky positioning behavior along the align axis when overflowing.\n\t * @default \"partial\"\n\t */\n\tsticky?: RadixMenubarContentProps[\"sticky\"];\n\t/**\n\t * Whether to hide content completely when trigger is fully occluded.\n\t * @default false\n\t */\n\thideWhenDetached?: RadixMenubarContentProps[\"hideWhenDetached\"];\n\t/**\n\t * Force mounting content in DOM for external animation controls.\n\t * When forceMount is true, the content remains mounted even when closed.\n\t * The caller is responsible for visibility transitions and unmounting after animation completes.\n\t */\n\tforceMount?: true;\n\t/**\n\t * Change the default rendered div element to the child element, merging props and behavior.\n\t * Content forwards ref to HTMLDivElement and inherits native div attributes.\n\t * @default false\n\t */\n\tasChild?: RadixMenubarContentProps[\"asChild\"];\n\t/**\n\t * Callback fired when auto-focusing on close. Can be prevented.\n\t */\n\tonCloseAutoFocus?: RadixMenubarContentProps[\"onCloseAutoFocus\"];\n\t/**\n\t * Callback fired when the Escape key is down on the dismissable layer. Can be prevented.\n\t */\n\tonEscapeKeyDown?: RadixMenubarContentProps[\"onEscapeKeyDown\"];\n\t/**\n\t * Callback fired when a pointerdown event happens outside the bounds of the dismissable layer. Can be prevented.\n\t */\n\tonPointerDownOutside?: RadixMenubarContentProps[\"onPointerDownOutside\"];\n\t/**\n\t * Callback fired when focus moves outside the bounds of the dismissable layer. Can be prevented.\n\t */\n\tonFocusOutside?: RadixMenubarContentProps[\"onFocusOutside\"];\n\t/**\n\t * Callback fired when an interaction (pointerdown or focus) happens outside the bounds of the dismissable layer. Can be prevented.\n\t */\n\tonInteractOutside?: RadixMenubarContentProps[\"onInteractOutside\"];\n}\n\nexport const MenuBarContent = React.forwardRef<\n\tReact.ElementRef<typeof MenubarPrimitive.Content>,\n\tMenuBarContentProps\n>(({ className, sideOffset = MENU_GAP, ...props }, ref) => (\n\t<MenubarPrimitive.Portal forceMount={props.forceMount}>\n\t\t<MenubarPrimitive.Content\n\t\t\tref={ref}\n\t\t\tsideOffset={sideOffset}\n\t\t\tclassName={overlayPanelClass(cn(\"min-w-40\", className))}\n\t\t\t{...props}\n\t\t/>\n\t</MenubarPrimitive.Portal>\n));\nMenuBarContent.displayName = MenubarPrimitive.Content.displayName;\n\nexport interface MenuBarItemProps\n\textends Omit<RadixMenubarItemProps, \"disabled\" | \"onSelect\" | \"textValue\" | \"asChild\"> {\n\t/**\n\t * Disable the menubar item.\n\t * @default false\n\t */\n\tdisabled?: RadixMenubarItemProps[\"disabled\"];\n\t/**\n\t * Event handler called when the menubar item is selected (via click or keyboard).\n\t */\n\tonSelect?: RadixMenubarItemProps[\"onSelect\"];\n\t/**\n\t * Optional text used for typeahead navigation. By default typeahead uses item text content.\n\t */\n\ttextValue?: RadixMenubarItemProps[\"textValue\"];\n\t/**\n\t * Change the default rendered div element to the child element, merging props and behavior.\n\t * Item forwards ref to HTMLDivElement and inherits native div attributes.\n\t * @default false\n\t */\n\tasChild?: RadixMenubarItemProps[\"asChild\"];\n}\n\nexport const MenuBarItem = React.forwardRef<\n\tReact.ElementRef<typeof MenubarPrimitive.Item>,\n\tMenuBarItemProps\n>(({ className, ...props }, ref) => (\n\t<MenubarPrimitive.Item\n\t\tref={ref}\n\t\tclassName={overlayItemClass(cn(\"relative outline-hidden focus:bg-basalt-accent\", className))}\n\t\t{...props}\n\t/>\n));\nMenuBarItem.displayName = MenubarPrimitive.Item.displayName;\n\nexport interface MenuBarRootProps extends MenuBarProps {}\n\nexport function MenuBarRoot({ className, ...props }: MenuBarRootProps) {\n\treturn (\n\t\t<MenubarPrimitive.Root\n\t\t\tclassName={cn(\n\t\t\t\tBASALT_UI_CLASS,\n\t\t\t\t\"flex h-9 items-center gap-0.5 rounded-basalt-md border border-basalt-border bg-basalt-popover px-1\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n"],"mappings":";;;;;;;;AAgDA,SAAgB,EAAQ,GAAqB;CAC5C,OAAO,kBAAC,GAAD,EAAa,GAAI,EAAQ,CAAA;AACjC;AASA,SAAgB,EAAY,GAAyB;CACpD,OAAO,kBAAC,EAAiB,MAAlB,EAAuB,GAAI,EAAQ,CAAA;AAC3C;AAiBA,IAAa,IAAiB,EAAM,YAGjC,EAAE,cAAW,GAAG,KAAS,MAC3B,kBAAC,EAAiB,SAAlB;CACM;CACL,WAAW,EACV,GACA,oJACA,CACD;CACA,GAAI;AACJ,CAAA,CACD;AACD,EAAe,cAAc,EAAiB,QAAQ;AAiHtD,IAAa,IAAiB,EAAM,YAGjC,EAAE,cAAW,gBAAA,GAAuB,GAAG,KAAS,MAClD,kBAAC,EAAiB,QAAlB;CAAyB,YAAY,EAAM;CAC1C,UAAA,kBAAC,EAAiB,SAAlB;EACM;EACO;EACZ,WAAW,EAAkB,EAAG,YAAY,CAAS,CAAC;EACtD,GAAI;CACJ,CAAA;AACuB,CAAA,CACzB;AACD,EAAe,cAAc,EAAiB,QAAQ;AAyBtD,IAAa,IAAc,EAAM,YAG9B,EAAE,cAAW,GAAG,KAAS,MAC3B,kBAAC,EAAiB,MAAlB;CACM;CACL,WAAW,EAAiB,EAAG,kDAAkD,CAAS,CAAC;CAC3F,GAAI;AACJ,CAAA,CACD;AACD,EAAY,cAAc,EAAiB,KAAK;AAIhD,SAAgB,EAAY,EAAE,cAAW,GAAG,KAA2B;CACtE,OACC,kBAAC,EAAiB,MAAlB;EACC,WAAW,EACV,GACA,sGACA,CACD;EACA,GAAI;CACJ,CAAA;AAEH"}
|
|
1
|
+
{"version":3,"file":"menu-bar.js","names":["React","MenubarPrimitive","MenuBar","MenuBarRoot","props","MenuBarMenu","MenuBarTrigger","cn","BASALT_UI_CLASS","className","MenuBarContent","highlightRef","useHoverHighlight","ref","overlayPanelClass","MenuBarItem","overlayItemClass"],"sources":["../../src/components/menu-bar.tsx"],"sourcesContent":["import * as MenubarPrimitive from \"@radix-ui/react-menubar\";\nimport * as React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { BASALT_UI_CLASS } from \"../utils/control-surface\";\nimport { useHoverHighlight } from \"../utils/use-hover-highlight\";\nimport { MENU_GAP, overlayItemClass, overlayPanelClass } from \"./overlay\";\n\ntype RadixMenubarProps = React.ComponentProps<typeof MenubarPrimitive.Root>;\ntype RadixMenubarMenuProps = React.ComponentProps<typeof MenubarPrimitive.Menu>;\ntype RadixMenubarTriggerProps = React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Trigger>;\ntype RadixMenubarContentProps = React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Content>;\ntype RadixMenubarItemProps = React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Item>;\n\nexport interface MenuBarProps\n\textends Omit<\n\t\tRadixMenubarProps,\n\t\t\"value\" | \"defaultValue\" | \"onValueChange\" | \"loop\" | \"dir\" | \"asChild\"\n\t> {\n\t/**\n\t * Controlled value of the currently open menu.\n\t */\n\tvalue?: RadixMenubarProps[\"value\"];\n\t/**\n\t * Uncontrolled initial value of the open menu.\n\t * @default \"\"\n\t */\n\tdefaultValue?: RadixMenubarProps[\"defaultValue\"];\n\t/**\n\t * Callback invoked when the open menu value changes.\n\t */\n\tonValueChange?: RadixMenubarProps[\"onValueChange\"];\n\t/**\n\t * Whether keyboard navigation loops around when reaching ends.\n\t * @default true\n\t */\n\tloop?: RadixMenubarProps[\"loop\"];\n\t/**\n\t * Reading direction of the menubar.\n\t * @default \"ltr\"\n\t */\n\tdir?: RadixMenubarProps[\"dir\"];\n\t/**\n\t * Change the default rendered div element to the child element, merging props and behavior.\n\t * MenuBar forwards ref to HTMLDivElement and inherits native div attributes.\n\t * @default false\n\t */\n\tasChild?: RadixMenubarProps[\"asChild\"];\n}\n\nexport function MenuBar(props: MenuBarProps) {\n\treturn <MenuBarRoot {...props} />;\n}\n\nexport interface MenuBarMenuProps extends Omit<RadixMenubarMenuProps, \"value\"> {\n\t/**\n\t * Unique identifier value for this menu within the menubar.\n\t */\n\tvalue?: RadixMenubarMenuProps[\"value\"];\n}\n\nexport function MenuBarMenu(props: MenuBarMenuProps) {\n\treturn <MenubarPrimitive.Menu {...props} />;\n}\n\nexport interface MenuBarTriggerProps\n\textends Omit<RadixMenubarTriggerProps, \"disabled\" | \"asChild\"> {\n\t/**\n\t * Disable the menubar trigger button.\n\t * @default false\n\t */\n\tdisabled?: RadixMenubarTriggerProps[\"disabled\"];\n\t/**\n\t * Change the default rendered button element to the child element, merging props and behavior.\n\t * Trigger forwards ref to HTMLButtonElement and inherits native button attributes.\n\t * @default false\n\t */\n\tasChild?: RadixMenubarTriggerProps[\"asChild\"];\n}\n\nexport const MenuBarTrigger = React.forwardRef<\n\tReact.ElementRef<typeof MenubarPrimitive.Trigger>,\n\tMenuBarTriggerProps\n>(({ className, ...props }, ref) => (\n\t<MenubarPrimitive.Trigger\n\t\tref={ref}\n\t\tclassName={cn(\n\t\t\tBASALT_UI_CLASS,\n\t\t\t\"inline-flex min-h-basalt-control items-center rounded-basalt-sm px-basalt-space-lg text-basalt-base font-medium outline-hidden hover:bg-basalt-hover data-[state=open]:bg-basalt-selected\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n));\nMenuBarTrigger.displayName = MenubarPrimitive.Trigger.displayName;\n\nexport interface MenuBarContentProps\n\textends Omit<\n\t\tRadixMenubarContentProps,\n\t\t| \"loop\"\n\t\t| \"side\"\n\t\t| \"sideOffset\"\n\t\t| \"align\"\n\t\t| \"alignOffset\"\n\t\t| \"avoidCollisions\"\n\t\t| \"collisionBoundary\"\n\t\t| \"collisionPadding\"\n\t\t| \"arrowPadding\"\n\t\t| \"sticky\"\n\t\t| \"hideWhenDetached\"\n\t\t| \"forceMount\"\n\t\t| \"asChild\"\n\t\t| \"onCloseAutoFocus\"\n\t\t| \"onEscapeKeyDown\"\n\t\t| \"onPointerDownOutside\"\n\t\t| \"onFocusOutside\"\n\t\t| \"onInteractOutside\"\n\t> {\n\t/**\n\t * Whether keyboard navigation loops around when reaching the start or end item.\n\t * @default false\n\t */\n\tloop?: RadixMenubarContentProps[\"loop\"];\n\t/**\n\t * Preferred placement side relative to the menu trigger.\n\t * @default \"bottom\"\n\t */\n\tside?: RadixMenubarContentProps[\"side\"];\n\t/**\n\t * Distance in pixels between trigger and floating content panel.\n\t * @default 4\n\t */\n\tsideOffset?: RadixMenubarContentProps[\"sideOffset\"];\n\t/**\n\t * Preferred alignment along the side axis.\n\t * @default \"start\"\n\t */\n\talign?: RadixMenubarContentProps[\"align\"];\n\t/**\n\t * Offset in pixels from the start or end alignment position.\n\t * @default 0\n\t */\n\talignOffset?: RadixMenubarContentProps[\"alignOffset\"];\n\t/**\n\t * Whether to reposition content to avoid viewport boundary collisions.\n\t * @default true\n\t */\n\tavoidCollisions?: RadixMenubarContentProps[\"avoidCollisions\"];\n\t/**\n\t * Element or elements bounding boundary collision calculations.\n\t * @default []\n\t */\n\tcollisionBoundary?: RadixMenubarContentProps[\"collisionBoundary\"];\n\t/**\n\t * Virtual padding from collision boundaries in pixels.\n\t * @default 0\n\t */\n\tcollisionPadding?: RadixMenubarContentProps[\"collisionPadding\"];\n\t/**\n\t * Padding in pixels between popper arrow and floating panel edge.\n\t * @default 0\n\t */\n\tarrowPadding?: RadixMenubarContentProps[\"arrowPadding\"];\n\t/**\n\t * Sticky positioning behavior along the align axis when overflowing.\n\t * @default \"partial\"\n\t */\n\tsticky?: RadixMenubarContentProps[\"sticky\"];\n\t/**\n\t * Whether to hide content completely when trigger is fully occluded.\n\t * @default false\n\t */\n\thideWhenDetached?: RadixMenubarContentProps[\"hideWhenDetached\"];\n\t/**\n\t * Force mounting content in DOM for external animation controls.\n\t * When forceMount is true, the content remains mounted even when closed.\n\t * The caller is responsible for visibility transitions and unmounting after animation completes.\n\t */\n\tforceMount?: true;\n\t/**\n\t * Change the default rendered div element to the child element, merging props and behavior.\n\t * Content forwards ref to HTMLDivElement and inherits native div attributes.\n\t * @default false\n\t */\n\tasChild?: RadixMenubarContentProps[\"asChild\"];\n\t/**\n\t * Callback fired when auto-focusing on close. Can be prevented.\n\t */\n\tonCloseAutoFocus?: RadixMenubarContentProps[\"onCloseAutoFocus\"];\n\t/**\n\t * Callback fired when the Escape key is down on the dismissable layer. Can be prevented.\n\t */\n\tonEscapeKeyDown?: RadixMenubarContentProps[\"onEscapeKeyDown\"];\n\t/**\n\t * Callback fired when a pointerdown event happens outside the bounds of the dismissable layer. Can be prevented.\n\t */\n\tonPointerDownOutside?: RadixMenubarContentProps[\"onPointerDownOutside\"];\n\t/**\n\t * Callback fired when focus moves outside the bounds of the dismissable layer. Can be prevented.\n\t */\n\tonFocusOutside?: RadixMenubarContentProps[\"onFocusOutside\"];\n\t/**\n\t * Callback fired when an interaction (pointerdown or focus) happens outside the bounds of the dismissable layer. Can be prevented.\n\t */\n\tonInteractOutside?: RadixMenubarContentProps[\"onInteractOutside\"];\n}\n\nexport const MenuBarContent = React.forwardRef<\n\tReact.ElementRef<typeof MenubarPrimitive.Content>,\n\tMenuBarContentProps\n>(({ className, sideOffset = MENU_GAP, ...props }, ref) => {\n\tconst highlightRef = useHoverHighlight(ref);\n\treturn (\n\t\t<MenubarPrimitive.Portal forceMount={props.forceMount}>\n\t\t\t<MenubarPrimitive.Content\n\t\t\t\tref={highlightRef}\n\t\t\t\tsideOffset={sideOffset}\n\t\t\t\tclassName={overlayPanelClass(cn(\"basalt-hover-list min-w-basalt-40\", className))}\n\t\t\t\t{...props}\n\t\t\t/>\n\t\t</MenubarPrimitive.Portal>\n\t);\n});\nMenuBarContent.displayName = MenubarPrimitive.Content.displayName;\n\nexport interface MenuBarItemProps\n\textends Omit<RadixMenubarItemProps, \"disabled\" | \"onSelect\" | \"textValue\" | \"asChild\"> {\n\t/**\n\t * Disable the menubar item.\n\t * @default false\n\t */\n\tdisabled?: RadixMenubarItemProps[\"disabled\"];\n\t/**\n\t * Event handler called when the menubar item is selected (via click or keyboard).\n\t */\n\tonSelect?: RadixMenubarItemProps[\"onSelect\"];\n\t/**\n\t * Optional text used for typeahead navigation. By default typeahead uses item text content.\n\t */\n\ttextValue?: RadixMenubarItemProps[\"textValue\"];\n\t/**\n\t * Change the default rendered div element to the child element, merging props and behavior.\n\t * Item forwards ref to HTMLDivElement and inherits native div attributes.\n\t * @default false\n\t */\n\tasChild?: RadixMenubarItemProps[\"asChild\"];\n}\n\nexport const MenuBarItem = React.forwardRef<\n\tReact.ElementRef<typeof MenubarPrimitive.Item>,\n\tMenuBarItemProps\n>(({ className, ...props }, ref) => (\n\t<MenubarPrimitive.Item\n\t\tdata-basalt-hover-item=\"\"\n\t\tref={ref}\n\t\tclassName={overlayItemClass(cn(\"relative outline-hidden\", className))}\n\t\t{...props}\n\t/>\n));\nMenuBarItem.displayName = MenubarPrimitive.Item.displayName;\n\nexport interface MenuBarRootProps extends MenuBarProps {}\n\nexport function MenuBarRoot({ className, ...props }: MenuBarRootProps) {\n\treturn (\n\t\t<MenubarPrimitive.Root\n\t\t\tclassName={cn(\n\t\t\t\tBASALT_UI_CLASS,\n\t\t\t\t\"flex min-h-basalt-control items-center gap-basalt-space-xs rounded-basalt-md border border-basalt-border bg-basalt-popover px-basalt-space-sm py-basalt-space-xs\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n"],"mappings":";;;;;;;;;AAiDA,SAAgBE,EAAQ,GAAqB;CAC5C,OAAO,kBAACC,GAAD,EAAa,GAAIC,EAAQ,CAAA;AACjC;AASA,SAAgBC,EAAY,GAAyB;CACpD,OAAO,kBAACJ,EAAiB,MAAlB,EAAuB,GAAIG,EAAQ,CAAA;AAC3C;AAiBA,IAAaE,IAAiBN,EAAM,YAGjC,EAAE,cAAW,GAAGI,KAAS,MAC3B,kBAACH,EAAiB,SAAlB;CACM;CACL,WAAWM,EACVC,GACA,6LACAC,CACD;CACA,GAAIL;AACJ,CAAA,CACD;AACD,EAAe,cAAcH,EAAiB,QAAQ;AAiHtD,IAAaS,IAAiBV,EAAM,YAGjC,EAAE,cAAW,gBAAA,GAAuB,GAAGI,KAAS,MAAQ;CAC1D,IAAMO,IAAeC,EAAkBC,CAAG;CAC1C,OACC,kBAACZ,EAAiB,QAAlB;EAAyB,YAAYG,EAAM;EAC1C,UAAA,kBAACH,EAAiB,SAAlB;GACC,KAAKU;GACO;GACZ,WAAWG,EAAkBP,EAAG,qCAAqCE,CAAS,CAAC;GAC/E,GAAIL;EACJ,CAAA;CACuB,CAAA;AAE3B,CAAC;AACD,EAAe,cAAcH,EAAiB,QAAQ;AAyBtD,IAAac,IAAcf,EAAM,YAG9B,EAAE,cAAW,GAAGI,KAAS,MAC3B,kBAACH,EAAiB,MAAlB;CACC,0BAAuB;CAClB;CACL,WAAWe,EAAiBT,EAAG,2BAA2BE,CAAS,CAAC;CACpE,GAAIL;AACJ,CAAA,CACD;AACD,EAAY,cAAcH,EAAiB,KAAK;AAIhD,SAAgBE,EAAY,EAAE,cAAW,GAAGC,KAA2B;CACtE,OACC,kBAACH,EAAiB,MAAlB;EACC,WAAWM,EACVC,GACA,oKACAC,CACD;EACA,GAAIL;CACJ,CAAA;AAEH"}
|
|
@@ -2,22 +2,22 @@ export interface MeterProps {
|
|
|
2
2
|
/**
|
|
3
3
|
* Percentage value representing current meter progress (0..100).
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Values are clamped to [0, 100] for both segments and accessibility.
|
|
6
|
+
* Non-finite readings are displayed as unavailable.
|
|
7
7
|
* Does not support arbitrary native HTML props, ref forwarding, min/max, or change callbacks.
|
|
8
8
|
* @default 0
|
|
9
9
|
*/
|
|
10
10
|
value?: number;
|
|
11
11
|
/**
|
|
12
|
-
* Text label displayed above the meter
|
|
12
|
+
* Text label displayed above the segmented meter and used as accessible name fallback.
|
|
13
13
|
*/
|
|
14
14
|
label?: string;
|
|
15
15
|
/**
|
|
16
|
-
* Custom text overriding the
|
|
16
|
+
* Custom text overriding the value displayed beside the segments.
|
|
17
17
|
*/
|
|
18
18
|
customValue?: string;
|
|
19
19
|
/**
|
|
20
|
-
* Whether to hide the
|
|
20
|
+
* Whether to hide the value displayed beside the segments.
|
|
21
21
|
* @default false
|
|
22
22
|
*/
|
|
23
23
|
hideValue?: boolean;
|
package/dist/components/meter.js
CHANGED
|
@@ -4,19 +4,32 @@ import { jsx as t, jsxs as n } from "react/jsx-runtime";
|
|
|
4
4
|
import * as r from "@radix-ui/react-progress";
|
|
5
5
|
//#region src/components/meter.tsx
|
|
6
6
|
function i({ value: i = 0, label: a, customValue: o, hideValue: s = !1, className: c, "aria-label": l }) {
|
|
7
|
+
let u = Number.isFinite(i) ? Math.min(100, Math.max(0, i)) : null, d = Math.round((u ?? 0) / 100 * 17), f = Math.floor(d / 3), p = f + Math.ceil(d / 3);
|
|
7
8
|
return /* @__PURE__ */ n("div", {
|
|
8
|
-
className: e("w-full space-y-
|
|
9
|
-
children: [a
|
|
10
|
-
className: "
|
|
11
|
-
children:
|
|
12
|
-
}) : null, /* @__PURE__ */
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
9
|
+
className: e("w-full space-y-basalt-space-sm", c),
|
|
10
|
+
children: [a ? /* @__PURE__ */ t("div", {
|
|
11
|
+
className: "text-basalt-sm text-basalt-muted-foreground",
|
|
12
|
+
children: a
|
|
13
|
+
}) : null, /* @__PURE__ */ n("div", {
|
|
14
|
+
className: "flex items-center gap-basalt-space-lg",
|
|
15
|
+
children: [/* @__PURE__ */ t(r.Root, {
|
|
16
|
+
"data-basalt-meter": "",
|
|
17
|
+
value: u,
|
|
18
|
+
"aria-label": l ?? a,
|
|
19
|
+
"aria-valuetext": u === null ? "Unavailable" : o,
|
|
20
|
+
className: "h-basalt-3_5 min-w-0 flex-1 overflow-hidden rounded-basalt-sm bg-basalt-muted p-basalt-space-xs",
|
|
21
|
+
children: /* @__PURE__ */ t(r.Indicator, {
|
|
22
|
+
className: "flex h-full gap-basalt-space-xs",
|
|
23
|
+
"aria-hidden": "true",
|
|
24
|
+
children: Array.from({ length: 17 }, (n, r) => /* @__PURE__ */ t("span", {
|
|
25
|
+
"data-filled": r < d,
|
|
26
|
+
className: e("h-full min-w-0 flex-1 rounded-basalt-sm", r >= d ? "bg-basalt-foreground/10" : r < f ? "bg-basalt-danger" : r < p ? "bg-[hsl(var(--basalt-chart-yellow))]" : "bg-[hsl(var(--basalt-chart-green))]")
|
|
27
|
+
}, `segment-${r}`))
|
|
28
|
+
})
|
|
29
|
+
}), s ? null : /* @__PURE__ */ t("span", {
|
|
30
|
+
className: "shrink-0 text-basalt-sm tabular-nums text-basalt-foreground",
|
|
31
|
+
children: o ?? (u === null ? "Unavailable" : `${u}%`)
|
|
32
|
+
})]
|
|
20
33
|
})]
|
|
21
34
|
});
|
|
22
35
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"meter.js","names":[],"sources":["../../src/components/meter.tsx"],"sourcesContent":["import * as Progress from \"@radix-ui/react-progress\";\nimport { cn } from \"../utils/cn\";\n\nexport interface MeterProps {\n\t/**\n\t * Percentage value representing current meter progress (0..100).\n\t *\n\t *
|
|
1
|
+
{"version":3,"file":"meter.js","names":["Progress","Meter","ariaLabel","reading","value","filled","redEnd","yellowEnd","cn","className","label","customValue","index","hideValue"],"sources":["../../src/components/meter.tsx"],"sourcesContent":["import * as Progress from \"@radix-ui/react-progress\";\nimport { cn } from \"../utils/cn\";\n\nexport interface MeterProps {\n\t/**\n\t * Percentage value representing current meter progress (0..100).\n\t *\n\t * Values are clamped to [0, 100] for both segments and accessibility.\n\t * Non-finite readings are displayed as unavailable.\n\t * Does not support arbitrary native HTML props, ref forwarding, min/max, or change callbacks.\n\t * @default 0\n\t */\n\tvalue?: number;\n\t/**\n\t * Text label displayed above the segmented meter and used as accessible name fallback.\n\t */\n\tlabel?: string;\n\t/**\n\t * Custom text overriding the value displayed beside the segments.\n\t */\n\tcustomValue?: string;\n\t/**\n\t * Whether to hide the value displayed beside the segments.\n\t * @default false\n\t */\n\thideValue?: boolean;\n\t/**\n\t * Additional CSS classes applied to the root container.\n\t */\n\tclassName?: string;\n\t/**\n\t * Accessible label announced for assistive technologies. Takes precedence over `label`.\n\t */\n\t\"aria-label\"?: string;\n}\n\nexport function Meter({\n\tvalue = 0,\n\tlabel,\n\tcustomValue,\n\thideValue = false,\n\tclassName,\n\t\"aria-label\": ariaLabel,\n}: MeterProps) {\n\tconst reading = Number.isFinite(value) ? Math.min(100, Math.max(0, value)) : null;\n\tconst filled = Math.round(((reading ?? 0) / 100) * 17);\n\tconst redEnd = Math.floor(filled / 3);\n\tconst yellowEnd = redEnd + Math.ceil(filled / 3);\n\treturn (\n\t\t<div className={cn(\"w-full space-y-basalt-space-sm\", className)}>\n\t\t\t{label ? <div className=\"text-basalt-sm text-basalt-muted-foreground\">{label}</div> : null}\n\t\t\t<div className=\"flex items-center gap-basalt-space-lg\">\n\t\t\t\t<Progress.Root\n\t\t\t\t\tdata-basalt-meter=\"\"\n\t\t\t\t\tvalue={reading}\n\t\t\t\t\taria-label={ariaLabel ?? label}\n\t\t\t\t\taria-valuetext={reading === null ? \"Unavailable\" : customValue}\n\t\t\t\t\tclassName=\"h-basalt-3_5 min-w-0 flex-1 overflow-hidden rounded-basalt-sm bg-basalt-muted p-basalt-space-xs\"\n\t\t\t\t>\n\t\t\t\t\t<Progress.Indicator className=\"flex h-full gap-basalt-space-xs\" aria-hidden=\"true\">\n\t\t\t\t\t\t{Array.from({ length: 17 }, (_, index) => (\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tkey={`segment-${index}`}\n\t\t\t\t\t\t\t\tdata-filled={index < filled}\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\"h-full min-w-0 flex-1 rounded-basalt-sm\",\n\t\t\t\t\t\t\t\t\tindex >= filled\n\t\t\t\t\t\t\t\t\t\t? \"bg-basalt-foreground/10\"\n\t\t\t\t\t\t\t\t\t\t: index < redEnd\n\t\t\t\t\t\t\t\t\t\t\t? \"bg-basalt-danger\"\n\t\t\t\t\t\t\t\t\t\t\t: index < yellowEnd\n\t\t\t\t\t\t\t\t\t\t\t\t? \"bg-[hsl(var(--basalt-chart-yellow))]\"\n\t\t\t\t\t\t\t\t\t\t\t\t: \"bg-[hsl(var(--basalt-chart-green))]\",\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</Progress.Indicator>\n\t\t\t\t</Progress.Root>\n\t\t\t\t{hideValue ? null : (\n\t\t\t\t\t<span className=\"shrink-0 text-basalt-sm tabular-nums text-basalt-foreground\">\n\t\t\t\t\t\t{customValue ?? (reading === null ? \"Unavailable\" : `${reading}%`)}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n"],"mappings":";;;;;AAoCA,SAAgBC,EAAM,EACrB,WAAQ,GACR,UACA,gBACA,eAAY,IACZ,cACA,cAAcC,KACA;CACd,IAAMC,IAAU,OAAO,SAASC,CAAK,IAAI,KAAK,IAAI,KAAK,KAAK,IAAI,GAAGA,CAAK,CAAC,IAAI,MACvEC,IAAS,KAAK,OAAQF,KAAW,KAAK,MAAO,EAAE,GAC/CG,IAAS,KAAK,MAAMD,IAAS,CAAC,GAC9BE,IAAYD,IAAS,KAAK,KAAKD,IAAS,CAAC;CAC/C,OACC,kBAAC,OAAD;EAAK,WAAWG,EAAG,kCAAkCC,CAAS;EAA9D,UAAA,CACEC,IAAQ,kBAAC,OAAD;GAAK,WAAU;GAA+C,UAAAA;EAAW,CAAA,IAAI,MACtF,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACC,kBAACV,EAAS,MAAV;IACC,qBAAkB;IAClB,OAAOG;IACP,cAAYD,KAAaQ;IACzB,kBAAgBP,MAAY,OAAO,gBAAgBQ;IACnD,WAAU;IAEV,UAAA,kBAACX,EAAS,WAAV;KAAoB,WAAU;KAAkC,eAAY;KAC1E,UAAA,MAAM,KAAK,EAAE,QAAQ,GAAG,IAAI,GAAG,MAC/B,kBAAC,QAAD;MAEC,eAAaY,IAAQP;MACrB,WAAWG,EACV,2CACAI,KAASP,IACN,4BACAO,IAAQN,IACP,qBACAM,IAAQL,IACP,yCACA,qCACN;KACA,GAZK,WAAWK,GAYhB,CACD;IACkB,CAAA;GACN,CAAA,GACdC,IAAY,OACZ,kBAAC,QAAD;IAAM,WAAU;IACd,UAAAF,MAAgBR,MAAY,OAAO,gBAAgB,GAAGA,EAAQ;GAC1D,CAAA,CAEH;EACD,CAAA,CAAA;;AAEP"}
|
|
@@ -2,37 +2,38 @@
|
|
|
2
2
|
import { cn as e } from "../utils/cn.js";
|
|
3
3
|
import { BASALT_UI_CLASS as t } from "../utils/control-surface.js";
|
|
4
4
|
import { Button as n } from "./button.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
5
|
+
import { useHoverHighlight as r } from "../utils/use-hover-highlight.js";
|
|
6
|
+
import { Input as i } from "./input.js";
|
|
7
|
+
import { Popover as a, PopoverContent as ee, PopoverTrigger as o } from "./popover.js";
|
|
8
|
+
import { useEffect as s, useId as c, useRef as l, useState as u } from "react";
|
|
9
|
+
import { Check as te, ChevronDown as ne, X as re } from "lucide-react";
|
|
9
10
|
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
10
11
|
//#region src/components/multi-select.tsx
|
|
11
|
-
function p({ label: p, options: m, value: h, defaultValue: g = [], onValueChange:
|
|
12
|
-
let [
|
|
12
|
+
function p({ label: p, options: m, value: h, defaultValue: g = [], onValueChange: ie, query: _, defaultQuery: v = "", onQueryChange: ae, open: y, defaultOpen: oe = !1, onOpenChange: se, filterOptions: ce = !0, loading: b = !1, disabled: x = !1, name: S, form: C, placeholder: le = "Select options", searchPlaceholder: ue = "Search options…", emptyLabel: w = "No options found.", loadingLabel: T = "Loading options…", removeLabel: E = "Remove", showChips: D = !0, formatSelectionCount: O, className: de }) {
|
|
13
|
+
let [fe, k] = u(g), [pe, A] = u(v), [j, M] = u(oe), [N, P] = u(null), F = l(null), I = l(null), L = l(null), R = r(L), z = l(null), B = l({
|
|
13
14
|
value: h,
|
|
14
15
|
defaultValue: g,
|
|
15
|
-
query:
|
|
16
|
-
defaultQuery:
|
|
17
|
-
open:
|
|
16
|
+
query: _,
|
|
17
|
+
defaultQuery: v,
|
|
18
|
+
open: y
|
|
18
19
|
});
|
|
19
|
-
|
|
20
|
+
s(() => {
|
|
20
21
|
B.current = {
|
|
21
22
|
value: h,
|
|
22
23
|
defaultValue: g,
|
|
23
|
-
query:
|
|
24
|
-
defaultQuery:
|
|
25
|
-
open:
|
|
24
|
+
query: _,
|
|
25
|
+
defaultQuery: v,
|
|
26
|
+
open: y
|
|
26
27
|
};
|
|
27
|
-
}),
|
|
28
|
-
let e =
|
|
28
|
+
}), s(() => {
|
|
29
|
+
let e = I.current?.form;
|
|
29
30
|
if (!e) return;
|
|
30
31
|
let t = /* @__PURE__ */ new Set();
|
|
31
32
|
function n(e) {
|
|
32
33
|
let n = setTimeout(() => {
|
|
33
34
|
if (t.delete(n), e.defaultPrevented) return;
|
|
34
35
|
let r = B.current;
|
|
35
|
-
r.value === void 0 &&
|
|
36
|
+
r.value === void 0 && k(r.defaultValue), r.query === void 0 && A(r.defaultQuery), r.open === void 0 && M(!1), P(null);
|
|
36
37
|
}, 0);
|
|
37
38
|
t.add(n);
|
|
38
39
|
}
|
|
@@ -40,13 +41,13 @@ function p({ label: p, options: m, value: h, defaultValue: g = [], onValueChange
|
|
|
40
41
|
for (let e of t) clearTimeout(e);
|
|
41
42
|
e.removeEventListener("reset", n);
|
|
42
43
|
};
|
|
43
|
-
}, [
|
|
44
|
-
let V =
|
|
44
|
+
}, [C]);
|
|
45
|
+
let V = c(), H = h ?? fe, U = _ ?? pe, W = (y ?? j) && !x, G = m.filter((e) => !ce || `${e.label} ${e.description ?? ""}`.toLocaleLowerCase().includes(U.trim().toLocaleLowerCase())), K = G.filter((e) => !e.disabled), q = K.some((e) => e.value === N) ? N : K[0]?.value, J = G.findIndex((e) => e.value === q), Y = O ? O(H.length) : `${H.length} selected`;
|
|
45
46
|
function X(e) {
|
|
46
|
-
|
|
47
|
+
x || (y === void 0 && M(e), se?.(e));
|
|
47
48
|
}
|
|
48
49
|
function Z(e) {
|
|
49
|
-
|
|
50
|
+
x || (h === void 0 && k(e), ie?.(e));
|
|
50
51
|
}
|
|
51
52
|
function Q(e) {
|
|
52
53
|
e.disabled || Z(H.includes(e.value) ? H.filter((t) => t !== e.value) : [...H, e.value]);
|
|
@@ -57,49 +58,49 @@ function p({ label: p, options: m, value: h, defaultValue: g = [], onValueChange
|
|
|
57
58
|
e !== void 0 && Z(H.filter((t) => t !== e));
|
|
58
59
|
}
|
|
59
60
|
return /* @__PURE__ */ f("div", {
|
|
60
|
-
className: e(t, "min-w-0 space-y-
|
|
61
|
+
className: e(t, "min-w-0 space-y-basalt-space-lg", de),
|
|
61
62
|
children: [
|
|
62
|
-
|
|
63
|
+
D && H.length > 0 && /* @__PURE__ */ d("div", {
|
|
63
64
|
role: "group",
|
|
64
|
-
className: "flex flex-wrap gap-
|
|
65
|
+
className: "flex flex-wrap gap-basalt-space-md",
|
|
65
66
|
"aria-label": `${p}: ${Y}`,
|
|
66
67
|
children: H.map((e) => {
|
|
67
68
|
let t = m.find((t) => t.value === e), n = t?.label ?? e;
|
|
68
69
|
return /* @__PURE__ */ f("span", {
|
|
69
|
-
className: "inline-flex max-w-full items-center gap-
|
|
70
|
+
className: "inline-flex max-w-full items-center gap-basalt-space-sm rounded-basalt-md border border-basalt-border bg-basalt-selected py-basalt-space-xs pl-basalt-space-lg text-basalt-sm",
|
|
70
71
|
children: [/* @__PURE__ */ d("span", {
|
|
71
72
|
className: "min-w-0 break-words",
|
|
72
73
|
children: n
|
|
73
74
|
}), /* @__PURE__ */ d("button", {
|
|
74
75
|
type: "button",
|
|
75
|
-
disabled:
|
|
76
|
-
"aria-label": `${
|
|
77
|
-
className: "rounded p-
|
|
76
|
+
disabled: x || t?.disabled,
|
|
77
|
+
"aria-label": `${E} ${n}`,
|
|
78
|
+
className: "rounded-basalt-sm p-basalt-space-sm focus-visible:outline-2 focus-visible:outline-basalt-ring disabled:opacity-50",
|
|
78
79
|
onClick: () => Z(H.filter((t) => t !== e)),
|
|
79
|
-
children: /* @__PURE__ */ d(
|
|
80
|
-
className: "size-
|
|
80
|
+
children: /* @__PURE__ */ d(re, {
|
|
81
|
+
className: "size-basalt-icon-sm",
|
|
81
82
|
"aria-hidden": "true"
|
|
82
83
|
})
|
|
83
84
|
})]
|
|
84
85
|
}, e);
|
|
85
86
|
})
|
|
86
87
|
}),
|
|
87
|
-
!
|
|
88
|
+
!x && S && H.map((e) => /* @__PURE__ */ d("input", {
|
|
88
89
|
type: "hidden",
|
|
89
|
-
name:
|
|
90
|
-
form:
|
|
90
|
+
name: S,
|
|
91
|
+
form: C,
|
|
91
92
|
value: e
|
|
92
93
|
}, e)),
|
|
93
|
-
/* @__PURE__ */ f(
|
|
94
|
+
/* @__PURE__ */ f(a, {
|
|
94
95
|
open: W,
|
|
95
96
|
onOpenChange: X,
|
|
96
|
-
children: [/* @__PURE__ */ d(
|
|
97
|
+
children: [/* @__PURE__ */ d(o, {
|
|
97
98
|
asChild: !0,
|
|
98
99
|
children: /* @__PURE__ */ f(n, {
|
|
99
|
-
ref:
|
|
100
|
-
form:
|
|
100
|
+
ref: I,
|
|
101
|
+
form: C,
|
|
101
102
|
variant: "outline",
|
|
102
|
-
disabled:
|
|
103
|
+
disabled: x,
|
|
103
104
|
"aria-label": p,
|
|
104
105
|
"aria-describedby": `${V}-count`,
|
|
105
106
|
className: "w-full justify-between",
|
|
@@ -108,41 +109,41 @@ function p({ label: p, options: m, value: h, defaultValue: g = [], onValueChange
|
|
|
108
109
|
},
|
|
109
110
|
children: [/* @__PURE__ */ d("span", {
|
|
110
111
|
className: "truncate",
|
|
111
|
-
children: H.length ? Y :
|
|
112
|
-
}), /* @__PURE__ */ d(
|
|
112
|
+
children: H.length ? Y : le
|
|
113
|
+
}), /* @__PURE__ */ d(ne, { "aria-hidden": "true" })]
|
|
113
114
|
})
|
|
114
|
-
}), /* @__PURE__ */ f(
|
|
115
|
+
}), /* @__PURE__ */ f(ee, {
|
|
115
116
|
align: "start",
|
|
116
|
-
className: "w-72 max-w-[calc(100vw-2rem)] p-
|
|
117
|
+
className: "w-basalt-72 max-w-[calc(100vw-2rem)] p-basalt-space-lg",
|
|
117
118
|
"aria-label": p,
|
|
118
119
|
onOpenAutoFocus: (e) => {
|
|
119
|
-
e.preventDefault(),
|
|
120
|
+
e.preventDefault(), F.current?.focus();
|
|
120
121
|
},
|
|
121
122
|
children: [
|
|
122
|
-
/* @__PURE__ */ d(
|
|
123
|
-
ref:
|
|
123
|
+
/* @__PURE__ */ d(i, {
|
|
124
|
+
ref: F,
|
|
124
125
|
role: "combobox",
|
|
125
126
|
"aria-label": `${p}: search`,
|
|
126
127
|
"aria-expanded": W,
|
|
127
128
|
"aria-autocomplete": "list",
|
|
128
129
|
"aria-controls": `${V}-list`,
|
|
129
|
-
"aria-activedescendant": !
|
|
130
|
-
placeholder:
|
|
130
|
+
"aria-activedescendant": !b && J >= 0 ? `${V}-option-${J}` : void 0,
|
|
131
|
+
placeholder: ue,
|
|
131
132
|
value: U,
|
|
132
133
|
onChange: (e) => {
|
|
133
|
-
|
|
134
|
+
_ === void 0 && A(e.target.value), ae?.(e.target.value), P(null);
|
|
134
135
|
},
|
|
135
136
|
onKeyDown: (e) => {
|
|
136
137
|
if (!e.nativeEvent.isComposing) {
|
|
137
138
|
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
|
138
|
-
if (e.preventDefault(), !
|
|
139
|
+
if (e.preventDefault(), !b && K.length) {
|
|
139
140
|
let t = K.findIndex((e) => e.value === q), n = K[(t + (e.key === "ArrowDown" ? 1 : -1) + K.length) % K.length];
|
|
140
|
-
|
|
141
|
+
P(n.value), L.current?.querySelector(`[data-option-index="${G.indexOf(n)}"]`)?.scrollIntoView({ block: "nearest" });
|
|
141
142
|
}
|
|
142
143
|
} else if (e.key === "Enter") {
|
|
143
144
|
e.preventDefault();
|
|
144
145
|
let t = K.find((e) => e.value === q);
|
|
145
|
-
!
|
|
146
|
+
!b && t && Q(t);
|
|
146
147
|
} else e.key === "Backspace" && !U && (e.preventDefault(), $());
|
|
147
148
|
}
|
|
148
149
|
}
|
|
@@ -153,28 +154,31 @@ function p({ label: p, options: m, value: h, defaultValue: g = [], onValueChange
|
|
|
153
154
|
role: "listbox",
|
|
154
155
|
"aria-label": p,
|
|
155
156
|
"aria-multiselectable": "true",
|
|
156
|
-
"aria-busy":
|
|
157
|
-
className: "mt-
|
|
158
|
-
children: !
|
|
157
|
+
"aria-busy": b,
|
|
158
|
+
className: "basalt-hover-list mt-basalt-space-lg max-h-basalt-60 overflow-y-auto",
|
|
159
|
+
children: !b && G.map((t, n) => /* @__PURE__ */ f("button", {
|
|
159
160
|
type: "button",
|
|
160
161
|
role: "option",
|
|
162
|
+
"data-basalt-hover-item": "",
|
|
163
|
+
"data-hover-active": q === t.value,
|
|
161
164
|
id: `${V}-option-${n}`,
|
|
162
165
|
"data-option-index": n,
|
|
163
166
|
tabIndex: -1,
|
|
164
167
|
"aria-selected": H.includes(t.value),
|
|
168
|
+
"data-hover-selected": H.includes(t.value),
|
|
165
169
|
"aria-disabled": t.disabled || void 0,
|
|
166
170
|
disabled: t.disabled,
|
|
167
|
-
className: e("flex w-full items-center gap-
|
|
171
|
+
className: e("flex w-full items-center gap-basalt-space-lg rounded-basalt-md px-basalt-space-lg py-basalt-space-lg text-left text-basalt-base disabled:opacity-40"),
|
|
168
172
|
onPointerMove: (e) => {
|
|
169
173
|
let n = z.current;
|
|
170
174
|
(n?.x !== e.clientX || n.y !== e.clientY) && (z.current = {
|
|
171
175
|
x: e.clientX,
|
|
172
176
|
y: e.clientY
|
|
173
|
-
}, t.disabled ||
|
|
177
|
+
}, t.disabled || P(t.value));
|
|
174
178
|
},
|
|
175
179
|
onMouseDown: (e) => e.preventDefault(),
|
|
176
180
|
onClick: () => {
|
|
177
|
-
Q(t),
|
|
181
|
+
Q(t), F.current?.focus();
|
|
178
182
|
},
|
|
179
183
|
children: [
|
|
180
184
|
t.leading,
|
|
@@ -184,21 +188,21 @@ function p({ label: p, options: m, value: h, defaultValue: g = [], onValueChange
|
|
|
184
188
|
className: "block break-words",
|
|
185
189
|
children: t.label
|
|
186
190
|
}), t.description && /* @__PURE__ */ d("span", {
|
|
187
|
-
className: "block text-
|
|
191
|
+
className: "block text-basalt-sm text-basalt-muted-foreground",
|
|
188
192
|
children: t.description
|
|
189
193
|
})]
|
|
190
194
|
}),
|
|
191
|
-
H.includes(t.value) && /* @__PURE__ */ d(
|
|
192
|
-
className: "size-
|
|
195
|
+
H.includes(t.value) && /* @__PURE__ */ d(te, {
|
|
196
|
+
className: "size-basalt-icon-lg shrink-0",
|
|
193
197
|
"aria-hidden": "true"
|
|
194
198
|
})
|
|
195
199
|
]
|
|
196
200
|
}, t.value))
|
|
197
201
|
}),
|
|
198
|
-
(
|
|
202
|
+
(b || G.length === 0) && /* @__PURE__ */ d("p", {
|
|
199
203
|
role: "status",
|
|
200
|
-
className: "px-
|
|
201
|
-
children:
|
|
204
|
+
className: "px-basalt-space-lg py-basalt-space-lg text-basalt-base text-basalt-muted-foreground",
|
|
205
|
+
children: b ? T : w
|
|
202
206
|
})
|
|
203
207
|
]
|
|
204
208
|
})]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multi-select.js","names":[],"sources":["../../src/components/multi-select.tsx"],"sourcesContent":["import { Check, ChevronDown, X } from \"lucide-react\";\nimport { type ReactNode, useEffect, useId, useRef, useState } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { BASALT_UI_CLASS } from \"../utils/control-surface\";\nimport { Button } from \"./button\";\nimport { Input } from \"./input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"./popover\";\n\nexport interface MultiSelectOption {\n\t/** Stable identifier, independent of the display label. */\n\tvalue: string;\n\t/** Searchable and accessible display name. */\n\tlabel: string;\n\t/** Optional secondary searchable text. */\n\tdescription?: string;\n\t/** Caller-provided icon or avatar. */\n\tleading?: ReactNode;\n\t/** Prevent adding or removing this option. */\n\tdisabled?: boolean;\n}\n\nexport interface MultiSelectProps {\n\t/** Accessible field and popup name. */\n\tlabel: string;\n\t/** Available options; retain selected options to preserve their display labels. */\n\toptions: readonly MultiSelectOption[];\n\t/** Controlled selected identifiers. */\n\tvalue?: readonly string[];\n\t/** Initial uncontrolled selection. @default [] */\n\tdefaultValue?: readonly string[];\n\t/** Selection requests; controlled values change only when accepted by the caller. */\n\tonValueChange?: (value: string[]) => void;\n\t/** Controlled search text; asynchronous requests belong to the caller. */\n\tquery?: string;\n\t/** Initial uncontrolled search text. @default \"\" */\n\tdefaultQuery?: string;\n\t/** Search text requests. */\n\tonQueryChange?: (query: string) => void;\n\t/** Controlled popup visibility. */\n\topen?: boolean;\n\t/** Initial uncontrolled popup visibility. @default false */\n\tdefaultOpen?: boolean;\n\t/** Popup visibility requests. */\n\tonOpenChange?: (open: boolean) => void;\n\t/** Use local label/description filtering; disable for server-filtered options. @default true */\n\tfilterOptions?: boolean;\n\t/** Loading content replaces the option list. @default false */\n\tloading?: boolean;\n\t/** Disables the trigger, chips, and native form entries. @default false */\n\tdisabled?: boolean;\n\t/** Repeated native form entry name, one entry per selected identifier. */\n\tname?: string;\n\t/** Optional external native form owner. Uncontrolled state respects uncancelled form resets. */\n\tform?: string;\n\t/** Empty selection prompt. @default \"Select options\" */\n\tplaceholder?: string;\n\t/** Search input prompt. @default \"Search options…\" */\n\tsearchPlaceholder?: string;\n\t/** Empty search result text. @default \"No options found.\" */\n\temptyLabel?: string;\n\t/** Loading announcement. @default \"Loading options…\" */\n\tloadingLabel?: string;\n\t/** Chip removal label prefix. @default \"Remove\" */\n\tremoveLabel?: string;\n\t/** Show removable selection chips; disable when a FilterBar renders its own chips. @default true */\n\tshowChips?: boolean;\n\t/** Selection count formatter, including localization. */\n\tformatSelectionCount?: (count: number) => string;\n\t/** Additional root classes. */\n\tclassName?: string;\n}\n\nexport function MultiSelect({\n\tlabel,\n\toptions,\n\tvalue,\n\tdefaultValue = [],\n\tonValueChange,\n\tquery,\n\tdefaultQuery = \"\",\n\tonQueryChange,\n\topen,\n\tdefaultOpen = false,\n\tonOpenChange,\n\tfilterOptions = true,\n\tloading = false,\n\tdisabled = false,\n\tname,\n\tform,\n\tplaceholder = \"Select options\",\n\tsearchPlaceholder = \"Search options…\",\n\temptyLabel = \"No options found.\",\n\tloadingLabel = \"Loading options…\",\n\tremoveLabel = \"Remove\",\n\tshowChips = true,\n\tformatSelectionCount,\n\tclassName,\n}: MultiSelectProps) {\n\tconst [localValue, setLocalValue] = useState(defaultValue);\n\tconst [localQuery, setLocalQuery] = useState(defaultQuery);\n\tconst [localOpen, setLocalOpen] = useState(defaultOpen);\n\tconst [active, setActive] = useState<string | null>(null);\n\tconst input = useRef<HTMLInputElement>(null);\n\tconst trigger = useRef<HTMLButtonElement>(null);\n\tconst list = useRef<HTMLDivElement>(null);\n\tconst pointer = useRef<{ x: number; y: number } | null>(null);\n\tconst resetConfig = useRef({ value, defaultValue, query, defaultQuery, open });\n\tuseEffect(() => {\n\t\tresetConfig.current = { value, defaultValue, query, defaultQuery, open };\n\t});\n\t// biome-ignore lint/correctness/useExhaustiveDependencies: form changes the native owner of the same trigger and requires rebinding its reset listener.\n\tuseEffect(() => {\n\t\tconst owner = trigger.current?.form;\n\t\tif (!owner) return;\n\t\tconst timers = new Set<ReturnType<typeof setTimeout>>();\n\t\tfunction reset(event: Event) {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\ttimers.delete(timer);\n\t\t\t\tif (event.defaultPrevented) return;\n\t\t\t\tconst config = resetConfig.current;\n\t\t\t\tif (config.value === undefined) setLocalValue(config.defaultValue);\n\t\t\t\tif (config.query === undefined) setLocalQuery(config.defaultQuery);\n\t\t\t\tif (config.open === undefined) setLocalOpen(false);\n\t\t\t\tsetActive(null);\n\t\t\t}, 0);\n\t\t\ttimers.add(timer);\n\t\t}\n\t\towner.addEventListener(\"reset\", reset);\n\t\treturn () => {\n\t\t\tfor (const timer of timers) clearTimeout(timer);\n\t\t\towner.removeEventListener(\"reset\", reset);\n\t\t};\n\t}, [form]);\n\tconst id = useId();\n\tconst selected = value ?? localValue;\n\tconst search = query ?? localQuery;\n\tconst expanded = (open ?? localOpen) && !disabled;\n\tconst visible = options.filter(\n\t\t(option) =>\n\t\t\t!filterOptions ||\n\t\t\t`${option.label} ${option.description ?? \"\"}`\n\t\t\t\t.toLocaleLowerCase()\n\t\t\t\t.includes(search.trim().toLocaleLowerCase()),\n\t);\n\tconst enabled = visible.filter((option) => !option.disabled);\n\tconst activeValue = enabled.some((option) => option.value === active)\n\t\t? active\n\t\t: enabled[0]?.value;\n\tconst activeIndex = visible.findIndex((option) => option.value === activeValue);\n\tconst countText = formatSelectionCount\n\t\t? formatSelectionCount(selected.length)\n\t\t: `${selected.length} selected`;\n\n\tfunction changeOpen(next: boolean) {\n\t\tif (disabled) return;\n\t\tif (open === undefined) setLocalOpen(next);\n\t\tonOpenChange?.(next);\n\t}\n\tfunction changeValue(next: string[]) {\n\t\tif (disabled) return;\n\t\tif (value === undefined) setLocalValue(next);\n\t\tonValueChange?.(next);\n\t}\n\tfunction toggle(option: MultiSelectOption) {\n\t\tif (option.disabled) return;\n\t\tchangeValue(\n\t\t\tselected.includes(option.value)\n\t\t\t\t? selected.filter((item) => item !== option.value)\n\t\t\t\t: [...selected, option.value],\n\t\t);\n\t}\n\tfunction removeLast() {\n\t\tif (search) return;\n\t\tconst item = [...selected]\n\t\t\t.reverse()\n\t\t\t.find((value) => !options.find((option) => option.value === value)?.disabled);\n\t\tif (item !== undefined) changeValue(selected.filter((value) => value !== item));\n\t}\n\n\treturn (\n\t\t<div className={cn(BASALT_UI_CLASS, \"min-w-0 space-y-2\", className)}>\n\t\t\t{showChips && selected.length > 0 && (\n\t\t\t\t<div role=\"group\" className=\"flex flex-wrap gap-1.5\" aria-label={`${label}: ${countText}`}>\n\t\t\t\t\t{selected.map((item) => {\n\t\t\t\t\t\tconst option = options.find((option) => option.value === item);\n\t\t\t\t\t\tconst text = option?.label ?? item;\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tkey={item}\n\t\t\t\t\t\t\t\tclassName=\"inline-flex max-w-full items-center gap-1 rounded-basalt-md border border-basalt-border bg-basalt-accent py-0.5 pl-2 text-xs\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span className=\"min-w-0 break-words\">{text}</span>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tdisabled={disabled || option?.disabled}\n\t\t\t\t\t\t\t\t\taria-label={`${removeLabel} ${text}`}\n\t\t\t\t\t\t\t\t\tclassName=\"rounded p-1 focus-visible:outline-2 focus-visible:outline-basalt-ring disabled:opacity-50\"\n\t\t\t\t\t\t\t\t\tonClick={() => changeValue(selected.filter((value) => value !== item))}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<X className=\"size-3\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t);\n\t\t\t\t\t})}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t{!disabled &&\n\t\t\t\tname &&\n\t\t\t\tselected.map((item) => (\n\t\t\t\t\t<input key={item} type=\"hidden\" name={name} form={form} value={item} />\n\t\t\t\t))}\n\t\t\t<Popover open={expanded} onOpenChange={changeOpen}>\n\t\t\t\t<PopoverTrigger asChild>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tref={trigger}\n\t\t\t\t\t\tform={form}\n\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\t\taria-label={label}\n\t\t\t\t\t\taria-describedby={`${id}-count`}\n\t\t\t\t\t\tclassName=\"w-full justify-between\"\n\t\t\t\t\t\tonKeyDown={(event) => {\n\t\t\t\t\t\t\tif (event.nativeEvent.isComposing) return;\n\t\t\t\t\t\t\tif (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tchangeOpen(true);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (event.key === \"Backspace\") {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tremoveLast();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t<span className=\"truncate\">{selected.length ? countText : placeholder}</span>\n\t\t\t\t\t\t<ChevronDown aria-hidden=\"true\" />\n\t\t\t\t\t</Button>\n\t\t\t\t</PopoverTrigger>\n\t\t\t\t<PopoverContent\n\t\t\t\t\talign=\"start\"\n\t\t\t\t\tclassName=\"w-72 max-w-[calc(100vw-2rem)] p-2\"\n\t\t\t\t\taria-label={label}\n\t\t\t\t\tonOpenAutoFocus={(event) => {\n\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\tinput.current?.focus();\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<Input\n\t\t\t\t\t\tref={input}\n\t\t\t\t\t\trole=\"combobox\"\n\t\t\t\t\t\taria-label={`${label}: search`}\n\t\t\t\t\t\taria-expanded={expanded}\n\t\t\t\t\t\taria-autocomplete=\"list\"\n\t\t\t\t\t\taria-controls={`${id}-list`}\n\t\t\t\t\t\taria-activedescendant={\n\t\t\t\t\t\t\t!loading && activeIndex >= 0 ? `${id}-option-${activeIndex}` : undefined\n\t\t\t\t\t\t}\n\t\t\t\t\t\tplaceholder={searchPlaceholder}\n\t\t\t\t\t\tvalue={search}\n\t\t\t\t\t\tonChange={(event) => {\n\t\t\t\t\t\t\tif (query === undefined) setLocalQuery(event.target.value);\n\t\t\t\t\t\t\tonQueryChange?.(event.target.value);\n\t\t\t\t\t\t\tsetActive(null);\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tonKeyDown={(event) => {\n\t\t\t\t\t\t\tif (event.nativeEvent.isComposing) return;\n\t\t\t\t\t\t\tif (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tif (!loading && enabled.length) {\n\t\t\t\t\t\t\t\t\tconst index = enabled.findIndex((option) => option.value === activeValue);\n\t\t\t\t\t\t\t\t\tconst next =\n\t\t\t\t\t\t\t\t\t\tenabled[\n\t\t\t\t\t\t\t\t\t\t\t(index + (event.key === \"ArrowDown\" ? 1 : -1) + enabled.length) %\n\t\t\t\t\t\t\t\t\t\t\t\tenabled.length\n\t\t\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t\t\tsetActive(next.value);\n\t\t\t\t\t\t\t\t\tlist.current\n\t\t\t\t\t\t\t\t\t\t?.querySelector<HTMLElement>(`[data-option-index=\"${visible.indexOf(next)}\"]`)\n\t\t\t\t\t\t\t\t\t\t?.scrollIntoView({ block: \"nearest\" });\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else if (event.key === \"Enter\") {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tconst option = enabled.find((option) => option.value === activeValue);\n\t\t\t\t\t\t\t\tif (!loading && option) toggle(option);\n\t\t\t\t\t\t\t} else if (event.key === \"Backspace\" && !search) {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tremoveLast();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\t\t\t\t\t<div\n\t\t\t\t\t\tref={list}\n\t\t\t\t\t\tid={`${id}-list`}\n\t\t\t\t\t\trole=\"listbox\"\n\t\t\t\t\t\taria-label={label}\n\t\t\t\t\t\taria-multiselectable=\"true\"\n\t\t\t\t\t\taria-busy={loading}\n\t\t\t\t\t\tclassName=\"mt-2 max-h-60 overflow-y-auto\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{!loading &&\n\t\t\t\t\t\t\tvisible.map((option, index) => (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\trole=\"option\"\n\t\t\t\t\t\t\t\t\tid={`${id}-option-${index}`}\n\t\t\t\t\t\t\t\t\tdata-option-index={index}\n\t\t\t\t\t\t\t\t\tkey={option.value}\n\t\t\t\t\t\t\t\t\ttabIndex={-1}\n\t\t\t\t\t\t\t\t\taria-selected={selected.includes(option.value)}\n\t\t\t\t\t\t\t\t\taria-disabled={option.disabled || undefined}\n\t\t\t\t\t\t\t\t\tdisabled={option.disabled}\n\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\"flex w-full items-center gap-2 rounded-basalt-md px-2 py-2 text-left text-sm disabled:opacity-40\",\n\t\t\t\t\t\t\t\t\t\tactiveValue === option.value && \"bg-basalt-accent\",\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\tonPointerMove={(event) => {\n\t\t\t\t\t\t\t\t\t\tconst previous = pointer.current;\n\t\t\t\t\t\t\t\t\t\tif (previous?.x === event.clientX && previous.y === event.clientY) return;\n\t\t\t\t\t\t\t\t\t\tpointer.current = { x: event.clientX, y: event.clientY };\n\t\t\t\t\t\t\t\t\t\tif (!option.disabled) setActive(option.value);\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\tonMouseDown={(event) => event.preventDefault()}\n\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\ttoggle(option);\n\t\t\t\t\t\t\t\t\t\tinput.current?.focus();\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{option.leading}\n\t\t\t\t\t\t\t\t\t<span className=\"min-w-0 flex-1\">\n\t\t\t\t\t\t\t\t\t\t<span className=\"block break-words\">{option.label}</span>\n\t\t\t\t\t\t\t\t\t\t{option.description && (\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"block text-xs text-basalt-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t\t{option.description}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t{selected.includes(option.value) && (\n\t\t\t\t\t\t\t\t\t\t<Check className=\"size-4 shrink-0\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t\t{(loading || visible.length === 0) && (\n\t\t\t\t\t\t<p role=\"status\" className=\"px-2 py-4 text-sm text-basalt-muted-foreground\">\n\t\t\t\t\t\t\t{loading ? loadingLabel : emptyLabel}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t</PopoverContent>\n\t\t\t</Popover>\n\t\t\t<span id={`${id}-count`} className=\"sr-only\" aria-live=\"polite\">\n\t\t\t\t{countText}\n\t\t\t</span>\n\t\t</div>\n\t);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"multi-select.js","names":["MultiSelect","localValue","setLocalValue","useState","defaultValue","localQuery","setLocalQuery","defaultQuery","localOpen","setLocalOpen","defaultOpen","active","setActive","input","useRef","trigger","list","highlightRef","useHoverHighlight","pointer","resetConfig","useEffect","owner","timers","reset","timer","event","config","form","id","useId","selected","value","search","query","expanded","open","disabled","visible","options","filterOptions","option","enabled","activeValue","activeIndex","countText","formatSelectionCount","changeOpen","next","onOpenChange","changeValue","onValueChange","toggle","item","removeLast","cn","BASALT_UI_CLASS","className","showChips","label","text","removeLabel","X","name","Popover","PopoverTrigger","Button","placeholder","ChevronDown","PopoverContent","Input","loading","searchPlaceholder","onQueryChange","index","previous","Check","loadingLabel","emptyLabel"],"sources":["../../src/components/multi-select.tsx"],"sourcesContent":["import { Check, ChevronDown, X } from \"lucide-react\";\nimport { type ReactNode, useEffect, useId, useRef, useState } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { BASALT_UI_CLASS } from \"../utils/control-surface\";\nimport { useHoverHighlight } from \"../utils/use-hover-highlight\";\nimport { Button } from \"./button\";\nimport { Input } from \"./input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"./popover\";\n\nexport interface MultiSelectOption {\n\t/** Stable identifier, independent of the display label. */\n\tvalue: string;\n\t/** Searchable and accessible display name. */\n\tlabel: string;\n\t/** Optional secondary searchable text. */\n\tdescription?: string;\n\t/** Caller-provided icon or avatar. */\n\tleading?: ReactNode;\n\t/** Prevent adding or removing this option. */\n\tdisabled?: boolean;\n}\n\nexport interface MultiSelectProps {\n\t/** Accessible field and popup name. */\n\tlabel: string;\n\t/** Available options; retain selected options to preserve their display labels. */\n\toptions: readonly MultiSelectOption[];\n\t/** Controlled selected identifiers. */\n\tvalue?: readonly string[];\n\t/** Initial uncontrolled selection. @default [] */\n\tdefaultValue?: readonly string[];\n\t/** Selection requests; controlled values change only when accepted by the caller. */\n\tonValueChange?: (value: string[]) => void;\n\t/** Controlled search text; asynchronous requests belong to the caller. */\n\tquery?: string;\n\t/** Initial uncontrolled search text. @default \"\" */\n\tdefaultQuery?: string;\n\t/** Search text requests. */\n\tonQueryChange?: (query: string) => void;\n\t/** Controlled popup visibility. */\n\topen?: boolean;\n\t/** Initial uncontrolled popup visibility. @default false */\n\tdefaultOpen?: boolean;\n\t/** Popup visibility requests. */\n\tonOpenChange?: (open: boolean) => void;\n\t/** Use local label/description filtering; disable for server-filtered options. @default true */\n\tfilterOptions?: boolean;\n\t/** Loading content replaces the option list. @default false */\n\tloading?: boolean;\n\t/** Disables the trigger, chips, and native form entries. @default false */\n\tdisabled?: boolean;\n\t/** Repeated native form entry name, one entry per selected identifier. */\n\tname?: string;\n\t/** Optional external native form owner. Uncontrolled state respects uncancelled form resets. */\n\tform?: string;\n\t/** Empty selection prompt. @default \"Select options\" */\n\tplaceholder?: string;\n\t/** Search input prompt. @default \"Search options…\" */\n\tsearchPlaceholder?: string;\n\t/** Empty search result text. @default \"No options found.\" */\n\temptyLabel?: string;\n\t/** Loading announcement. @default \"Loading options…\" */\n\tloadingLabel?: string;\n\t/** Chip removal label prefix. @default \"Remove\" */\n\tremoveLabel?: string;\n\t/** Show removable selection chips; disable when a FilterBar renders its own chips. @default true */\n\tshowChips?: boolean;\n\t/** Selection count formatter, including localization. */\n\tformatSelectionCount?: (count: number) => string;\n\t/** Additional root classes. */\n\tclassName?: string;\n}\n\nexport function MultiSelect({\n\tlabel,\n\toptions,\n\tvalue,\n\tdefaultValue = [],\n\tonValueChange,\n\tquery,\n\tdefaultQuery = \"\",\n\tonQueryChange,\n\topen,\n\tdefaultOpen = false,\n\tonOpenChange,\n\tfilterOptions = true,\n\tloading = false,\n\tdisabled = false,\n\tname,\n\tform,\n\tplaceholder = \"Select options\",\n\tsearchPlaceholder = \"Search options…\",\n\temptyLabel = \"No options found.\",\n\tloadingLabel = \"Loading options…\",\n\tremoveLabel = \"Remove\",\n\tshowChips = true,\n\tformatSelectionCount,\n\tclassName,\n}: MultiSelectProps) {\n\tconst [localValue, setLocalValue] = useState(defaultValue);\n\tconst [localQuery, setLocalQuery] = useState(defaultQuery);\n\tconst [localOpen, setLocalOpen] = useState(defaultOpen);\n\tconst [active, setActive] = useState<string | null>(null);\n\tconst input = useRef<HTMLInputElement>(null);\n\tconst trigger = useRef<HTMLButtonElement>(null);\n\tconst list = useRef<HTMLDivElement>(null);\n\tconst highlightRef = useHoverHighlight(list);\n\tconst pointer = useRef<{ x: number; y: number } | null>(null);\n\tconst resetConfig = useRef({ value, defaultValue, query, defaultQuery, open });\n\tuseEffect(() => {\n\t\tresetConfig.current = { value, defaultValue, query, defaultQuery, open };\n\t});\n\t// biome-ignore lint/correctness/useExhaustiveDependencies: form changes the native owner of the same trigger and requires rebinding its reset listener.\n\tuseEffect(() => {\n\t\tconst owner = trigger.current?.form;\n\t\tif (!owner) return;\n\t\tconst timers = new Set<ReturnType<typeof setTimeout>>();\n\t\tfunction reset(event: Event) {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\ttimers.delete(timer);\n\t\t\t\tif (event.defaultPrevented) return;\n\t\t\t\tconst config = resetConfig.current;\n\t\t\t\tif (config.value === undefined) setLocalValue(config.defaultValue);\n\t\t\t\tif (config.query === undefined) setLocalQuery(config.defaultQuery);\n\t\t\t\tif (config.open === undefined) setLocalOpen(false);\n\t\t\t\tsetActive(null);\n\t\t\t}, 0);\n\t\t\ttimers.add(timer);\n\t\t}\n\t\towner.addEventListener(\"reset\", reset);\n\t\treturn () => {\n\t\t\tfor (const timer of timers) clearTimeout(timer);\n\t\t\towner.removeEventListener(\"reset\", reset);\n\t\t};\n\t}, [form]);\n\tconst id = useId();\n\tconst selected = value ?? localValue;\n\tconst search = query ?? localQuery;\n\tconst expanded = (open ?? localOpen) && !disabled;\n\tconst visible = options.filter(\n\t\t(option) =>\n\t\t\t!filterOptions ||\n\t\t\t`${option.label} ${option.description ?? \"\"}`\n\t\t\t\t.toLocaleLowerCase()\n\t\t\t\t.includes(search.trim().toLocaleLowerCase()),\n\t);\n\tconst enabled = visible.filter((option) => !option.disabled);\n\tconst activeValue = enabled.some((option) => option.value === active)\n\t\t? active\n\t\t: enabled[0]?.value;\n\tconst activeIndex = visible.findIndex((option) => option.value === activeValue);\n\tconst countText = formatSelectionCount\n\t\t? formatSelectionCount(selected.length)\n\t\t: `${selected.length} selected`;\n\n\tfunction changeOpen(next: boolean) {\n\t\tif (disabled) return;\n\t\tif (open === undefined) setLocalOpen(next);\n\t\tonOpenChange?.(next);\n\t}\n\tfunction changeValue(next: string[]) {\n\t\tif (disabled) return;\n\t\tif (value === undefined) setLocalValue(next);\n\t\tonValueChange?.(next);\n\t}\n\tfunction toggle(option: MultiSelectOption) {\n\t\tif (option.disabled) return;\n\t\tchangeValue(\n\t\t\tselected.includes(option.value)\n\t\t\t\t? selected.filter((item) => item !== option.value)\n\t\t\t\t: [...selected, option.value],\n\t\t);\n\t}\n\tfunction removeLast() {\n\t\tif (search) return;\n\t\tconst item = [...selected]\n\t\t\t.reverse()\n\t\t\t.find((value) => !options.find((option) => option.value === value)?.disabled);\n\t\tif (item !== undefined) changeValue(selected.filter((value) => value !== item));\n\t}\n\n\treturn (\n\t\t<div className={cn(BASALT_UI_CLASS, \"min-w-0 space-y-basalt-space-lg\", className)}>\n\t\t\t{showChips && selected.length > 0 && (\n\t\t\t\t<div\n\t\t\t\t\trole=\"group\"\n\t\t\t\t\tclassName=\"flex flex-wrap gap-basalt-space-md\"\n\t\t\t\t\taria-label={`${label}: ${countText}`}\n\t\t\t\t>\n\t\t\t\t\t{selected.map((item) => {\n\t\t\t\t\t\tconst option = options.find((option) => option.value === item);\n\t\t\t\t\t\tconst text = option?.label ?? item;\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tkey={item}\n\t\t\t\t\t\t\t\tclassName=\"inline-flex max-w-full items-center gap-basalt-space-sm rounded-basalt-md border border-basalt-border bg-basalt-selected py-basalt-space-xs pl-basalt-space-lg text-basalt-sm\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span className=\"min-w-0 break-words\">{text}</span>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tdisabled={disabled || option?.disabled}\n\t\t\t\t\t\t\t\t\taria-label={`${removeLabel} ${text}`}\n\t\t\t\t\t\t\t\t\tclassName=\"rounded-basalt-sm p-basalt-space-sm focus-visible:outline-2 focus-visible:outline-basalt-ring disabled:opacity-50\"\n\t\t\t\t\t\t\t\t\tonClick={() => changeValue(selected.filter((value) => value !== item))}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<X className=\"size-basalt-icon-sm\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t);\n\t\t\t\t\t})}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t{!disabled &&\n\t\t\t\tname &&\n\t\t\t\tselected.map((item) => (\n\t\t\t\t\t<input key={item} type=\"hidden\" name={name} form={form} value={item} />\n\t\t\t\t))}\n\t\t\t<Popover open={expanded} onOpenChange={changeOpen}>\n\t\t\t\t<PopoverTrigger asChild>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tref={trigger}\n\t\t\t\t\t\tform={form}\n\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\t\taria-label={label}\n\t\t\t\t\t\taria-describedby={`${id}-count`}\n\t\t\t\t\t\tclassName=\"w-full justify-between\"\n\t\t\t\t\t\tonKeyDown={(event) => {\n\t\t\t\t\t\t\tif (event.nativeEvent.isComposing) return;\n\t\t\t\t\t\t\tif (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tchangeOpen(true);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (event.key === \"Backspace\") {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tremoveLast();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t<span className=\"truncate\">{selected.length ? countText : placeholder}</span>\n\t\t\t\t\t\t<ChevronDown aria-hidden=\"true\" />\n\t\t\t\t\t</Button>\n\t\t\t\t</PopoverTrigger>\n\t\t\t\t<PopoverContent\n\t\t\t\t\talign=\"start\"\n\t\t\t\t\tclassName=\"w-basalt-72 max-w-[calc(100vw-2rem)] p-basalt-space-lg\"\n\t\t\t\t\taria-label={label}\n\t\t\t\t\tonOpenAutoFocus={(event) => {\n\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\tinput.current?.focus();\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<Input\n\t\t\t\t\t\tref={input}\n\t\t\t\t\t\trole=\"combobox\"\n\t\t\t\t\t\taria-label={`${label}: search`}\n\t\t\t\t\t\taria-expanded={expanded}\n\t\t\t\t\t\taria-autocomplete=\"list\"\n\t\t\t\t\t\taria-controls={`${id}-list`}\n\t\t\t\t\t\taria-activedescendant={\n\t\t\t\t\t\t\t!loading && activeIndex >= 0 ? `${id}-option-${activeIndex}` : undefined\n\t\t\t\t\t\t}\n\t\t\t\t\t\tplaceholder={searchPlaceholder}\n\t\t\t\t\t\tvalue={search}\n\t\t\t\t\t\tonChange={(event) => {\n\t\t\t\t\t\t\tif (query === undefined) setLocalQuery(event.target.value);\n\t\t\t\t\t\t\tonQueryChange?.(event.target.value);\n\t\t\t\t\t\t\tsetActive(null);\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tonKeyDown={(event) => {\n\t\t\t\t\t\t\tif (event.nativeEvent.isComposing) return;\n\t\t\t\t\t\t\tif (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tif (!loading && enabled.length) {\n\t\t\t\t\t\t\t\t\tconst index = enabled.findIndex((option) => option.value === activeValue);\n\t\t\t\t\t\t\t\t\tconst next =\n\t\t\t\t\t\t\t\t\t\tenabled[\n\t\t\t\t\t\t\t\t\t\t\t(index + (event.key === \"ArrowDown\" ? 1 : -1) + enabled.length) %\n\t\t\t\t\t\t\t\t\t\t\t\tenabled.length\n\t\t\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t\t\tsetActive(next.value);\n\t\t\t\t\t\t\t\t\tlist.current\n\t\t\t\t\t\t\t\t\t\t?.querySelector<HTMLElement>(`[data-option-index=\"${visible.indexOf(next)}\"]`)\n\t\t\t\t\t\t\t\t\t\t?.scrollIntoView({ block: \"nearest\" });\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else if (event.key === \"Enter\") {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tconst option = enabled.find((option) => option.value === activeValue);\n\t\t\t\t\t\t\t\tif (!loading && option) toggle(option);\n\t\t\t\t\t\t\t} else if (event.key === \"Backspace\" && !search) {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tremoveLast();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\t\t\t\t\t<div\n\t\t\t\t\t\tref={highlightRef}\n\t\t\t\t\t\tid={`${id}-list`}\n\t\t\t\t\t\trole=\"listbox\"\n\t\t\t\t\t\taria-label={label}\n\t\t\t\t\t\taria-multiselectable=\"true\"\n\t\t\t\t\t\taria-busy={loading}\n\t\t\t\t\t\tclassName=\"basalt-hover-list mt-basalt-space-lg max-h-basalt-60 overflow-y-auto\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{!loading &&\n\t\t\t\t\t\t\tvisible.map((option, index) => (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\trole=\"option\"\n\t\t\t\t\t\t\t\t\tdata-basalt-hover-item=\"\"\n\t\t\t\t\t\t\t\t\tdata-hover-active={activeValue === option.value}\n\t\t\t\t\t\t\t\t\tid={`${id}-option-${index}`}\n\t\t\t\t\t\t\t\t\tdata-option-index={index}\n\t\t\t\t\t\t\t\t\tkey={option.value}\n\t\t\t\t\t\t\t\t\ttabIndex={-1}\n\t\t\t\t\t\t\t\t\taria-selected={selected.includes(option.value)}\n\t\t\t\t\t\t\t\t\tdata-hover-selected={selected.includes(option.value)}\n\t\t\t\t\t\t\t\t\taria-disabled={option.disabled || undefined}\n\t\t\t\t\t\t\t\t\tdisabled={option.disabled}\n\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\"flex w-full items-center gap-basalt-space-lg rounded-basalt-md px-basalt-space-lg py-basalt-space-lg text-left text-basalt-base disabled:opacity-40\",\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\tonPointerMove={(event) => {\n\t\t\t\t\t\t\t\t\t\tconst previous = pointer.current;\n\t\t\t\t\t\t\t\t\t\tif (previous?.x === event.clientX && previous.y === event.clientY) return;\n\t\t\t\t\t\t\t\t\t\tpointer.current = { x: event.clientX, y: event.clientY };\n\t\t\t\t\t\t\t\t\t\tif (!option.disabled) setActive(option.value);\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\tonMouseDown={(event) => event.preventDefault()}\n\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\ttoggle(option);\n\t\t\t\t\t\t\t\t\t\tinput.current?.focus();\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{option.leading}\n\t\t\t\t\t\t\t\t\t<span className=\"min-w-0 flex-1\">\n\t\t\t\t\t\t\t\t\t\t<span className=\"block break-words\">{option.label}</span>\n\t\t\t\t\t\t\t\t\t\t{option.description && (\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"block text-basalt-sm text-basalt-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t\t{option.description}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t{selected.includes(option.value) && (\n\t\t\t\t\t\t\t\t\t\t<Check className=\"size-basalt-icon-lg shrink-0\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t\t{(loading || visible.length === 0) && (\n\t\t\t\t\t\t<p\n\t\t\t\t\t\t\trole=\"status\"\n\t\t\t\t\t\t\tclassName=\"px-basalt-space-lg py-basalt-space-lg text-basalt-base text-basalt-muted-foreground\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{loading ? loadingLabel : emptyLabel}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t</PopoverContent>\n\t\t\t</Popover>\n\t\t\t<span id={`${id}-count`} className=\"sr-only\" aria-live=\"polite\">\n\t\t\t\t{countText}\n\t\t\t</span>\n\t\t</div>\n\t);\n}\n"],"mappings":"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"}
|
|
@@ -53,7 +53,7 @@ export interface NavigationMenuListProps extends Omit<RadixNavigationMenuListPro
|
|
|
53
53
|
*/
|
|
54
54
|
asChild?: RadixNavigationMenuListProps["asChild"];
|
|
55
55
|
}
|
|
56
|
-
export declare
|
|
56
|
+
export declare function NavigationMenuList({ className, ref, ...props }: NavigationMenuListProps): React.JSX.Element;
|
|
57
57
|
export interface NavigationMenuItemProps extends Omit<RadixNavigationMenuItemProps, "value" | "asChild"> {
|
|
58
58
|
/**
|
|
59
59
|
* Unique value that associates the item with sub-navigation state.
|
|
@@ -85,5 +85,5 @@ export interface NavigationMenuLinkProps extends Omit<RadixNavigationMenuLinkPro
|
|
|
85
85
|
*/
|
|
86
86
|
asChild?: RadixNavigationMenuLinkProps["asChild"];
|
|
87
87
|
}
|
|
88
|
-
export declare function NavigationMenuLink({ className, ...props }: NavigationMenuLinkProps): React.JSX.Element;
|
|
88
|
+
export declare function NavigationMenuLink({ className, active, ...props }: NavigationMenuLinkProps): React.JSX.Element;
|
|
89
89
|
export {};
|
|
@@ -1,16 +1,30 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn as e } from "../utils/cn.js";
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
3
|
+
import { useHoverHighlight as t } from "../utils/use-hover-highlight.js";
|
|
4
|
+
import { NAV_ITEM_CLASS as n, NAV_LIST_CLASS as r } from "../utils/navigation.js";
|
|
5
|
+
import { jsx as i } from "react/jsx-runtime";
|
|
6
|
+
import * as a from "@radix-ui/react-navigation-menu";
|
|
5
7
|
//#region src/components/navigation-menu.tsx
|
|
6
|
-
var
|
|
7
|
-
function
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
var o = a.Root;
|
|
9
|
+
function s({ className: n, ref: o, ...s }) {
|
|
10
|
+
let c = t(o);
|
|
11
|
+
return /* @__PURE__ */ i(a.List, {
|
|
12
|
+
ref: c,
|
|
13
|
+
className: e(r, "data-[orientation=vertical]:flex-col", n),
|
|
14
|
+
...s
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
var c = a.Item;
|
|
18
|
+
function l({ className: t, active: r, ...o }) {
|
|
19
|
+
return /* @__PURE__ */ i(a.Link, {
|
|
20
|
+
active: r,
|
|
21
|
+
"data-basalt-hover-item": "",
|
|
22
|
+
"data-hover-selected": r,
|
|
23
|
+
className: e(n, t),
|
|
24
|
+
...o
|
|
11
25
|
});
|
|
12
26
|
}
|
|
13
27
|
//#endregion
|
|
14
|
-
export {
|
|
28
|
+
export { o as NavigationMenu, c as NavigationMenuItem, l as NavigationMenuLink, s as NavigationMenuList };
|
|
15
29
|
|
|
16
30
|
//# sourceMappingURL=navigation-menu.js.map
|