@galaxy-io/dls 1.6.0 → 2.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 +47 -50
- package/biome/index.json +74 -0
- package/biome/plugins/no-hex-literal.grit +20 -0
- package/biome/plugins/no-inline-style.grit +13 -0
- package/biome/plugins/no-margin.grit +9 -0
- package/biome/plugins/warn/no-hex-literal.grit +23 -0
- package/biome/plugins/warn/no-inline-style.grit +13 -0
- package/biome/plugins/warn/no-margin.grit +9 -0
- package/dist/a11y/FocusScope.d.ts +44 -0
- package/dist/a11y/FocusScope.js +229 -0
- package/dist/a11y/VisuallyHidden.d.ts +21 -0
- package/dist/a11y/VisuallyHidden.js +30 -0
- package/dist/avatar/Avatar.d.ts +56 -14
- package/dist/avatar/Avatar.js +250 -96
- package/dist/avatar/AvatarGroup.d.ts +43 -0
- package/dist/avatar/AvatarGroup.js +153 -0
- package/dist/backgrounds/GridBackground.d.ts +48 -0
- package/dist/backgrounds/GridBackground.js +146 -0
- package/dist/beacons/Beacon.d.ts +86 -22
- package/dist/beacons/Beacon.js +170 -47
- package/dist/brand/GalaxyFilamentWordmark.d.ts +30 -0
- package/dist/brand/GalaxyFilamentWordmark.js +39 -0
- package/dist/brand/GalaxyLogomark.d.ts +26 -0
- package/dist/brand/GalaxyLogomark.js +41 -0
- package/dist/brand/GalaxyLogomarkAnimation.d.ts +36 -0
- package/dist/brand/GalaxyLogomarkAnimation.js +107 -0
- package/dist/brand/GalaxyWordmark.d.ts +30 -0
- package/dist/brand/GalaxyWordmark.js +63 -0
- package/dist/brand/assets/galaxy-logomark-animation.js +136678 -0
- package/dist/brand/brandMark.d.ts +26 -0
- package/dist/brand/brandMark.js +35 -0
- package/dist/buttons/Button.d.ts +148 -59
- package/dist/buttons/Button.js +437 -227
- package/dist/buttons/ButtonGroup.d.ts +38 -0
- package/dist/buttons/ButtonGroup.js +68 -0
- package/dist/buttons/CopyButton.d.ts +77 -0
- package/dist/buttons/CopyButton.js +65 -0
- package/dist/charts/AreaChart.d.ts +49 -0
- package/dist/charts/AreaChart.js +31 -0
- package/dist/charts/BarChart.d.ts +54 -67
- package/dist/charts/BarChart.js +136 -116
- package/dist/charts/ChartAxis.d.ts +35 -40
- package/dist/charts/ChartAxis.js +114 -90
- package/dist/charts/ChartCategoryTargets.d.ts +12 -26
- package/dist/charts/ChartCategoryTargets.js +32 -20
- package/dist/charts/ChartFrame.d.ts +69 -116
- package/dist/charts/ChartFrame.js +253 -119
- package/dist/charts/ChartGroupProvider.d.ts +30 -41
- package/dist/charts/ChartGroupProvider.js +20 -7
- package/dist/charts/ChartLegend.d.ts +27 -48
- package/dist/charts/ChartLegend.js +206 -125
- package/dist/charts/ChartMarkTargets.d.ts +12 -28
- package/dist/charts/ChartMarkTargets.js +33 -20
- package/dist/charts/ChartPrimitives.d.ts +50 -305
- package/dist/charts/ChartPrimitives.js +65 -413
- package/dist/charts/ChartTooltip.d.ts +20 -26
- package/dist/charts/ChartTooltip.js +77 -40
- package/dist/charts/Heatmap.d.ts +35 -0
- package/dist/charts/Heatmap.js +295 -0
- package/dist/charts/LineChart.d.ts +54 -47
- package/dist/charts/LineChart.js +159 -117
- package/dist/charts/PieChart.d.ts +32 -53
- package/dist/charts/PieChart.js +149 -135
- package/dist/charts/Sparkline.d.ts +107 -0
- package/dist/charts/Sparkline.js +244 -0
- package/dist/charts/StatChart.d.ts +37 -13
- package/dist/charts/StatChart.js +82 -35
- package/dist/charts/barChartGeometry.d.ts +138 -86
- package/dist/charts/barChartGeometry.js +122 -101
- package/dist/charts/barChartLegend.d.ts +14 -18
- package/dist/charts/barChartLegend.js +18 -13
- package/dist/charts/chartFormat.d.ts +12 -18
- package/dist/charts/chartFormat.js +20 -52
- package/dist/charts/chartPalette.d.ts +33 -0
- package/dist/charts/chartPalette.js +68 -0
- package/dist/charts/chartScales.d.ts +41 -108
- package/dist/charts/chartScales.js +62 -128
- package/dist/charts/constants.d.ts +96 -256
- package/dist/charts/constants.js +105 -275
- package/dist/charts/heatmapGeometry.d.ts +95 -0
- package/dist/charts/heatmapGeometry.js +155 -0
- package/dist/charts/lineChartGeometry.d.ts +108 -58
- package/dist/charts/lineChartGeometry.js +120 -78
- package/dist/charts/pieChartGeometry.d.ts +66 -40
- package/dist/charts/pieChartGeometry.js +68 -38
- package/dist/charts/types.d.ts +197 -466
- package/dist/charts/types.js +55 -144
- package/dist/charts/useCartesianChartLayout.d.ts +28 -59
- package/dist/charts/useCartesianChartLayout.js +47 -32
- package/dist/charts/useChartDimensions.d.ts +19 -40
- package/dist/charts/useChartDimensions.js +40 -56
- package/dist/charts/useChartFormatters.d.ts +6 -15
- package/dist/charts/useChartFormatters.js +4 -9
- package/dist/charts/useChartInteraction.d.ts +47 -116
- package/dist/charts/useChartInteraction.js +95 -71
- package/dist/charts/useChartTooltipPosition.d.ts +29 -39
- package/dist/charts/useChartTooltipPosition.js +14 -5
- package/dist/charts/useSeriesColorResolver.d.ts +11 -33
- package/dist/charts/useSeriesColorResolver.js +19 -12
- package/dist/chips/Chip.d.ts +155 -44
- package/dist/chips/Chip.js +329 -155
- package/dist/chips/ChipGroup.d.ts +53 -0
- package/dist/chips/ChipGroup.js +98 -0
- package/dist/drawer/Drawer.d.ts +81 -17
- package/dist/drawer/Drawer.js +370 -65
- package/dist/dropdown/Dropdown.d.ts +80 -63
- package/dist/dropdown/Dropdown.js +127 -129
- package/dist/editor/CodeEditor.d.ts +119 -40
- package/dist/editor/CodeEditor.js +490 -400
- package/dist/editor/CodeEditorReactWidget.d.ts +20 -17
- package/dist/editor/CodeEditorReactWidget.js +28 -21
- package/dist/feedback/Alert.d.ts +49 -0
- package/dist/feedback/Alert.js +172 -0
- package/dist/feedback/EmptyState.d.ts +39 -0
- package/dist/feedback/EmptyState.js +109 -0
- package/dist/feedback/ProgressBar.d.ts +125 -0
- package/dist/feedback/ProgressBar.js +183 -0
- package/dist/feedback/ProgressCircle.d.ts +99 -0
- package/dist/feedback/ProgressCircle.js +187 -0
- package/dist/feedback/Skeleton.d.ts +69 -0
- package/dist/feedback/Skeleton.js +130 -0
- package/dist/feedback/Spinner.d.ts +63 -0
- package/dist/feedback/Spinner.js +141 -0
- package/dist/fonts.css +70 -0
- package/dist/hooks/internal/useIsClient.d.ts +2 -0
- package/dist/hooks/internal/useIsClient.js +14 -0
- package/dist/hooks/useClipboard.d.ts +19 -0
- package/dist/hooks/useClipboard.js +45 -0
- package/dist/hooks/useControlledOpenState.d.ts +21 -0
- package/dist/hooks/useControlledOpenState.js +19 -0
- package/dist/hooks/useControlledState.d.ts +19 -0
- package/dist/hooks/useControlledState.js +22 -0
- package/dist/hooks/useDebouncedValue.d.ts +14 -0
- package/dist/hooks/useDebouncedValue.js +33 -0
- package/dist/hooks/useDisclosure.d.ts +26 -0
- package/dist/hooks/useDisclosure.js +36 -0
- package/dist/hooks/useDismiss.d.ts +23 -27
- package/dist/hooks/useDismiss.js +39 -27
- package/dist/hooks/useElementClientSize.d.ts +10 -10
- package/dist/hooks/useElementClientSize.js +8 -11
- package/dist/hooks/useElementWidth.d.ts +6 -13
- package/dist/hooks/useElementWidth.js +6 -14
- package/dist/hooks/useField.d.ts +47 -0
- package/dist/hooks/useField.js +40 -0
- package/dist/hooks/useHotkey.d.ts +77 -0
- package/dist/hooks/useHotkey.js +246 -0
- package/dist/hooks/useIsMac.d.ts +12 -0
- package/dist/hooks/useIsMac.js +27 -0
- package/dist/hooks/useIsomorphicLayoutEffect.d.ts +17 -0
- package/dist/hooks/useIsomorphicLayoutEffect.js +20 -0
- package/dist/hooks/useLocalStorage.d.ts +28 -0
- package/dist/hooks/useLocalStorage.js +145 -0
- package/dist/hooks/useMediaQuery.d.ts +11 -0
- package/dist/hooks/useMediaQuery.js +23 -0
- package/dist/hooks/usePrefersReducedMotion.d.ts +12 -0
- package/dist/hooks/usePrefersReducedMotion.js +25 -0
- package/dist/hooks/useResize.d.ts +42 -62
- package/dist/hooks/useResize.js +43 -31
- package/dist/icons/Icon.d.ts +64 -37
- package/dist/icons/Icon.js +96 -37
- package/dist/icons/internal/IconSlot.d.ts +10 -0
- package/dist/icons/internal/IconSlot.js +32 -0
- package/dist/inputs/CheckboxGroup.d.ts +115 -0
- package/dist/inputs/CheckboxGroup.js +183 -0
- package/dist/inputs/CheckboxInput.d.ts +98 -28
- package/dist/inputs/CheckboxInput.js +218 -88
- package/dist/inputs/ColorInput.d.ts +115 -29
- package/dist/inputs/ColorInput.js +748 -326
- package/dist/inputs/CopyInput.d.ts +49 -20
- package/dist/inputs/CopyInput.js +57 -49
- package/dist/inputs/CronInput.d.ts +94 -0
- package/dist/inputs/CronInput.js +618 -0
- package/dist/inputs/DateInput.d.ts +175 -28
- package/dist/inputs/DateInput.js +1109 -451
- package/dist/inputs/DurationInput.d.ts +101 -0
- package/dist/inputs/DurationInput.js +441 -0
- package/dist/inputs/Field.d.ts +75 -0
- package/dist/inputs/Field.js +247 -0
- package/dist/inputs/Fieldset.d.ts +53 -0
- package/dist/inputs/Fieldset.js +149 -0
- package/dist/inputs/FileInput.d.ts +110 -0
- package/dist/inputs/FileInput.js +476 -0
- package/dist/inputs/Input.d.ts +148 -187
- package/dist/inputs/Input.js +339 -259
- package/dist/inputs/KeyValueInput.d.ts +120 -0
- package/dist/inputs/KeyValueInput.js +383 -0
- package/dist/inputs/MultiSelectInput.d.ts +165 -54
- package/dist/inputs/MultiSelectInput.js +175 -559
- package/dist/inputs/NumberInput.d.ts +65 -9
- package/dist/inputs/NumberInput.js +197 -40
- package/dist/inputs/PasswordInput.d.ts +17 -12
- package/dist/inputs/PasswordInput.js +33 -33
- package/dist/inputs/PinInput.d.ts +117 -0
- package/dist/inputs/PinInput.js +364 -0
- package/dist/inputs/RadioGroup.d.ts +115 -0
- package/dist/inputs/RadioGroup.js +179 -0
- package/dist/inputs/RadioInput.d.ts +87 -24
- package/dist/inputs/RadioInput.js +182 -95
- package/dist/inputs/SearchInput.d.ts +66 -0
- package/dist/inputs/SearchInput.js +156 -0
- package/dist/inputs/SelectInput.d.ts +195 -55
- package/dist/inputs/SelectInput.js +1308 -446
- package/dist/inputs/SliderInput.d.ts +150 -30
- package/dist/inputs/SliderInput.js +441 -186
- package/dist/inputs/SwitchInput.d.ts +109 -0
- package/dist/inputs/SwitchInput.js +248 -0
- package/dist/inputs/TagInput.d.ts +117 -0
- package/dist/inputs/TagInput.js +408 -0
- package/dist/inputs/TextAreaInput.d.ts +146 -44
- package/dist/inputs/TextAreaInput.js +292 -198
- package/dist/inputs/TextInput.d.ts +4 -13
- package/dist/inputs/TextInput.js +13 -48
- package/dist/inputs/ToggleInput.d.ts +120 -22
- package/dist/inputs/ToggleInput.js +262 -116
- package/dist/inputs/internal/bareField.d.ts +18 -0
- package/dist/inputs/internal/bareField.js +38 -0
- package/dist/inputs/internal/devWarn.d.ts +7 -0
- package/dist/inputs/internal/devWarn.js +30 -0
- package/dist/inputs/internal/fieldFrame.d.ts +34 -0
- package/dist/inputs/internal/fieldFrame.js +64 -0
- package/dist/inputs/internal/fieldWiring.d.ts +50 -0
- package/dist/inputs/internal/fieldWiring.js +38 -0
- package/dist/inputs/internal/innerButton.d.ts +2 -0
- package/dist/inputs/internal/innerButton.js +24 -0
- package/dist/inputs/internal/ownField.d.ts +45 -0
- package/dist/inputs/internal/ownField.js +58 -0
- package/dist/inputs/internal/predicates.d.ts +9 -0
- package/dist/inputs/internal/predicates.js +11 -0
- package/dist/inputs/internal/useComboboxPopover.d.ts +46 -0
- package/dist/inputs/internal/useComboboxPopover.js +92 -0
- package/dist/json/JsonViewer.d.ts +62 -0
- package/dist/json/JsonViewer.js +483 -0
- package/dist/layout/Box.d.ts +87 -0
- package/dist/layout/Box.js +109 -0
- package/dist/layout/Divider.d.ts +25 -0
- package/dist/layout/Divider.js +70 -0
- package/dist/layout/Flex.d.ts +139 -0
- package/dist/layout/Flex.js +165 -0
- package/dist/layout/FlexItem.d.ts +72 -0
- package/dist/layout/FlexItem.js +84 -0
- package/dist/layout/Grid.d.ts +140 -0
- package/dist/layout/Grid.js +178 -0
- package/dist/layout/Portal.d.ts +29 -0
- package/dist/layout/Portal.js +27 -0
- package/dist/layout/ResizablePanels.d.ts +88 -0
- package/dist/layout/ResizablePanels.js +198 -0
- package/dist/layout/ResizeHandle.d.ts +48 -0
- package/dist/layout/ResizeHandle.js +143 -0
- package/dist/layout/ScrollArea.d.ts +30 -0
- package/dist/layout/ScrollArea.js +138 -0
- package/dist/links/Link.d.ts +101 -1
- package/dist/links/Link.js +166 -4
- package/dist/lists/BulletedList.d.ts +25 -7
- package/dist/lists/BulletedList.js +72 -27
- package/dist/lists/DescriptionList.d.ts +91 -0
- package/dist/lists/DescriptionList.js +147 -0
- package/dist/menu/ContextMenu.d.ts +64 -0
- package/dist/menu/ContextMenu.js +34 -0
- package/dist/menu/Menu.d.ts +280 -0
- package/dist/menu/Menu.js +1003 -0
- package/dist/modal/ConfirmDialog.d.ts +55 -0
- package/dist/modal/ConfirmDialog.js +151 -0
- package/dist/modal/Modal.d.ts +64 -14
- package/dist/modal/Modal.js +189 -48
- package/dist/navigation/Breadcrumbs.d.ts +65 -0
- package/dist/navigation/Breadcrumbs.js +243 -0
- package/dist/navigation/CommandPalette.d.ts +83 -0
- package/dist/navigation/CommandPalette.js +509 -0
- package/dist/navigation/Pagination.d.ts +87 -0
- package/dist/navigation/Pagination.js +300 -0
- package/dist/navigation/SidebarNav.d.ts +107 -0
- package/dist/navigation/SidebarNav.js +341 -0
- package/dist/navigation/Stepper.d.ts +52 -0
- package/dist/navigation/Stepper.js +207 -0
- package/dist/navigation/Tabs.d.ts +113 -0
- package/dist/navigation/Tabs.js +543 -0
- package/dist/navigation/Topbar.d.ts +20 -0
- package/dist/navigation/Topbar.js +62 -0
- package/dist/overlay/OverlayProvider.d.ts +28 -32
- package/dist/overlay/OverlayProvider.js +42 -93
- package/dist/overlay/useOverlay.d.ts +88 -12
- package/dist/overlay/useOverlay.js +220 -43
- package/dist/overlays/Popover.d.ts +114 -0
- package/dist/overlays/Popover.js +389 -0
- package/dist/shapes/Circle.d.ts +84 -8
- package/dist/shapes/Circle.js +122 -20
- package/dist/shapes/Square.d.ts +84 -8
- package/dist/shapes/Square.js +123 -20
- package/dist/styles/mixins.d.ts +35 -0
- package/dist/styles/mixins.js +60 -0
- package/dist/styles.css +1 -202
- package/dist/table/InfiniteSpreadsheet.d.ts +140 -10
- package/dist/table/InfiniteSpreadsheet.js +762 -225
- package/dist/table/InfiniteTable.d.ts +116 -14
- package/dist/table/InfiniteTable.js +287 -429
- package/dist/table/SpreadsheetCellEditor.d.ts +54 -6
- package/dist/table/SpreadsheetCellEditor.js +163 -84
- package/dist/table/TableCore.d.ts +252 -0
- package/dist/table/TableCore.js +1077 -0
- package/dist/table/VirtualizedInfiniteTable.d.ts +104 -14
- package/dist/table/VirtualizedInfiniteTable.js +280 -219
- package/dist/table/types.d.ts +66 -244
- package/dist/table/types.js +0 -51
- package/dist/table/useInfiniteScrollSentinel.d.ts +25 -21
- package/dist/table/useInfiniteScrollSentinel.js +38 -30
- package/dist/table/useSpreadsheetCellNavigation.d.ts +44 -0
- package/dist/table/useSpreadsheetCellNavigation.js +141 -0
- package/dist/table/useTableColumnLayout.d.ts +90 -25
- package/dist/table/useTableColumnLayout.js +255 -50
- package/dist/table/useTableKeyboardNavigation.d.ts +32 -21
- package/dist/table/useTableKeyboardNavigation.js +95 -71
- package/dist/table/useTableRowSelection.d.ts +24 -25
- package/dist/table/useTableRowSelection.js +57 -84
- package/dist/table/useTableVirtualizer.d.ts +20 -0
- package/dist/table/useTableVirtualizer.js +48 -0
- package/dist/text/Bold.d.ts +5 -6
- package/dist/text/Bold.js +16 -15
- package/dist/text/Code.d.ts +5 -6
- package/dist/text/Code.js +16 -25
- package/dist/text/CodeBlock.d.ts +77 -0
- package/dist/text/CodeBlock.js +373 -0
- package/dist/text/EditableText.d.ts +55 -15
- package/dist/text/EditableText.js +153 -135
- package/dist/text/Ellipsis.d.ts +34 -8
- package/dist/text/Ellipsis.js +55 -23
- package/dist/text/Kbd.d.ts +72 -0
- package/dist/text/Kbd.js +284 -0
- package/dist/text/MarkdownText.d.ts +27 -10
- package/dist/text/MarkdownText.js +317 -300
- package/dist/text/RequiredMarker.d.ts +17 -5
- package/dist/text/RequiredMarker.js +34 -36
- package/dist/text/Selectable.d.ts +5 -6
- package/dist/text/Selectable.js +16 -13
- package/dist/text/Span.d.ts +82 -0
- package/dist/text/Span.js +115 -0
- package/dist/text/Text.d.ts +128 -41
- package/dist/text/Text.js +209 -76
- package/dist/theme/GalaxyProvider.d.ts +75 -0
- package/dist/theme/GalaxyProvider.js +223 -0
- package/dist/theme/ThemeSwitcher.d.ts +52 -0
- package/dist/theme/ThemeSwitcher.js +94 -0
- package/dist/theme/enums.d.ts +118 -0
- package/dist/theme/enums.js +144 -0
- package/dist/theme/tokens/dark.d.ts +2 -0
- package/dist/theme/tokens/dark.js +275 -0
- package/dist/theme/tokens/generate.d.ts +15 -0
- package/dist/theme/tokens/generate.js +47 -0
- package/dist/theme/tokens/light.d.ts +2 -0
- package/dist/theme/tokens/light.js +274 -0
- package/dist/theme/tokens/palette.d.ts +14 -0
- package/dist/theme/tokens/palette.js +153 -0
- package/dist/theme/tokens/scales.d.ts +26 -0
- package/dist/theme/tokens/scales.js +154 -0
- package/dist/theme/tokens/t.d.ts +3 -0
- package/dist/theme/tokens/t.js +25 -0
- package/dist/theme/tokens/types.d.ts +189 -0
- package/dist/theme/tokens/types.js +0 -0
- package/dist/theme/useGalaxyTheme.d.ts +46 -0
- package/dist/theme/useGalaxyTheme.js +36 -0
- package/dist/theme/useInverseScope.d.ts +18 -0
- package/dist/theme/useInverseScope.js +33 -0
- package/dist/three/GalaxyLogomark3D.d.ts +46 -0
- package/dist/three/GalaxyLogomark3D.js +134 -0
- package/dist/three/assets/galaxy-logomark.js +4 -0
- package/dist/three/galaxyLogomark3DScene.d.ts +27 -0
- package/dist/three/galaxyLogomark3DScene.js +165 -0
- package/dist/timeline/Timeline.d.ts +99 -0
- package/dist/timeline/Timeline.js +269 -0
- package/dist/toast/Toast.d.ts +43 -19
- package/dist/toast/Toast.js +130 -89
- package/dist/toast/ToastProvider.d.ts +23 -23
- package/dist/toast/ToastProvider.js +298 -101
- package/dist/toast/ToastViewport.d.ts +12 -0
- package/dist/toast/ToastViewport.js +210 -0
- package/dist/toast/useToast.d.ts +139 -5
- package/dist/toast/useToast.js +15 -13
- package/dist/tokens.css +654 -0
- package/dist/tooltip/Tooltip.d.ts +64 -44
- package/dist/tooltip/Tooltip.js +280 -73
- package/dist/tooltip/internal/LazyTooltip.d.ts +7 -0
- package/dist/tooltip/internal/LazyTooltip.js +53 -0
- package/dist/transform/Fade.d.ts +33 -11
- package/dist/transform/Fade.js +71 -19
- package/dist/transform/Flasher.d.ts +25 -0
- package/dist/transform/Flasher.js +39 -0
- package/dist/transform/Rotate.d.ts +24 -12
- package/dist/transform/Rotate.js +36 -22
- package/dist/transform/ScaleFade.d.ts +36 -11
- package/dist/transform/ScaleFade.js +86 -28
- package/dist/transform/internal/CollapseMotion.d.ts +10 -0
- package/dist/transform/internal/CollapseMotion.js +65 -0
- package/dist/tree/Tree.d.ts +85 -0
- package/dist/tree/Tree.js +387 -0
- package/dist/tree/treeRow.d.ts +39 -0
- package/dist/tree/treeRow.js +75 -0
- package/dist/utils/colors.d.ts +46 -13
- package/dist/utils/colors.js +92 -34
- package/dist/utils/css.d.ts +23 -0
- package/dist/utils/css.js +30 -0
- package/dist/utils/format.d.ts +58 -0
- package/dist/utils/format.js +220 -0
- package/dist/vite/index.d.ts +16 -0
- package/dist/vite/index.js +52 -0
- package/dist/widget/Widget.d.ts +164 -48
- package/dist/widget/Widget.js +335 -96
- package/dist/widget/WidgetGroup.d.ts +36 -0
- package/dist/widget/WidgetGroup.js +103 -0
- package/package.json +851 -157
- package/dist/accordion/Accordion.d.ts +0 -54
- package/dist/accordion/Accordion.js +0 -183
- package/dist/assets/components/GalaxyFilamentWordmarkDark.d.ts +0 -3
- package/dist/assets/components/GalaxyFilamentWordmarkDark.js +0 -14
- package/dist/assets/components/GalaxyFilamentWordmarkLight.d.ts +0 -3
- package/dist/assets/components/GalaxyFilamentWordmarkLight.js +0 -14
- package/dist/assets/components/GalaxyLogomarkDark.d.ts +0 -3
- package/dist/assets/components/GalaxyLogomarkDark.js +0 -11
- package/dist/assets/components/GalaxyLogomarkLight.d.ts +0 -3
- package/dist/assets/components/GalaxyLogomarkLight.js +0 -17
- package/dist/assets/components/GalaxyWordmarkDark.d.ts +0 -3
- package/dist/assets/components/GalaxyWordmarkDark.js +0 -15
- package/dist/assets/components/GalaxyWordmarkLight.d.ts +0 -3
- package/dist/assets/components/GalaxyWordmarkLight.js +0 -28
- package/dist/assets/models/galaxy-animation.js +0 -136666
- package/dist/assets/models/galaxy-logomark.js +0 -4
- package/dist/avatar/AvatarShimmer.d.ts +0 -30
- package/dist/avatar/AvatarShimmer.js +0 -22
- package/dist/backgrounds/CellGridBackground.d.ts +0 -24
- package/dist/backgrounds/CellGridBackground.js +0 -44
- package/dist/backgrounds/CrossGridBackground.d.ts +0 -26
- package/dist/backgrounds/CrossGridBackground.js +0 -49
- package/dist/backgrounds/DotGridBackground.d.ts +0 -24
- package/dist/backgrounds/DotGridBackground.js +0 -47
- package/dist/badge/Badge.d.ts +0 -42
- package/dist/badge/Badge.js +0 -99
- package/dist/charts/ChartEmptyState.d.ts +0 -17
- package/dist/charts/ChartEmptyState.js +0 -29
- package/dist/charts/ProgressBar.d.ts +0 -29
- package/dist/charts/ProgressBar.js +0 -65
- package/dist/charts/chartTheme.d.ts +0 -18
- package/dist/charts/chartTheme.js +0 -26
- package/dist/containers/FlexItem.d.ts +0 -50
- package/dist/containers/FlexItem.js +0 -91
- package/dist/containers/FlexWrapper.d.ts +0 -96
- package/dist/containers/FlexWrapper.js +0 -143
- package/dist/containers/GridWrapper.d.ts +0 -79
- package/dist/containers/GridWrapper.js +0 -125
- package/dist/containers/Spacing.d.ts +0 -23
- package/dist/containers/Spacing.js +0 -59
- package/dist/containers/Wrapper.d.ts +0 -26
- package/dist/containers/Wrapper.js +0 -54
- package/dist/dividers/HorizontalDivider.d.ts +0 -7
- package/dist/dividers/HorizontalDivider.js +0 -39
- package/dist/dividers/VerticalDivider.d.ts +0 -6
- package/dist/dividers/VerticalDivider.js +0 -21
- package/dist/icons/GalaxyFilamentWordmark.d.ts +0 -11
- package/dist/icons/GalaxyFilamentWordmark.js +0 -25
- package/dist/icons/GalaxyLogomark.d.ts +0 -13
- package/dist/icons/GalaxyLogomark.js +0 -27
- package/dist/icons/GalaxyLogomark3D.d.ts +0 -56
- package/dist/icons/GalaxyLogomark3D.js +0 -319
- package/dist/icons/GalaxyLogomarkAnimation.d.ts +0 -18
- package/dist/icons/GalaxyLogomarkAnimation.js +0 -48
- package/dist/icons/GalaxyWordmark.d.ts +0 -11
- package/dist/icons/GalaxyWordmark.js +0 -25
- package/dist/inputs/MultiTextInput.d.ts +0 -32
- package/dist/inputs/MultiTextInput.js +0 -180
- package/dist/inputs/SwitcherInput.d.ts +0 -27
- package/dist/inputs/SwitcherInput.js +0 -64
- package/dist/inputs/hooks.d.ts +0 -10
- package/dist/inputs/hooks.js +0 -22
- package/dist/overlay/constants.d.ts +0 -28
- package/dist/overlay/constants.js +0 -37
- package/dist/resizable/HorizontalResizeHandle.d.ts +0 -17
- package/dist/resizable/HorizontalResizeHandle.js +0 -38
- package/dist/resizable/VerticalResizeHandle.d.ts +0 -18
- package/dist/resizable/VerticalResizeHandle.js +0 -39
- package/dist/table/SpreadsheetCell.d.ts +0 -21
- package/dist/table/SpreadsheetCell.js +0 -71
- package/dist/table/SpreadsheetHeaderCell.d.ts +0 -23
- package/dist/table/SpreadsheetHeaderCell.js +0 -91
- package/dist/table/SpreadsheetMenuItem.d.ts +0 -14
- package/dist/table/SpreadsheetMenuItem.js +0 -33
- package/dist/table/SpreadsheetPrimitives.d.ts +0 -172
- package/dist/table/SpreadsheetPrimitives.js +0 -105
- package/dist/table/SpreadsheetRow.d.ts +0 -33
- package/dist/table/SpreadsheetRow.js +0 -73
- package/dist/table/SpreadsheetRowActionCell.d.ts +0 -14
- package/dist/table/SpreadsheetRowActionCell.js +0 -51
- package/dist/table/TableExpandCell.d.ts +0 -11
- package/dist/table/TableExpandCell.js +0 -24
- package/dist/table/TableExpandedRow.d.ts +0 -16
- package/dist/table/TableExpandedRow.js +0 -77
- package/dist/table/TableHeaderCellContent.d.ts +0 -30
- package/dist/table/TableHeaderCellContent.js +0 -48
- package/dist/table/TablePrimitives.d.ts +0 -313
- package/dist/table/TablePrimitives.js +0 -302
- package/dist/table/TableSelectCell.d.ts +0 -18
- package/dist/table/TableSelectCell.js +0 -27
- package/dist/table/TableStatusRow.d.ts +0 -18
- package/dist/table/TableStatusRow.js +0 -50
- package/dist/table/useSpreadsheetEditing.d.ts +0 -13
- package/dist/table/useSpreadsheetEditing.js +0 -41
- package/dist/table/useTableColumnResize.d.ts +0 -38
- package/dist/table/useTableColumnResize.js +0 -155
- package/dist/table/useTableRowExpansion.d.ts +0 -24
- package/dist/table/useTableRowExpansion.js +0 -62
- package/dist/table/utils.d.ts +0 -50
- package/dist/table/utils.js +0 -78
- package/dist/text/HotKeys.d.ts +0 -30
- package/dist/text/HotKeys.js +0 -74
- package/dist/text/Paragraph.d.ts +0 -33
- package/dist/text/Paragraph.js +0 -83
- package/dist/text/TextShimmer.d.ts +0 -14
- package/dist/text/TextShimmer.js +0 -34
- package/dist/theme/GalaxyTheme.d.ts +0 -67
- package/dist/theme/GalaxyTheme.js +0 -119
- package/dist/theme/constants.d.ts +0 -8
- package/dist/theme/constants.js +0 -38
- package/dist/theme/index.d.ts +0 -6
- package/dist/theme/index.js +0 -7
- package/dist/theme/types.d.ts +0 -195
- package/dist/theme/types.js +0 -10
- package/dist/theme/typography.d.ts +0 -2
- package/dist/theme/typography.js +0 -73
- package/dist/theme/variants/dark.d.ts +0 -2
- package/dist/theme/variants/dark.js +0 -125
- package/dist/theme/variants/light.d.ts +0 -2
- package/dist/theme/variants/light.js +0 -125
- package/dist/toast/ToastContainer.d.ts +0 -10
- package/dist/toast/ToastContainer.js +0 -31
- package/dist/toast/ToastWrapper.d.ts +0 -6
- package/dist/toast/ToastWrapper.js +0 -27
- package/dist/transform/Flash.d.ts +0 -7
- package/dist/transform/Flash.js +0 -18
- package/dist/transform/Spin.d.ts +0 -14
- package/dist/transform/Spin.js +0 -25
- package/dist/utils/linaria.d.ts +0 -1
- package/dist/utils/linaria.js +0 -8
|
@@ -1,495 +1,1357 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { useIsomorphicLayoutEffect } from "../hooks/useIsomorphicLayoutEffect.js";
|
|
2
|
+
import { Placement } from "../theme/enums.js";
|
|
3
|
+
import Text, { TextSize, TextVariant, TextWeight } from "../text/Text.js";
|
|
4
|
+
import { CONTROL_SIZE, SPACE, TYPE_SCALE } from "../theme/tokens/scales.js";
|
|
5
|
+
import { useControlledState } from "../hooks/useControlledState.js";
|
|
6
|
+
import { useControlledOpenState } from "../hooks/useControlledOpenState.js";
|
|
7
|
+
import Tooltip from "../tooltip/Tooltip.js";
|
|
8
|
+
import Divider from "../layout/Divider.js";
|
|
9
|
+
import ScrollArea from "../layout/ScrollArea.js";
|
|
10
|
+
import Popover, { PopoverRole, PopoverTrigger, PopoverVariant } from "../overlays/Popover.js";
|
|
11
|
+
import Icon, { IconVariant } from "../icons/Icon.js";
|
|
3
12
|
import Rotate from "../transform/Rotate.js";
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
import
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
+
import Button, { ButtonSize, ButtonVariant } from "../buttons/Button.js";
|
|
14
|
+
import Spinner, { SpinnerSize } from "../feedback/Spinner.js";
|
|
15
|
+
import { useDebouncedValue } from "../hooks/useDebouncedValue.js";
|
|
16
|
+
import Span from "../text/Span.js";
|
|
17
|
+
import { isDevelopment, useDevWarning } from "./internal/devWarn.js";
|
|
18
|
+
import { hasContent, isFilled } from "./internal/predicates.js";
|
|
19
|
+
import { useFieldWiring } from "./internal/fieldWiring.js";
|
|
20
|
+
import Field, { FieldSize } from "./Field.js";
|
|
21
|
+
import { rootStyle, useOwnField } from "./internal/ownField.js";
|
|
22
|
+
import { BareFieldContext, useIsBareField } from "./internal/bareField.js";
|
|
23
|
+
import "./internal/innerButton.js";
|
|
24
|
+
import { useDedupedOpen, useRestoreFocusAfterOutsidePress } from "./internal/useComboboxPopover.js";
|
|
13
25
|
/* empty css */
|
|
14
26
|
import { styled } from "@linaria/react";
|
|
15
|
-
import { useCallback, useEffect,
|
|
27
|
+
import { forwardRef, useCallback, useEffect, useRef, useState } from "react";
|
|
16
28
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
17
|
-
import {
|
|
29
|
+
import { cx } from "@linaria/core";
|
|
30
|
+
import { useMergeRefs } from "@floating-ui/react";
|
|
31
|
+
import { CaretDownIcon, CheckIcon, PlusIcon, XIcon } from "@phosphor-icons/react";
|
|
32
|
+
import { defaultRangeExtractor, useVirtualizer } from "@tanstack/react-virtual";
|
|
18
33
|
//#region src/inputs/SelectInput.tsx
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
34
|
+
/**
|
|
35
|
+
* The form control for choosing one value from a list of options. A field shows the chosen option
|
|
36
|
+
* (or a placeholder) and opens a listbox panel below it, where the user moves through the options
|
|
37
|
+
* with the arrow keys or the pointer, optionally narrows them by typing a search term, and picks
|
|
38
|
+
* one. The value is the option's `id`, a string, so it serializes into forms and URLs.
|
|
39
|
+
*
|
|
40
|
+
* The panel is a `Popover` (`role` `LISTBOX`, `MANUAL`: opened only by the field). The field is a
|
|
41
|
+
* WAI-ARIA combobox with `aria-activedescendant`: DOM focus stays on it the whole time. Rows follow
|
|
42
|
+
* Menu's anatomy (rungs, highlight, check mark, group headings, filter and typeahead).
|
|
43
|
+
* Options can carry a description, an icon or leading node, a group heading and a disabled
|
|
44
|
+
* reason; some can be pinned to the top. Lists over 100 rows are virtualized.
|
|
45
|
+
*
|
|
46
|
+
* `SelectInputRoot` is the `@internal` engine that `MultiSelectInput` shares. Neither validates
|
|
47
|
+
* (the app passes `error`) nor fetches (the app answers `onSearch` and `onEndReached`).
|
|
48
|
+
*
|
|
49
|
+
* SSR: closed by default, so the server renders the field only. Nothing here touches `window` or
|
|
50
|
+
* `document` at module scope; the virtualizer, measuring and timers run in effects.
|
|
51
|
+
*/
|
|
52
|
+
/** Canonical rungs; SelectInput supports all five (the Input rung table). */
|
|
53
|
+
var SelectInputSize = /* @__PURE__ */ function(SelectInputSize) {
|
|
54
|
+
/** 20px field, caption text, 12px icons, 10px caret; 24px rows, 12px row icons. */
|
|
55
|
+
SelectInputSize["X_SMALL"] = "X_SMALL";
|
|
56
|
+
/** 24px field, body_sm text, 12px icons, 10px caret; 24px rows, 12px row icons. */
|
|
57
|
+
SelectInputSize["SMALL"] = "SMALL";
|
|
58
|
+
/** 32px field, body_md text, 14px icons, 12px caret; 32px rows, 14px row icons. The default (1.x `MEDIUM` was 28px). */
|
|
59
|
+
SelectInputSize["MEDIUM"] = "MEDIUM";
|
|
60
|
+
/** 36px field, body_lg text, 14px icons, 12px caret; 32px rows, 14px row icons. */
|
|
61
|
+
SelectInputSize["LARGE"] = "LARGE";
|
|
62
|
+
/** 40px field, body_lg text, 16px icons, 12px caret; 32px rows, 14px row icons. */
|
|
63
|
+
SelectInputSize["X_LARGE"] = "X_LARGE";
|
|
64
|
+
return SelectInputSize;
|
|
65
|
+
}({});
|
|
66
|
+
/** Resting fill of the field (Constitution V-A surface set; 1.x `InputVariant` unchanged). */
|
|
67
|
+
var SelectInputVariant = /* @__PURE__ */ function(SelectInputVariant) {
|
|
68
|
+
/** `background.base` fill. */
|
|
69
|
+
SelectInputVariant["BASE"] = "BASE";
|
|
70
|
+
/** `background.primary` fill. The default. */
|
|
71
|
+
SelectInputVariant["PRIMARY"] = "PRIMARY";
|
|
72
|
+
/** `background.secondary` fill. */
|
|
73
|
+
SelectInputVariant["SECONDARY"] = "SECONDARY";
|
|
74
|
+
/** `background.tertiary` fill. */
|
|
75
|
+
SelectInputVariant["TERTIARY"] = "TERTIARY";
|
|
76
|
+
return SelectInputVariant;
|
|
77
|
+
}({});
|
|
78
|
+
/** The field's width without `fillWidth` (no width token exists; the `MENU_MAX_WIDTH` precedent). */
|
|
79
|
+
/** The panel's widest list (Menu's maximum) and tallest list. */
|
|
80
|
+
/** The panel is `border-box` with a 0.5px hairline top and bottom (Menu's compensation). */
|
|
81
|
+
/** Pause after typing before `onSearch` receives the term. */
|
|
82
|
+
var SELECT_SEARCH_DEBOUNCE_MS = 150;
|
|
83
|
+
/** Pause after which the typeahead prefix starts over. */
|
|
84
|
+
var SELECT_TYPEAHEAD_RESET_MS = 500;
|
|
85
|
+
/** Above this many rows the list renders through the virtualizer. */
|
|
86
|
+
var SELECT_VIRTUALIZE_THRESHOLD = 100;
|
|
87
|
+
/** Rows moved by PageDown / PageUp. */
|
|
88
|
+
var SELECT_PAGE_STEP = 10;
|
|
89
|
+
/** Rows rendered beyond the visible ones when virtualized. */
|
|
90
|
+
var SELECT_VIRTUAL_OVERSCAN = 8;
|
|
91
|
+
/** The list's block inset (4px less the panel hairline), for the virtualizer's start and end padding. */
|
|
92
|
+
var SELECT_LIST_INSET_PX = Number.parseFloat(SPACE[4]) - .5;
|
|
93
|
+
/** The field caret: 12px, 10px on the 20px and 24px rungs (the collapsible Widget's caret). */
|
|
94
|
+
var SELECT_INPUT_SIZE_TO_CARET_PX_MAP = {
|
|
95
|
+
["X_SMALL"]: 10,
|
|
96
|
+
["SMALL"]: 10,
|
|
97
|
+
["MEDIUM"]: 12,
|
|
98
|
+
["LARGE"]: 12,
|
|
99
|
+
["X_LARGE"]: 12
|
|
25
100
|
};
|
|
26
|
-
|
|
27
|
-
var
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if ($isActive) return theme.color.border.selected;
|
|
34
|
-
if ($isError) return theme.color.border.error;
|
|
35
|
-
return getInputBorderColor(theme, $variant);
|
|
101
|
+
/** Field icon px per rung: one step below the rung's III-A.4 icon (Mitch, 2026-10-04). */
|
|
102
|
+
var SELECT_INPUT_SIZE_TO_ICON_PX_MAP = {
|
|
103
|
+
["X_SMALL"]: 12,
|
|
104
|
+
["SMALL"]: 12,
|
|
105
|
+
["MEDIUM"]: 14,
|
|
106
|
+
["LARGE"]: 14,
|
|
107
|
+
["X_LARGE"]: 16
|
|
36
108
|
};
|
|
37
|
-
|
|
38
|
-
var
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
var
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
109
|
+
/** The clear button's rung: 20px up to MEDIUM, 24px for LARGE and X_LARGE (Input's). */
|
|
110
|
+
var SELECT_INPUT_SIZE_TO_BUTTON_SIZE_MAP = {
|
|
111
|
+
["X_SMALL"]: ButtonSize.X_SMALL,
|
|
112
|
+
["SMALL"]: ButtonSize.X_SMALL,
|
|
113
|
+
["MEDIUM"]: ButtonSize.X_SMALL,
|
|
114
|
+
["LARGE"]: ButtonSize.SMALL,
|
|
115
|
+
["X_LARGE"]: ButtonSize.SMALL
|
|
116
|
+
};
|
|
117
|
+
/** The clear button's px per rung, for the reserved padding (16 on X_SMALL: `X_SMALL_INNER_BUTTON_CSS`). */
|
|
118
|
+
/** Field text step per rung (the canonical control text). */
|
|
119
|
+
var SELECT_INPUT_SIZE_TO_TEXT_SIZE_MAP = {
|
|
120
|
+
["X_SMALL"]: TextSize.CAPTION,
|
|
121
|
+
["SMALL"]: TextSize.BODY_SM,
|
|
122
|
+
["MEDIUM"]: TextSize.BODY_MD,
|
|
123
|
+
["LARGE"]: TextSize.BODY_LG,
|
|
124
|
+
["X_LARGE"]: TextSize.BODY_LG
|
|
125
|
+
};
|
|
126
|
+
/** The `Field` a SelectInput renders for its own `label` / `error` uses the same rung. */
|
|
127
|
+
var SELECT_INPUT_SIZE_TO_FIELD_SIZE_MAP = {
|
|
128
|
+
["X_SMALL"]: FieldSize.X_SMALL,
|
|
129
|
+
["SMALL"]: FieldSize.SMALL,
|
|
130
|
+
["MEDIUM"]: FieldSize.MEDIUM,
|
|
131
|
+
["LARGE"]: FieldSize.LARGE,
|
|
132
|
+
["X_LARGE"]: FieldSize.X_LARGE
|
|
133
|
+
};
|
|
134
|
+
/** Row label step per rung (Menu's two rungs). */
|
|
135
|
+
var SELECT_INPUT_SIZE_TO_ROW_TEXT_SIZE_MAP = {
|
|
136
|
+
["X_SMALL"]: TextSize.BODY_SM,
|
|
137
|
+
["SMALL"]: TextSize.BODY_SM,
|
|
138
|
+
["MEDIUM"]: TextSize.BODY_MD,
|
|
139
|
+
["LARGE"]: TextSize.BODY_MD,
|
|
140
|
+
["X_LARGE"]: TextSize.BODY_MD
|
|
141
|
+
};
|
|
142
|
+
/** Row description and group heading step per rung. */
|
|
143
|
+
var SELECT_INPUT_SIZE_TO_ROW_SECONDARY_TEXT_SIZE_MAP = {
|
|
144
|
+
["X_SMALL"]: TextSize.CAPTION,
|
|
145
|
+
["SMALL"]: TextSize.CAPTION,
|
|
146
|
+
["MEDIUM"]: TextSize.BODY_SM,
|
|
147
|
+
["LARGE"]: TextSize.BODY_SM,
|
|
148
|
+
["X_LARGE"]: TextSize.BODY_SM
|
|
149
|
+
};
|
|
150
|
+
/** Row icon (and check mark) px per row rung (24px → 12, 32px → 14; Mitch, 2026-10-04). */
|
|
151
|
+
var SELECT_INPUT_SIZE_TO_ROW_ICON_PX_MAP = {
|
|
152
|
+
["X_SMALL"]: 12,
|
|
153
|
+
["SMALL"]: 12,
|
|
154
|
+
["MEDIUM"]: 14,
|
|
155
|
+
["LARGE"]: 14,
|
|
156
|
+
["X_LARGE"]: 14
|
|
157
|
+
};
|
|
158
|
+
/** The loading row's spinner names the row rung (24px or 32px). */
|
|
159
|
+
var SELECT_INPUT_SIZE_TO_SPINNER_SIZE_MAP = {
|
|
160
|
+
["X_SMALL"]: SpinnerSize.SMALL,
|
|
161
|
+
["SMALL"]: SpinnerSize.SMALL,
|
|
162
|
+
["MEDIUM"]: SpinnerSize.MEDIUM,
|
|
163
|
+
["LARGE"]: SpinnerSize.MEDIUM,
|
|
164
|
+
["X_LARGE"]: SpinnerSize.MEDIUM
|
|
165
|
+
};
|
|
166
|
+
/** Row height (px) per rung, the virtualizer's estimate. */
|
|
167
|
+
var SELECT_INPUT_SIZE_TO_ROW_HEIGHT_PX_MAP = {
|
|
168
|
+
["X_SMALL"]: Number.parseFloat(CONTROL_SIZE.small),
|
|
169
|
+
["SMALL"]: Number.parseFloat(CONTROL_SIZE.small),
|
|
170
|
+
["MEDIUM"]: Number.parseFloat(CONTROL_SIZE.medium),
|
|
171
|
+
["LARGE"]: Number.parseFloat(CONTROL_SIZE.medium),
|
|
172
|
+
["X_LARGE"]: Number.parseFloat(CONTROL_SIZE.medium)
|
|
173
|
+
};
|
|
174
|
+
/** A description's line height (px) per rung, added to the estimate of rows that have one. */
|
|
175
|
+
var SELECT_INPUT_SIZE_TO_SECONDARY_LINE_PX_MAP = {
|
|
176
|
+
["X_SMALL"]: Number.parseFloat(TYPE_SCALE.sans.height.caption),
|
|
177
|
+
["SMALL"]: Number.parseFloat(TYPE_SCALE.sans.height.caption),
|
|
178
|
+
["MEDIUM"]: Number.parseFloat(TYPE_SCALE.sans.height.body_sm),
|
|
179
|
+
["LARGE"]: Number.parseFloat(TYPE_SCALE.sans.height.body_sm),
|
|
180
|
+
["X_LARGE"]: Number.parseFloat(TYPE_SCALE.sans.height.body_sm)
|
|
181
|
+
};
|
|
182
|
+
/** Panel fill: 1.x's map. A ghost field (`isGhost`) gets a readable `BASE` panel instead. */
|
|
183
|
+
var SELECT_INPUT_VARIANT_TO_POPOVER_VARIANT_MAP = {
|
|
184
|
+
["BASE"]: PopoverVariant.PRIMARY,
|
|
185
|
+
["PRIMARY"]: PopoverVariant.PRIMARY,
|
|
186
|
+
["SECONDARY"]: PopoverVariant.SECONDARY,
|
|
187
|
+
["TERTIARY"]: PopoverVariant.TERTIARY
|
|
188
|
+
};
|
|
189
|
+
/** The polite live region that speaks for the status rows (VisuallyHidden takes no `role`). */
|
|
190
|
+
var StatusRegion = /*#__PURE__*/ styled("span")({
|
|
191
|
+
name: "StatusRegion",
|
|
192
|
+
class: "gx-SelectInput-StatusRegion-s1ecxi56",
|
|
193
|
+
propsAsIs: false
|
|
194
|
+
});
|
|
195
|
+
/** Holds the field box, the trailing parts and the status region; 200px unless `fillWidth`. */
|
|
196
|
+
var Wrapper = /*#__PURE__*/ styled("div")({
|
|
197
|
+
name: "Wrapper",
|
|
198
|
+
class: "gx-SelectInput-Wrapper-w14m69un",
|
|
199
|
+
propsAsIs: false
|
|
200
|
+
});
|
|
201
|
+
/**
|
|
202
|
+
* The combobox (the focused element and Popover's reference): the field box. No outline; focus is
|
|
203
|
+
* `FOCUS_CSS`. Declared before the frame maps so they win at equal specificity.
|
|
204
|
+
*/
|
|
205
|
+
var Control = /*#__PURE__*/ styled("div")({
|
|
206
|
+
name: "Control",
|
|
207
|
+
class: "gx-SelectInput-Control-cbfl8n6",
|
|
208
|
+
propsAsIs: false
|
|
84
209
|
});
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
210
|
+
/** The searchable combobox: the same box as an `<input>`. Declared before the frame maps. */
|
|
211
|
+
var ControlInput = /*#__PURE__*/ styled("input")({
|
|
212
|
+
name: "ControlInput",
|
|
213
|
+
class: "gx-SelectInput-ControlInput-ck260f0",
|
|
88
214
|
propsAsIs: false
|
|
89
215
|
});
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
216
|
+
/** Rest look per variant: fill and hairline color. */
|
|
217
|
+
var SELECT_INPUT_VARIANT_TO_CSS_MAP = {
|
|
218
|
+
["BASE"]: "gx-SelectInput-BASE-bo10ras",
|
|
219
|
+
["PRIMARY"]: "gx-SelectInput-PRIMARY-poe2ind",
|
|
220
|
+
["SECONDARY"]: "gx-SelectInput-SECONDARY-s9236d8",
|
|
221
|
+
["TERTIARY"]: "gx-SelectInput-TERTIARY-t5jqpvy"
|
|
93
222
|
};
|
|
94
|
-
|
|
95
|
-
var
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if ($isSelected || $isFocused) return theme.color.background.tertiary;
|
|
106
|
-
return "transparent";
|
|
223
|
+
/** `isGhost`: no fill and no hairline at rest. Declared after the variant map, which it replaces. */
|
|
224
|
+
var GHOST_CSS = "gx-SelectInput-GHOST_CSS-g1yocujd";
|
|
225
|
+
/**
|
|
226
|
+
* Hover on an editable field: `border.hovered` (stronger than `border.primary` in both themes,
|
|
227
|
+
* Constitution III.6).
|
|
228
|
+
*/
|
|
229
|
+
var SELECT_INPUT_VARIANT_TO_HOVER_CSS_MAP = {
|
|
230
|
+
["BASE"]: "gx-SelectInput-BASE-b1uexxly",
|
|
231
|
+
["PRIMARY"]: "gx-SelectInput-PRIMARY-pazywje",
|
|
232
|
+
["SECONDARY"]: "gx-SelectInput-SECONDARY-s18jekpe",
|
|
233
|
+
["TERTIARY"]: "gx-SelectInput-TERTIARY-t1gos90x"
|
|
107
234
|
};
|
|
108
|
-
|
|
109
|
-
var
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
235
|
+
/** Hover on an editable ghost field: the frame appears, `border.hovered` with no fill. */
|
|
236
|
+
var GHOST_HOVER_CSS = "gx-SelectInput-GHOST_HOVER_CSS-g1oazqgn";
|
|
237
|
+
/**
|
|
238
|
+
* Height, start inset, gap and the end inset that reserves the caret zone (rung padding + caret +
|
|
239
|
+
* gap) per rung. Off-scale insets are sums of `space` tokens and the rung's caret px. The start inset
|
|
240
|
+
* subtracts the 0.5px hairline so the value starts on a whole pixel, the rung padding from the edge.
|
|
241
|
+
*/
|
|
242
|
+
var SELECT_INPUT_SIZE_TO_CSS_MAP = {
|
|
243
|
+
["X_SMALL"]: "gx-SelectInput-X_SMALL-xhmj664",
|
|
244
|
+
["SMALL"]: "gx-SelectInput-SMALL-skndlgg",
|
|
245
|
+
["MEDIUM"]: "gx-SelectInput-MEDIUM-m1v59wlg",
|
|
246
|
+
["LARGE"]: "gx-SelectInput-LARGE-lkrrva3",
|
|
247
|
+
["X_LARGE"]: "gx-SelectInput-X_LARGE-x1js26tz"
|
|
248
|
+
};
|
|
249
|
+
/** With `isClearable`: the end inset also reserves the clear button and a gap. */
|
|
250
|
+
var SELECT_INPUT_SIZE_TO_CLEARABLE_CSS_MAP = {
|
|
251
|
+
["X_SMALL"]: "gx-SelectInput-X_SMALL-x1y9j7k3",
|
|
252
|
+
["SMALL"]: "gx-SelectInput-SMALL-s14dj8fy",
|
|
253
|
+
["MEDIUM"]: "gx-SelectInput-MEDIUM-mxjxwo8",
|
|
254
|
+
["LARGE"]: "gx-SelectInput-LARGE-lk7nfq8",
|
|
255
|
+
["X_LARGE"]: "gx-SelectInput-X_LARGE-x1vo3rqs"
|
|
256
|
+
};
|
|
257
|
+
/** The typed term's type per rung (searchable field), so it matches the value text. */
|
|
258
|
+
var SELECT_INPUT_SIZE_TO_FONT_CSS_MAP = {
|
|
259
|
+
["X_SMALL"]: "gx-SelectInput-X_SMALL-x1jz5yi4",
|
|
260
|
+
["SMALL"]: "gx-SelectInput-SMALL-s1ysyxtm",
|
|
261
|
+
["MEDIUM"]: "gx-SelectInput-MEDIUM-ms13kdi",
|
|
262
|
+
["LARGE"]: "gx-SelectInput-LARGE-llyskmg",
|
|
263
|
+
["X_LARGE"]: "gx-SelectInput-X_LARGE-xcjuv87"
|
|
264
|
+
};
|
|
265
|
+
/**
|
|
266
|
+
* Focus, from the mouse or the keyboard: `FIELD_FOCUS`, `border.focused` at 1px inside the frame. `:focus:hover`
|
|
267
|
+
* beats the hover map, so a ghost field keeps it. Not applied while disabled (the disabled
|
|
268
|
+
* combobox is a `tabIndex={-1}` div, which a click can still focus).
|
|
269
|
+
*/
|
|
270
|
+
var FOCUS_CSS = "gx-SelectInput-FOCUS_CSS-fqmfw76";
|
|
271
|
+
/** Open: `border.focused` (1.x's "selected" border). */
|
|
272
|
+
var OPEN_CSS = "gx-SelectInput-OPEN_CSS-o15ps1x0";
|
|
273
|
+
/** Error border at rest, on hover, with focus and while open (the 1px inset focus line is red too). */
|
|
274
|
+
var ERROR_CSS = "gx-SelectInput-ERROR_CSS-e1mxvlaa";
|
|
275
|
+
/**
|
|
276
|
+
* Disabled: the variant's resting fill and hairline are kept (`border.disabled` is not used on
|
|
277
|
+
* frames); the field reads as disabled through `text.disabled` / `text.disabled` and the cursor.
|
|
278
|
+
*/
|
|
279
|
+
var DISABLED_CSS = "gx-SelectInput-DISABLED_CSS-due2xpd";
|
|
280
|
+
/** Read-only: no pointer affordance. */
|
|
281
|
+
var READ_ONLY_CSS = "gx-SelectInput-READ_ONLY_CSS-rz60qcy";
|
|
282
|
+
/** The searchable input while it is open: a text cursor. */
|
|
283
|
+
var TYPING_CSS = "gx-SelectInput-TYPING_CSS-t1kwlo75";
|
|
284
|
+
/** The searchable input while closed with a selection: the overlay shows the value. */
|
|
285
|
+
var VALUE_OVERLAID_CSS = "gx-SelectInput-VALUE_OVERLAID_CSS-v7qbedr";
|
|
286
|
+
/** Stretch to the parent's width (wrapper, and the `Field` wrapper when there is one). */
|
|
287
|
+
var FILL_WIDTH_CSS = "gx-SelectInput-FILL_WIDTH_CSS-f14e5rra";
|
|
288
|
+
/** The value inside the field: leading, icon and label in a row; only the label shrinks. */
|
|
289
|
+
var ValueDisplay = /*#__PURE__*/ styled("span")({
|
|
290
|
+
name: "ValueDisplay",
|
|
291
|
+
class: "gx-SelectInput-ValueDisplay-vcoak0",
|
|
121
292
|
propsAsIs: false
|
|
122
293
|
});
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
[
|
|
126
|
-
[
|
|
127
|
-
[
|
|
128
|
-
[
|
|
294
|
+
/** Gap between the value's leading, icon and label per rung. */
|
|
295
|
+
var SELECT_INPUT_SIZE_TO_VALUE_CSS_MAP = {
|
|
296
|
+
["X_SMALL"]: "gx-SelectInput-X_SMALL-xo0k8iv",
|
|
297
|
+
["SMALL"]: "gx-SelectInput-SMALL-s10dru8j",
|
|
298
|
+
["MEDIUM"]: "gx-SelectInput-MEDIUM-mxd48k3",
|
|
299
|
+
["LARGE"]: "gx-SelectInput-LARGE-l1uv1y8i",
|
|
300
|
+
["X_LARGE"]: "gx-SelectInput-X_LARGE-x4kepfq"
|
|
129
301
|
};
|
|
302
|
+
/** Holds `renderSelection`'s output (or the placeholder): fills the field and may shrink. */
|
|
303
|
+
var SelectionSlot = /*#__PURE__*/ styled("span")({
|
|
304
|
+
name: "SelectionSlot",
|
|
305
|
+
class: "gx-SelectInput-SelectionSlot-s19x5eff",
|
|
306
|
+
propsAsIs: false
|
|
307
|
+
});
|
|
130
308
|
/**
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
309
|
+
* The searchable field's value while closed: laid over the input with the field's own insets (a
|
|
310
|
+
* transparent hairline included), so it sits exactly where the `div` field draws its value.
|
|
311
|
+
*/
|
|
312
|
+
var ValueOverlay = /*#__PURE__*/ styled("span")({
|
|
313
|
+
name: "ValueOverlay",
|
|
314
|
+
class: "gx-SelectInput-ValueOverlay-v1igascl",
|
|
315
|
+
propsAsIs: false
|
|
316
|
+
});
|
|
317
|
+
/** The overlay's insets per rung (the field's). */
|
|
318
|
+
var SELECT_INPUT_SIZE_TO_OVERLAY_CSS_MAP = {
|
|
319
|
+
["X_SMALL"]: "gx-SelectInput-X_SMALL-xj42cy5",
|
|
320
|
+
["SMALL"]: "gx-SelectInput-SMALL-sjwzds1",
|
|
321
|
+
["MEDIUM"]: "gx-SelectInput-MEDIUM-mzzsir9",
|
|
322
|
+
["LARGE"]: "gx-SelectInput-LARGE-l15l4j2i",
|
|
323
|
+
["X_LARGE"]: "gx-SelectInput-X_LARGE-x1ohc03t"
|
|
324
|
+
};
|
|
325
|
+
/** The caret and the clear button: laid over the field's end, outside the combobox. */
|
|
326
|
+
var Trailing = /*#__PURE__*/ styled("span")({
|
|
327
|
+
name: "Trailing",
|
|
328
|
+
class: "gx-SelectInput-Trailing-ta9xiv4",
|
|
329
|
+
propsAsIs: false
|
|
330
|
+
});
|
|
331
|
+
/** Trailing inset (the rung padding) and gap per rung. */
|
|
332
|
+
var SELECT_INPUT_SIZE_TO_TRAILING_CSS_MAP = {
|
|
333
|
+
["X_SMALL"]: "gx-SelectInput-X_SMALL-x1p8ykbf",
|
|
334
|
+
["SMALL"]: "gx-SelectInput-SMALL-s1evykgc",
|
|
335
|
+
["MEDIUM"]: "gx-SelectInput-MEDIUM-m15bsqie",
|
|
336
|
+
["LARGE"]: "gx-SelectInput-LARGE-liqey9w",
|
|
337
|
+
["X_LARGE"]: "gx-SelectInput-X_LARGE-xhe3qvy"
|
|
338
|
+
};
|
|
339
|
+
/** Wraps the clear button: the only part of `Trailing` that takes the pointer. */
|
|
340
|
+
var ClearSlot = /*#__PURE__*/ styled("span")({
|
|
341
|
+
name: "ClearSlot",
|
|
342
|
+
class: "gx-SelectInput-ClearSlot-c18c84l6",
|
|
343
|
+
propsAsIs: false
|
|
344
|
+
});
|
|
345
|
+
/** Hidden clear button: keeps its width, leaves the tab order and the accessibility tree. */
|
|
346
|
+
var CLEAR_HIDDEN_CSS = "gx-SelectInput-CLEAR_HIDDEN_CSS-c1da7pn7";
|
|
347
|
+
/**
|
|
348
|
+
* The panel content: between the field's width and 320px wide, capped to 320px tall. The
|
|
349
|
+
* available height falls back to the viewport until Popover has measured it, so the list is
|
|
350
|
+
* capped from its first frame (the active row is scrolled to on that frame).
|
|
136
351
|
*/
|
|
137
|
-
var
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
352
|
+
var ListFrame = /*#__PURE__*/ styled("div")({
|
|
353
|
+
name: "ListFrame",
|
|
354
|
+
class: "gx-SelectInput-ListFrame-lzozmwc",
|
|
355
|
+
propsAsIs: false
|
|
356
|
+
});
|
|
357
|
+
/** A virtualized panel is exactly as wide as the field (absolute rows have no content width). */
|
|
358
|
+
var VIRTUALIZED_FRAME_CSS = "gx-SelectInput-VIRTUALIZED_FRAME_CSS-vz9a0uz";
|
|
359
|
+
/** The rows, 4px in from the panel's outer edge (the 0.5px hairline counts toward the inset). */
|
|
360
|
+
var List = /*#__PURE__*/ styled("div")({
|
|
361
|
+
name: "List",
|
|
362
|
+
class: "gx-SelectInput-List-l1pxtftz",
|
|
363
|
+
propsAsIs: false
|
|
364
|
+
});
|
|
365
|
+
/** Virtualized: the inset is the virtualizer's start / end padding, so only the sides keep it. */
|
|
366
|
+
var VIRTUAL_LIST_CSS = "gx-SelectInput-VIRTUAL_LIST_CSS-v1pcykbv";
|
|
367
|
+
/** Virtualized: the full list height; rows are positioned inside it. */
|
|
368
|
+
var VirtualBody = /*#__PURE__*/ styled("div")({
|
|
369
|
+
name: "VirtualBody",
|
|
370
|
+
class: "gx-SelectInput-VirtualBody-v173z9ja",
|
|
371
|
+
propsAsIs: false
|
|
372
|
+
});
|
|
373
|
+
/** Virtualized row: absolutely placed at its measured offset. */
|
|
374
|
+
var VIRTUAL_ROW_CSS = "gx-SelectInput-VIRTUAL_ROW_CSS-vul1txn";
|
|
375
|
+
/** An option row (Menu's row restated). Declared before the row maps. */
|
|
376
|
+
var OptionRow = /*#__PURE__*/ styled("div")({
|
|
377
|
+
name: "OptionRow",
|
|
378
|
+
class: "gx-SelectInput-OptionRow-o1aponwc",
|
|
379
|
+
propsAsIs: false
|
|
380
|
+
});
|
|
381
|
+
/** A status row (empty, loading): a row's box, not interactive. */
|
|
382
|
+
var StatusRow = /*#__PURE__*/ styled("div")({
|
|
383
|
+
name: "StatusRow",
|
|
384
|
+
class: "gx-SelectInput-StatusRow-sxgsv7z",
|
|
385
|
+
propsAsIs: false
|
|
386
|
+
});
|
|
387
|
+
/** Row height, inline and block padding, and gap per rung (Menu's two rungs). */
|
|
388
|
+
var SELECT_INPUT_SIZE_TO_ROW_CSS_MAP = {
|
|
389
|
+
["X_SMALL"]: "gx-SelectInput-X_SMALL-x1n6kh9n",
|
|
390
|
+
["SMALL"]: "gx-SelectInput-SMALL-sxrvm1c",
|
|
391
|
+
["MEDIUM"]: "gx-SelectInput-MEDIUM-mw9xd7i",
|
|
392
|
+
["LARGE"]: "gx-SelectInput-LARGE-lgrwy9f",
|
|
393
|
+
["X_LARGE"]: "gx-SelectInput-X_LARGE-xlfzaiz"
|
|
394
|
+
};
|
|
395
|
+
/** The active row: the only highlight, so pointer and keyboard never show two. */
|
|
396
|
+
var ACTIVE_ROW_CSS = "gx-SelectInput-ACTIVE_ROW_CSS-a1r6mtzd";
|
|
397
|
+
/** Pressing an enabled active row. */
|
|
398
|
+
var ACTIVE_ROW_PRESS_CSS = "gx-SelectInput-ACTIVE_ROW_PRESS_CSS-a1lc3k5v";
|
|
399
|
+
/** A disabled row. */
|
|
400
|
+
var ROW_DISABLED_CSS = "gx-SelectInput-ROW_DISABLED_CSS-rfsa9g";
|
|
401
|
+
/** `leading`: never squeezed, so only the text shrinks. */
|
|
402
|
+
var Slot = /*#__PURE__*/ styled("span")({
|
|
403
|
+
name: "Slot",
|
|
404
|
+
class: "gx-SelectInput-Slot-s1vsgpgc",
|
|
405
|
+
propsAsIs: false
|
|
406
|
+
});
|
|
407
|
+
/** Label and description, stacked; the only part of a row that shrinks. */
|
|
408
|
+
var Content = /*#__PURE__*/ styled("span")({
|
|
409
|
+
name: "Content",
|
|
410
|
+
class: "gx-SelectInput-Content-c67enmk",
|
|
411
|
+
propsAsIs: false
|
|
412
|
+
});
|
|
413
|
+
/** The check mark's box (the icon's width), so rows align selected or not. */
|
|
414
|
+
var CheckSlot = /*#__PURE__*/ styled("span")({
|
|
415
|
+
name: "CheckSlot",
|
|
416
|
+
class: "gx-SelectInput-CheckSlot-c12tkpdx",
|
|
417
|
+
propsAsIs: false
|
|
418
|
+
});
|
|
419
|
+
/** The check mark's reserved width per rung: the row icon px. */
|
|
420
|
+
var SELECT_INPUT_SIZE_TO_CHECK_SLOT_CSS_MAP = {
|
|
421
|
+
["X_SMALL"]: "gx-SelectInput-X_SMALL-x1p1u54p",
|
|
422
|
+
["SMALL"]: "gx-SelectInput-SMALL-s1xnxona",
|
|
423
|
+
["MEDIUM"]: "gx-SelectInput-MEDIUM-m740si8",
|
|
424
|
+
["LARGE"]: "gx-SelectInput-LARGE-lo64a8t",
|
|
425
|
+
["X_LARGE"]: "gx-SelectInput-X_LARGE-xk487n1"
|
|
426
|
+
};
|
|
427
|
+
/** Icons never shrink. */
|
|
428
|
+
var NO_SHRINK_CSS = "gx-SelectInput-NO_SHRINK_CSS-nkvnet2";
|
|
429
|
+
/** A labelled section of rows (unvirtualized). */
|
|
430
|
+
var GroupRoot = /*#__PURE__*/ styled("div")({
|
|
431
|
+
name: "GroupRoot",
|
|
432
|
+
class: "gx-SelectInput-GroupRoot-g1qlz5rz",
|
|
433
|
+
propsAsIs: false
|
|
434
|
+
});
|
|
435
|
+
/** A section heading. */
|
|
436
|
+
var GroupLabel = /*#__PURE__*/ styled("div")({
|
|
437
|
+
name: "GroupLabel",
|
|
438
|
+
class: "gx-SelectInput-GroupLabel-g1mhh2uu",
|
|
439
|
+
propsAsIs: false
|
|
440
|
+
});
|
|
441
|
+
/** Group heading height and inset per rung (Menu's). */
|
|
442
|
+
var SELECT_INPUT_SIZE_TO_GROUP_LABEL_CSS_MAP = {
|
|
443
|
+
["X_SMALL"]: "gx-SelectInput-X_SMALL-x19rqrei",
|
|
444
|
+
["SMALL"]: "gx-SelectInput-SMALL-sx1x7i6",
|
|
445
|
+
["MEDIUM"]: "gx-SelectInput-MEDIUM-m1oifut7",
|
|
446
|
+
["LARGE"]: "gx-SelectInput-LARGE-lt7vyaq",
|
|
447
|
+
["X_LARGE"]: "gx-SelectInput-X_LARGE-x1firy3m"
|
|
448
|
+
};
|
|
449
|
+
/** Space above and below the pinned section's separator (Menu's). */
|
|
450
|
+
var SEPARATOR_CSS = "gx-SelectInput-SEPARATOR_CSS-s9yly5q";
|
|
451
|
+
/** Lower-cased, trimmed term (the filter's comparison form; Menu's rule). */
|
|
452
|
+
function normalizeTerm(term) {
|
|
453
|
+
return term.trim().toLocaleLowerCase();
|
|
454
|
+
}
|
|
455
|
+
/** An option matches when the term is empty or its label (or description) contains it. */
|
|
456
|
+
function matches(term, option) {
|
|
457
|
+
if (term === "") return true;
|
|
458
|
+
if (option.label.toLocaleLowerCase().includes(term)) return true;
|
|
459
|
+
return option.description?.toLocaleLowerCase().includes(term) ?? false;
|
|
460
|
+
}
|
|
461
|
+
/** A printable character that should drive typeahead or the search term. */
|
|
462
|
+
function isPrintableKey(event) {
|
|
463
|
+
return event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey;
|
|
464
|
+
}
|
|
465
|
+
/** Builds the rows in order: pinned, separator, sections, Create, then the status row. */
|
|
466
|
+
function buildModel({ options, pinnedIds, term, isFiltering, selected, limitReason, createLabel, isLoading, listboxId }) {
|
|
467
|
+
const indexById = /* @__PURE__ */ new Map();
|
|
468
|
+
options.forEach((option, index) => {
|
|
469
|
+
if (!indexById.has(option.id)) indexById.set(option.id, index);
|
|
470
|
+
});
|
|
471
|
+
let position = 0;
|
|
472
|
+
const makeItem = (option, index) => {
|
|
473
|
+
const isSelected = selected.has(option.id);
|
|
474
|
+
const isOwnDisabled = option.isDisabled === true;
|
|
475
|
+
const isLimitDisabled = limitReason !== void 0 && !isSelected;
|
|
476
|
+
position += 1;
|
|
477
|
+
return {
|
|
478
|
+
kind: "option",
|
|
479
|
+
key: `option-${index}`,
|
|
480
|
+
option,
|
|
481
|
+
domId: `${listboxId}-${index}`,
|
|
482
|
+
navIndex: -1,
|
|
483
|
+
flatIndex: -1,
|
|
484
|
+
position,
|
|
485
|
+
isSelected,
|
|
486
|
+
isDisabled: isOwnDisabled || isLimitDisabled,
|
|
487
|
+
disabledReason: isOwnDisabled ? option.disabledReason : limitReason
|
|
488
|
+
};
|
|
489
|
+
};
|
|
490
|
+
const pinnedSet = /* @__PURE__ */ new Set();
|
|
491
|
+
const pinned = [];
|
|
492
|
+
for (const id of pinnedIds ?? []) {
|
|
493
|
+
const index = indexById.get(id);
|
|
494
|
+
if (index === void 0 || pinnedSet.has(id)) continue;
|
|
495
|
+
const option = options[index];
|
|
496
|
+
if (option === void 0) continue;
|
|
497
|
+
pinnedSet.add(id);
|
|
498
|
+
pinned.push(makeItem(option, index));
|
|
499
|
+
}
|
|
500
|
+
const sectionMap = /* @__PURE__ */ new Map();
|
|
501
|
+
const sections = [];
|
|
502
|
+
options.forEach((option, index) => {
|
|
503
|
+
if (pinnedSet.has(option.id) || indexById.get(option.id) !== index) return;
|
|
504
|
+
if (isFiltering && !matches(term, option)) return;
|
|
505
|
+
const sectionKey = option.group === void 0 ? "" : `group:${option.group}`;
|
|
506
|
+
let section = sectionMap.get(sectionKey);
|
|
507
|
+
if (section === void 0) {
|
|
508
|
+
const headingId = `${listboxId}-group-${sections.length}`;
|
|
509
|
+
section = {
|
|
510
|
+
key: sectionKey === "" ? "ungrouped" : sectionKey,
|
|
511
|
+
heading: option.group === void 0 ? null : {
|
|
512
|
+
kind: "heading",
|
|
513
|
+
key: `heading-${sections.length}`,
|
|
514
|
+
id: headingId,
|
|
515
|
+
label: option.group
|
|
516
|
+
},
|
|
517
|
+
rows: []
|
|
518
|
+
};
|
|
519
|
+
sectionMap.set(sectionKey, section);
|
|
520
|
+
sections.push(section);
|
|
521
|
+
}
|
|
522
|
+
section.rows.push(makeItem(option, index));
|
|
523
|
+
});
|
|
524
|
+
const restCount = sections.reduce((count, section) => count + section.rows.length, 0);
|
|
525
|
+
const hasSeparator = pinned.length > 0 && restCount > 0;
|
|
526
|
+
const create = createLabel === null ? null : {
|
|
527
|
+
kind: "create",
|
|
528
|
+
key: "create",
|
|
529
|
+
domId: `${listboxId}-create`,
|
|
530
|
+
navIndex: -1,
|
|
531
|
+
flatIndex: -1,
|
|
532
|
+
label: createLabel
|
|
533
|
+
};
|
|
534
|
+
const nav = [];
|
|
535
|
+
const flat = [];
|
|
536
|
+
const pushNav = (item) => {
|
|
537
|
+
item.navIndex = nav.length;
|
|
538
|
+
item.flatIndex = flat.length;
|
|
539
|
+
nav.push(item);
|
|
540
|
+
flat.push(item);
|
|
541
|
+
};
|
|
542
|
+
for (const item of pinned) pushNav(item);
|
|
543
|
+
if (hasSeparator) flat.push({
|
|
544
|
+
kind: "separator",
|
|
545
|
+
key: "separator"
|
|
546
|
+
});
|
|
547
|
+
for (const section of sections) {
|
|
548
|
+
if (section.heading !== null) flat.push(section.heading);
|
|
549
|
+
for (const item of section.rows) pushNav(item);
|
|
550
|
+
}
|
|
551
|
+
if (create !== null) pushNav(create);
|
|
552
|
+
const status = isLoading ? "loading" : nav.length === 0 ? "empty" : null;
|
|
553
|
+
if (status !== null) flat.push({
|
|
554
|
+
kind: status,
|
|
555
|
+
key: status
|
|
556
|
+
});
|
|
557
|
+
return {
|
|
558
|
+
pinned,
|
|
559
|
+
hasSeparator,
|
|
560
|
+
sections,
|
|
561
|
+
create,
|
|
562
|
+
status,
|
|
563
|
+
nav,
|
|
564
|
+
flat,
|
|
565
|
+
optionCount: position
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
/** The first enabled row, else the first row (or -1). */
|
|
569
|
+
function firstEnabledIndex(nav) {
|
|
570
|
+
const index = nav.findIndex((item) => item.kind === "create" || !item.isDisabled);
|
|
571
|
+
return index === -1 ? nav.length > 0 ? 0 : -1 : index;
|
|
572
|
+
}
|
|
573
|
+
/** The first selected option row, or -1. */
|
|
574
|
+
function selectedIndex(nav) {
|
|
575
|
+
return nav.findIndex((item) => item.kind === "option" && item.isSelected);
|
|
576
|
+
}
|
|
577
|
+
/** Resolves an open target to a row index. */
|
|
578
|
+
function openIndex(nav, target) {
|
|
579
|
+
if (nav.length === 0) return -1;
|
|
580
|
+
if (target === "first") return 0;
|
|
581
|
+
if (target === "last") return nav.length - 1;
|
|
582
|
+
const selected = selectedIndex(nav);
|
|
583
|
+
if (selected !== -1) return selected;
|
|
584
|
+
return target === "selected-or-first" ? firstEnabledIndex(nav) : nav.length - 1;
|
|
585
|
+
}
|
|
586
|
+
/** The field, the trailing parts, the panel and the engine, wired to the nearest Field. */
|
|
587
|
+
function SelectInputFrame({ rootProps, forwardedRef, isRoot }) {
|
|
588
|
+
const { options, selectedIds, isMultiple, onOptionSelect, onClear, renderSelection, getSelectionText, limitReason, onCreate, size = "MEDIUM", variant = "PRIMARY", isGhost = false, label: _label, placeholder = "Select an option…", error, isError = false, isDisabled = false, isReadOnly = false, isRequired = false, fillWidth = false, ariaLabel, isOpen, defaultIsOpen = false, onOpenChange, isSearchable = false, onSearch, isClearable = false, isLoading = false, emptyMessage = "No results", onEndReached, renderOption, pinnedIds, placement = Placement.BOTTOM_START, id, "aria-describedby": ariaDescribedBy, className, style, ...rest } = rootProps;
|
|
589
|
+
const isBare = useIsBareField();
|
|
590
|
+
const { field, controlId, describedBy, isInvalid, isRequired: isRequiredState, hasName } = useFieldWiring({
|
|
591
|
+
id,
|
|
592
|
+
ariaLabel,
|
|
593
|
+
ariaLabelAttribute: rest["aria-label"],
|
|
594
|
+
ariaLabelledBy: rest["aria-labelledby"],
|
|
595
|
+
ariaDescribedBy,
|
|
596
|
+
error,
|
|
597
|
+
isError,
|
|
598
|
+
isRequired
|
|
599
|
+
});
|
|
600
|
+
const listboxId = `${controlId}-listbox`;
|
|
601
|
+
const controlRef = useRef(null);
|
|
602
|
+
const mergedRef = useMergeRefs([controlRef, forwardedRef]);
|
|
603
|
+
const panelRef = useRef(null);
|
|
604
|
+
const wrapperRef = useRef(null);
|
|
605
|
+
const [viewportElement, setViewportElement] = useState(null);
|
|
606
|
+
const isEditable = !isDisabled && !isReadOnly;
|
|
607
|
+
const { isOpen: isOpenState, setOpen: setOpenState } = useControlledOpenState({
|
|
608
|
+
isOpen,
|
|
609
|
+
defaultIsOpen,
|
|
147
610
|
onOpenChange
|
|
148
611
|
});
|
|
149
|
-
const
|
|
150
|
-
const
|
|
151
|
-
const
|
|
152
|
-
const
|
|
153
|
-
const
|
|
154
|
-
const
|
|
612
|
+
const isShown = isOpenState && isEditable;
|
|
613
|
+
const { setOpen } = useDedupedOpen(isShown, setOpenState);
|
|
614
|
+
const [rawTerm, setRawTerm] = useState("");
|
|
615
|
+
const [hasSearched, setHasSearched] = useState(false);
|
|
616
|
+
const [isReasonDismissed, setIsReasonDismissed] = useState(false);
|
|
617
|
+
const cacheRef = useRef(/* @__PURE__ */ new Map());
|
|
618
|
+
const selectedSet = new Set(selectedIds);
|
|
619
|
+
for (const option of options) if (selectedSet.has(option.id)) cacheRef.current.set(option.id, option);
|
|
620
|
+
const warnedIdsRef = useRef(/* @__PURE__ */ new Set());
|
|
621
|
+
const unknownIds = [];
|
|
622
|
+
const selectedOptions = selectedIds.map((selectedId) => {
|
|
623
|
+
const known = options.find((option) => option.id === selectedId) ?? cacheRef.current.get(selectedId);
|
|
624
|
+
if (known !== void 0) return known;
|
|
625
|
+
unknownIds.push(selectedId);
|
|
626
|
+
return {
|
|
627
|
+
id: selectedId,
|
|
628
|
+
label: selectedId
|
|
629
|
+
};
|
|
630
|
+
});
|
|
631
|
+
const unknownKey = unknownIds.join("\n");
|
|
155
632
|
useEffect(() => {
|
|
156
|
-
if (
|
|
157
|
-
|
|
158
|
-
|
|
633
|
+
if (unknownKey === "" || !isDevelopment()) return;
|
|
634
|
+
for (const unknownId of unknownKey.split("\n")) {
|
|
635
|
+
if (warnedIdsRef.current.has(unknownId)) continue;
|
|
636
|
+
warnedIdsRef.current.add(unknownId);
|
|
637
|
+
console.warn(`[@galaxy-io/dls] SelectInput value "${unknownId}" is not in \`options\`; the raw id is shown as its label.`);
|
|
159
638
|
}
|
|
160
|
-
}, [
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
639
|
+
}, [unknownKey]);
|
|
640
|
+
const selectionText = selectedOptions.length > 0 ? getSelectionText(selectedOptions) : "";
|
|
641
|
+
const term = isSearchable ? normalizeTerm(rawTerm) : "";
|
|
642
|
+
const trimmedTerm = rawTerm.trim();
|
|
643
|
+
const isFiltering = isSearchable && onSearch === void 0;
|
|
644
|
+
const hasExactMatch = trimmedTerm !== "" && options.some((option) => option.label.toLocaleLowerCase() === trimmedTerm.toLocaleLowerCase());
|
|
645
|
+
const model = buildModel({
|
|
646
|
+
options,
|
|
647
|
+
pinnedIds,
|
|
648
|
+
term,
|
|
649
|
+
isFiltering,
|
|
650
|
+
selected: selectedSet,
|
|
651
|
+
limitReason,
|
|
652
|
+
createLabel: onCreate !== void 0 && isSearchable && trimmedTerm !== "" && !hasExactMatch && limitReason === void 0 ? trimmedTerm : null,
|
|
653
|
+
isLoading,
|
|
654
|
+
listboxId
|
|
655
|
+
});
|
|
656
|
+
const [activeIndexState, setActiveIndexState] = useState(() => isShown ? openIndex(model.nav, "selected-or-first") : -1);
|
|
657
|
+
const activeIndex = model.nav.length === 0 ? -1 : Math.min(activeIndexState, model.nav.length - 1);
|
|
658
|
+
const activeItem = activeIndex >= 0 ? model.nav[activeIndex] : void 0;
|
|
659
|
+
const shouldScrollRef = useRef(false);
|
|
660
|
+
const [lastTerm, setLastTerm] = useState(rawTerm);
|
|
661
|
+
if (lastTerm !== rawTerm) {
|
|
662
|
+
setLastTerm(rawTerm);
|
|
663
|
+
if (isShown) {
|
|
664
|
+
setHasSearched(true);
|
|
665
|
+
setActiveIndexState(firstEnabledIndex(model.nav));
|
|
666
|
+
shouldScrollRef.current = true;
|
|
167
667
|
}
|
|
168
668
|
}
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
setOpen(!visible);
|
|
193
|
-
}, [
|
|
194
|
-
effectiveIsDisabled,
|
|
195
|
-
setOpen,
|
|
196
|
-
visible
|
|
197
|
-
]);
|
|
198
|
-
const handleClose = useCallback(() => {
|
|
199
|
-
setOpen(false);
|
|
200
|
-
}, [setOpen]);
|
|
201
|
-
const handleOptionSelect = (option) => {
|
|
202
|
-
if (onChange) onChange(option);
|
|
203
|
-
handleClose();
|
|
204
|
-
};
|
|
205
|
-
const handlePinnedOptionSelect = (pinnedOption) => {
|
|
206
|
-
const option = options.find((opt) => opt.id === pinnedOption.optionId);
|
|
207
|
-
if (option && onChange) onChange(option);
|
|
208
|
-
handleClose();
|
|
669
|
+
const openedInternallyRef = useRef(false);
|
|
670
|
+
const [lastShown, setLastShown] = useState(isShown);
|
|
671
|
+
if (lastShown !== isShown) {
|
|
672
|
+
setLastShown(isShown);
|
|
673
|
+
if (isShown && !openedInternallyRef.current) setActiveIndexState(openIndex(model.nav, "selected-or-first"));
|
|
674
|
+
if (!isShown) {
|
|
675
|
+
setRawTerm("");
|
|
676
|
+
setHasSearched(false);
|
|
677
|
+
setIsReasonDismissed(false);
|
|
678
|
+
}
|
|
679
|
+
openedInternallyRef.current = false;
|
|
680
|
+
}
|
|
681
|
+
/** Keyboard moves scroll the active row into view; pointer moves do not. */
|
|
682
|
+
const setActive = useCallback((index, shouldScroll) => {
|
|
683
|
+
shouldScrollRef.current = shouldScroll;
|
|
684
|
+
setActiveIndexState(index);
|
|
685
|
+
setIsReasonDismissed(false);
|
|
686
|
+
}, []);
|
|
687
|
+
/** Opens the panel on a target row. */
|
|
688
|
+
const openTo = (index) => {
|
|
689
|
+
openedInternallyRef.current = true;
|
|
690
|
+
setActive(index, true);
|
|
691
|
+
setOpen(true);
|
|
209
692
|
};
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
693
|
+
const close = () => setOpen(false);
|
|
694
|
+
useRestoreFocusAfterOutsidePress({
|
|
695
|
+
isShown,
|
|
696
|
+
focusRef: controlRef,
|
|
697
|
+
insideRefs: [wrapperRef, panelRef],
|
|
698
|
+
focusScopeRefs: [wrapperRef, panelRef]
|
|
699
|
+
});
|
|
700
|
+
const onSearchRef = useRef(onSearch);
|
|
701
|
+
onSearchRef.current = onSearch;
|
|
702
|
+
const debouncedTerm = useDebouncedValue(rawTerm, SELECT_SEARCH_DEBOUNCE_MS, onSearch !== void 0);
|
|
703
|
+
const lastSentTermRef = useRef("");
|
|
704
|
+
useEffect(() => {
|
|
705
|
+
if (!isShown || onSearchRef.current === void 0) return;
|
|
706
|
+
if (debouncedTerm === lastSentTermRef.current) return;
|
|
707
|
+
lastSentTermRef.current = debouncedTerm;
|
|
708
|
+
onSearchRef.current(debouncedTerm);
|
|
709
|
+
}, [debouncedTerm, isShown]);
|
|
710
|
+
const wasShownForSearchRef = useRef(isShown);
|
|
711
|
+
useEffect(() => {
|
|
712
|
+
if (wasShownForSearchRef.current && !isShown && onSearchRef.current !== void 0) {
|
|
713
|
+
lastSentTermRef.current = "";
|
|
714
|
+
onSearchRef.current("");
|
|
715
|
+
}
|
|
716
|
+
wasShownForSearchRef.current = isShown;
|
|
717
|
+
}, [isShown]);
|
|
718
|
+
const onEndReachedRef = useRef(onEndReached);
|
|
719
|
+
onEndReachedRef.current = onEndReached;
|
|
720
|
+
const firedAtLengthRef = useRef(-1);
|
|
721
|
+
const optionsLength = options.length;
|
|
722
|
+
const reachEnd = useCallback(() => {
|
|
723
|
+
if (onEndReachedRef.current === void 0) return;
|
|
724
|
+
if (firedAtLengthRef.current === optionsLength) return;
|
|
725
|
+
firedAtLengthRef.current = optionsLength;
|
|
726
|
+
onEndReachedRef.current();
|
|
727
|
+
}, [optionsLength]);
|
|
728
|
+
const lastOptionNavIndex = (() => {
|
|
729
|
+
for (let index = model.nav.length - 1; index >= 0; index -= 1) if (model.nav[index]?.kind === "option") return index;
|
|
730
|
+
return -1;
|
|
731
|
+
})();
|
|
732
|
+
const typeaheadRef = useRef({
|
|
733
|
+
buffer: "",
|
|
734
|
+
timer: null
|
|
735
|
+
});
|
|
736
|
+
useEffect(() => () => {
|
|
737
|
+
if (typeaheadRef.current.timer !== null) clearTimeout(typeaheadRef.current.timer);
|
|
738
|
+
}, []);
|
|
739
|
+
const typeahead = (key, fromIndex) => {
|
|
740
|
+
const state = typeaheadRef.current;
|
|
741
|
+
if (state.timer !== null) clearTimeout(state.timer);
|
|
742
|
+
state.buffer += key.toLocaleLowerCase();
|
|
743
|
+
state.timer = setTimeout(() => {
|
|
744
|
+
state.buffer = "";
|
|
745
|
+
state.timer = null;
|
|
746
|
+
}, SELECT_TYPEAHEAD_RESET_MS);
|
|
747
|
+
const isRepeat = [...state.buffer].every((char) => char === state.buffer[0]);
|
|
748
|
+
const prefix = isRepeat ? state.buffer.slice(0, 1) : state.buffer;
|
|
749
|
+
const rows = model.nav;
|
|
750
|
+
const start = isRepeat ? fromIndex + 1 : Math.max(fromIndex, 0);
|
|
751
|
+
for (let offset = 0; offset < rows.length; offset += 1) {
|
|
752
|
+
const row = rows[(start + offset + rows.length) % rows.length];
|
|
753
|
+
if (row === void 0 || row.kind !== "option") continue;
|
|
754
|
+
if (row.option.label.toLocaleLowerCase().startsWith(prefix)) return row.navIndex;
|
|
755
|
+
}
|
|
756
|
+
return fromIndex;
|
|
213
757
|
};
|
|
214
|
-
|
|
215
|
-
|
|
758
|
+
/** Chooses a row: an enabled option, or the Create row. */
|
|
759
|
+
const choose = (index) => {
|
|
760
|
+
const item = model.nav[index];
|
|
761
|
+
if (item === void 0) return;
|
|
762
|
+
if (item.kind === "create") {
|
|
763
|
+
onCreate?.(item.label);
|
|
764
|
+
setRawTerm("");
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
if (item.isDisabled) return;
|
|
768
|
+
onOptionSelect(item.option.id);
|
|
769
|
+
if (!isMultiple) close();
|
|
216
770
|
};
|
|
217
|
-
|
|
218
|
-
|
|
771
|
+
/** Moves the active row by the keyboard. */
|
|
772
|
+
const moveTo = (index) => {
|
|
773
|
+
if (model.nav.length === 0) return;
|
|
774
|
+
const next = Math.max(0, Math.min(model.nav.length - 1, index));
|
|
775
|
+
setActive(next, true);
|
|
776
|
+
if (next === lastOptionNavIndex) reachEnd();
|
|
219
777
|
};
|
|
220
|
-
const handleKeyDown = (
|
|
221
|
-
if (
|
|
222
|
-
if (
|
|
223
|
-
|
|
778
|
+
const handleKeyDown = (event) => {
|
|
779
|
+
if (event.defaultPrevented || event.nativeEvent.isComposing) return;
|
|
780
|
+
if (!isEditable) return;
|
|
781
|
+
const { key } = event;
|
|
782
|
+
if (!isShown) {
|
|
783
|
+
if (key === "Enter" || key === "ArrowDown" || key === " " && !isSearchable) {
|
|
784
|
+
event.preventDefault();
|
|
785
|
+
openTo(openIndex(model.nav, "selected-or-first"));
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
if (key === "ArrowUp") {
|
|
789
|
+
event.preventDefault();
|
|
790
|
+
openTo(openIndex(model.nav, "selected-or-last"));
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
if ((key === "Home" || key === "End") && !isSearchable) {
|
|
794
|
+
event.preventDefault();
|
|
795
|
+
openTo(openIndex(model.nav, key === "Home" ? "first" : "last"));
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
if (isSearchable && (key === "Backspace" || key === "Delete")) {
|
|
799
|
+
event.preventDefault();
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
if (isPrintableKey(event)) {
|
|
803
|
+
event.preventDefault();
|
|
804
|
+
if (isSearchable) {
|
|
805
|
+
openedInternallyRef.current = true;
|
|
806
|
+
setRawTerm(key);
|
|
807
|
+
setOpen(true);
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
if (key === " ") return;
|
|
811
|
+
openTo(typeahead(key, openIndex(model.nav, "selected-or-first")));
|
|
812
|
+
}
|
|
224
813
|
return;
|
|
225
814
|
}
|
|
226
|
-
|
|
227
|
-
e.preventDefault();
|
|
228
|
-
handleToggle();
|
|
229
|
-
}
|
|
230
|
-
};
|
|
231
|
-
const handleSearchKeyDown = (e) => {
|
|
232
|
-
if (!isSearchable) return;
|
|
233
|
-
const allOptions = [...filteredPinnedOptions, ...filteredOptions];
|
|
234
|
-
const currentIndex = focusedOptionIndex;
|
|
235
|
-
const focusOptionAtIndex = (nextIndex) => {
|
|
236
|
-
setFocusedOptionIndex(nextIndex);
|
|
237
|
-
optionsRefs.current[nextIndex]?.scrollIntoView({ block: "nearest" });
|
|
238
|
-
};
|
|
239
|
-
switch (e.key) {
|
|
815
|
+
switch (key) {
|
|
240
816
|
case "ArrowDown":
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
817
|
+
event.preventDefault();
|
|
818
|
+
moveTo(activeIndex === -1 ? 0 : activeIndex + 1);
|
|
819
|
+
return;
|
|
244
820
|
case "ArrowUp":
|
|
245
|
-
|
|
246
|
-
if (
|
|
247
|
-
|
|
248
|
-
|
|
821
|
+
event.preventDefault();
|
|
822
|
+
if (event.altKey) {
|
|
823
|
+
if (!isMultiple && activeIndex !== -1) choose(activeIndex);
|
|
824
|
+
close();
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
moveTo(activeIndex === -1 ? model.nav.length - 1 : activeIndex - 1);
|
|
828
|
+
return;
|
|
829
|
+
case "PageDown":
|
|
830
|
+
event.preventDefault();
|
|
831
|
+
moveTo((activeIndex === -1 ? 0 : activeIndex) + SELECT_PAGE_STEP);
|
|
832
|
+
return;
|
|
833
|
+
case "PageUp":
|
|
834
|
+
event.preventDefault();
|
|
835
|
+
moveTo((activeIndex === -1 ? 0 : activeIndex) - SELECT_PAGE_STEP);
|
|
836
|
+
return;
|
|
837
|
+
case "Home":
|
|
838
|
+
case "End":
|
|
839
|
+
if (isSearchable) return;
|
|
840
|
+
event.preventDefault();
|
|
841
|
+
moveTo(key === "Home" ? 0 : model.nav.length - 1);
|
|
842
|
+
return;
|
|
249
843
|
case "Enter":
|
|
250
|
-
|
|
251
|
-
if (
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
e.preventDefault();
|
|
257
|
-
handleClose();
|
|
258
|
-
break;
|
|
844
|
+
event.preventDefault();
|
|
845
|
+
if (activeIndex !== -1) choose(activeIndex);
|
|
846
|
+
return;
|
|
847
|
+
case "Tab":
|
|
848
|
+
close();
|
|
849
|
+
return;
|
|
259
850
|
default:
|
|
260
|
-
if (
|
|
261
|
-
|
|
851
|
+
if (key === " " && !isSearchable) {
|
|
852
|
+
event.preventDefault();
|
|
853
|
+
if (activeIndex !== -1) choose(activeIndex);
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
if (!isSearchable && isPrintableKey(event)) {
|
|
857
|
+
event.preventDefault();
|
|
858
|
+
setActive(typeahead(key, activeIndex), true);
|
|
859
|
+
}
|
|
262
860
|
}
|
|
263
861
|
};
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
862
|
+
/** A click on the field toggles the panel; inside an open searchable input it only places the caret. */
|
|
863
|
+
const handleClick = () => {
|
|
864
|
+
if (!isEditable) return;
|
|
865
|
+
if (isShown) {
|
|
866
|
+
if (!isSearchable) close();
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
openTo(openIndex(model.nav, "selected-or-first"));
|
|
267
870
|
};
|
|
268
|
-
|
|
269
|
-
|
|
871
|
+
/** Focus leaving the field for anything outside the panel closes it. */
|
|
872
|
+
const handleBlur = (event) => {
|
|
873
|
+
if (!isShown) return;
|
|
874
|
+
const next = event.relatedTarget;
|
|
875
|
+
if (next !== null && panelRef.current?.contains(next)) return;
|
|
876
|
+
close();
|
|
270
877
|
};
|
|
271
|
-
const
|
|
272
|
-
|
|
878
|
+
const handleInputChange = (event) => {
|
|
879
|
+
if (!isEditable) return;
|
|
880
|
+
if (!isShown) {
|
|
881
|
+
openedInternallyRef.current = true;
|
|
882
|
+
setOpen(true);
|
|
883
|
+
}
|
|
884
|
+
setRawTerm(event.target.value);
|
|
273
885
|
};
|
|
274
|
-
|
|
275
|
-
|
|
886
|
+
/** Pasting into a closed searchable field starts a term instead of editing the value text. */
|
|
887
|
+
const handlePaste = (event) => {
|
|
888
|
+
if (isShown || !isEditable) return;
|
|
889
|
+
event.preventDefault();
|
|
890
|
+
openedInternallyRef.current = true;
|
|
891
|
+
setRawTerm(event.clipboardData.getData("text"));
|
|
892
|
+
setOpen(true);
|
|
276
893
|
};
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
return /* @__PURE__ */ jsxs(SelectInputOption, {
|
|
307
|
-
ref: (el) => {
|
|
308
|
-
optionsRefs.current[absoluteIndex] = el;
|
|
309
|
-
},
|
|
310
|
-
$isSelected: option.id === value?.id,
|
|
311
|
-
$isFocused: isOptionFocused(index),
|
|
312
|
-
onClick: () => handleOptionSelect(option),
|
|
313
|
-
onMouseEnter: () => setFocusedOptionIndex(absoluteIndex),
|
|
314
|
-
children: [/* @__PURE__ */ jsxs(FlexWrapper, {
|
|
315
|
-
alignItems: AlignItems.CENTER,
|
|
316
|
-
gap: 8,
|
|
317
|
-
minWidth: 0,
|
|
318
|
-
grow: 1,
|
|
319
|
-
children: [option.icon && /* @__PURE__ */ jsx(OptionIconSlot, { children: option.icon }), /* @__PURE__ */ jsx(FlexItem, {
|
|
320
|
-
grow: 1,
|
|
321
|
-
shrink: 1,
|
|
322
|
-
minWidth: 0,
|
|
323
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
324
|
-
size: getInputTextSize(size),
|
|
325
|
-
cursor: !effectiveIsDisabled ? "pointer" : "not-allowed",
|
|
326
|
-
isEllipsis: true,
|
|
327
|
-
variant: option.variant ?? TextVariant.PRIMARY,
|
|
328
|
-
children: option.label
|
|
329
|
-
})
|
|
330
|
-
})]
|
|
331
|
-
}), option.id === value?.id && /* @__PURE__ */ jsx(OptionIconSlot, { children: /* @__PURE__ */ jsx(Icon, {
|
|
332
|
-
component: CheckIcon,
|
|
333
|
-
variant: IconVariant.TERTIARY,
|
|
334
|
-
weight: IconWeight.BOLD,
|
|
335
|
-
size: 12
|
|
336
|
-
}) })]
|
|
337
|
-
}, option.id);
|
|
338
|
-
}) : /* @__PURE__ */ jsx(FlexWrapper, {
|
|
339
|
-
padding: 8,
|
|
340
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
341
|
-
variant: TextVariant.TERTIARY,
|
|
342
|
-
size: getInputTextSize(size),
|
|
343
|
-
children: "No options found"
|
|
344
|
-
})
|
|
345
|
-
}) }),
|
|
346
|
-
onReset && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(HorizontalDivider, {}), /* @__PURE__ */ jsx(PinnedOptionButton, {
|
|
347
|
-
onClick: handleReset,
|
|
348
|
-
$isSelected: false,
|
|
349
|
-
$isFocused: false,
|
|
350
|
-
children: /* @__PURE__ */ jsxs(FlexWrapper, {
|
|
351
|
-
alignItems: AlignItems.CENTER,
|
|
352
|
-
gap: 8,
|
|
353
|
-
children: [/* @__PURE__ */ jsx(Icon, {
|
|
354
|
-
component: ArrowCounterClockwiseIcon,
|
|
355
|
-
variant: IconVariant.TERTIARY,
|
|
356
|
-
size: 12
|
|
357
|
-
}), /* @__PURE__ */ jsx(Text, {
|
|
358
|
-
variant: TextVariant.SECONDARY,
|
|
359
|
-
size: getInputTextSize(size),
|
|
360
|
-
cursor: !effectiveIsDisabled ? "pointer" : "not-allowed",
|
|
361
|
-
children: "Reset"
|
|
362
|
-
})]
|
|
363
|
-
})
|
|
364
|
-
})] })
|
|
365
|
-
]
|
|
894
|
+
const clear = () => {
|
|
895
|
+
onClear();
|
|
896
|
+
if (isShown) close();
|
|
897
|
+
controlRef.current?.focus();
|
|
898
|
+
};
|
|
899
|
+
const activeFlatIndex = activeItem?.flatIndex ?? -1;
|
|
900
|
+
const isVirtualized = model.flat.length > SELECT_VIRTUALIZE_THRESHOLD;
|
|
901
|
+
const rowHeightPx = SELECT_INPUT_SIZE_TO_ROW_HEIGHT_PX_MAP[size];
|
|
902
|
+
const secondaryLinePx = SELECT_INPUT_SIZE_TO_SECONDARY_LINE_PX_MAP[size];
|
|
903
|
+
const flatRef = useRef(model.flat);
|
|
904
|
+
flatRef.current = model.flat;
|
|
905
|
+
const virtualizer = useVirtualizer({
|
|
906
|
+
count: isVirtualized ? model.flat.length : 0,
|
|
907
|
+
getScrollElement: () => viewportElement,
|
|
908
|
+
estimateSize: (index) => {
|
|
909
|
+
const item = flatRef.current[index];
|
|
910
|
+
if (item?.kind === "option" && isFilled(item.option.description)) return rowHeightPx + secondaryLinePx;
|
|
911
|
+
if (item?.kind === "separator") return Number.parseFloat(SPACE[4]) * 2 + 1;
|
|
912
|
+
return rowHeightPx;
|
|
913
|
+
},
|
|
914
|
+
overscan: SELECT_VIRTUAL_OVERSCAN,
|
|
915
|
+
rangeExtractor: (range) => {
|
|
916
|
+
const indexes = defaultRangeExtractor(range);
|
|
917
|
+
if (activeFlatIndex < 0 || activeFlatIndex >= range.count) return indexes;
|
|
918
|
+
if (indexes.includes(activeFlatIndex)) return indexes;
|
|
919
|
+
return [...indexes, activeFlatIndex].sort((a, b) => a - b);
|
|
920
|
+
},
|
|
921
|
+
paddingStart: SELECT_LIST_INSET_PX,
|
|
922
|
+
paddingEnd: SELECT_LIST_INSET_PX
|
|
366
923
|
});
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
924
|
+
const virtualItems = isVirtualized ? virtualizer.getVirtualItems() : [];
|
|
925
|
+
const visibleRange = isVirtualized ? virtualizer.range : null;
|
|
926
|
+
const lastVirtualIndex = visibleRange === null ? -1 : Math.min(visibleRange.endIndex + SELECT_VIRTUAL_OVERSCAN, model.flat.length - 1);
|
|
927
|
+
const lastOptionFlatIndex = lastOptionNavIndex === -1 ? -1 : model.nav[lastOptionNavIndex]?.flatIndex ?? -1;
|
|
928
|
+
useEffect(() => {
|
|
929
|
+
if (!isShown || !isVirtualized || lastOptionFlatIndex === -1) return;
|
|
930
|
+
if (lastVirtualIndex >= lastOptionFlatIndex) reachEnd();
|
|
371
931
|
}, [
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
932
|
+
isShown,
|
|
933
|
+
isVirtualized,
|
|
934
|
+
lastVirtualIndex,
|
|
935
|
+
lastOptionFlatIndex,
|
|
936
|
+
reachEnd
|
|
376
937
|
]);
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
938
|
+
const activeDomId = activeItem?.domId;
|
|
939
|
+
useIsomorphicLayoutEffect(() => {
|
|
940
|
+
if (!shouldScrollRef.current || !isShown || viewportElement === null) return;
|
|
941
|
+
shouldScrollRef.current = false;
|
|
942
|
+
if (isVirtualized) {
|
|
943
|
+
if (activeFlatIndex >= 0) virtualizer.scrollToIndex(activeFlatIndex, { align: "auto" });
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
if (activeDomId === void 0) return;
|
|
947
|
+
panelRef.current?.ownerDocument.getElementById(activeDomId)?.scrollIntoView({ block: "nearest" });
|
|
948
|
+
});
|
|
949
|
+
const handleScroll = (event) => {
|
|
950
|
+
if (isVirtualized) return;
|
|
951
|
+
const { scrollTop, clientHeight, scrollHeight } = event.currentTarget;
|
|
952
|
+
if (scrollTop + clientHeight >= scrollHeight - rowHeightPx) reachEnd();
|
|
953
|
+
};
|
|
954
|
+
useDevWarning(!hasName, "[@galaxy-io/dls] SelectInput has no accessible name. Pass `label`, wrap it in a labelled `Field`, or set `ariaLabel`. `placeholder` is not a name.");
|
|
955
|
+
let statusText = "";
|
|
956
|
+
if (isShown) {
|
|
957
|
+
if (isLoading) statusText = "Loading";
|
|
958
|
+
else if (model.nav.length === 0) statusText = emptyMessage;
|
|
959
|
+
else if (hasSearched) statusText = `${model.optionCount} ${model.optionCount === 1 ? "result" : "results"}`;
|
|
960
|
+
}
|
|
961
|
+
const textSize = SELECT_INPUT_SIZE_TO_TEXT_SIZE_MAP[size];
|
|
962
|
+
const caretPx = SELECT_INPUT_SIZE_TO_CARET_PX_MAP[size];
|
|
963
|
+
const rowTextSize = SELECT_INPUT_SIZE_TO_ROW_TEXT_SIZE_MAP[size];
|
|
964
|
+
const rowSecondarySize = SELECT_INPUT_SIZE_TO_ROW_SECONDARY_TEXT_SIZE_MAP[size];
|
|
965
|
+
const rowIconPx = SELECT_INPUT_SIZE_TO_ROW_ICON_PX_MAP[size];
|
|
966
|
+
const hasSelection = selectedOptions.length > 0;
|
|
967
|
+
const isClearHidden = !hasSelection || !isEditable;
|
|
968
|
+
const controlClassName = cx(isGhost ? GHOST_CSS : SELECT_INPUT_VARIANT_TO_CSS_MAP[variant], SELECT_INPUT_SIZE_TO_CSS_MAP[size], isClearable && SELECT_INPUT_SIZE_TO_CLEARABLE_CSS_MAP[size], isEditable && !isInvalid && (isGhost ? GHOST_HOVER_CSS : SELECT_INPUT_VARIANT_TO_HOVER_CSS_MAP[variant]), !isDisabled && FOCUS_CSS, isShown && OPEN_CSS, isInvalid && !isDisabled && ERROR_CSS, isDisabled && DISABLED_CSS, isReadOnly && READ_ONLY_CSS, isBare && "gx-bareField-BARE_FIELD_CSS-b1memido");
|
|
969
|
+
const sharedControlProps = {
|
|
970
|
+
ref: mergedRef,
|
|
971
|
+
id: controlId,
|
|
972
|
+
role: "combobox",
|
|
973
|
+
"aria-activedescendant": isShown && activeDomId !== void 0 ? activeDomId : void 0,
|
|
974
|
+
"aria-labelledby": field?.labelId,
|
|
975
|
+
"aria-label": ariaLabel,
|
|
976
|
+
"aria-describedby": describedBy,
|
|
977
|
+
"aria-invalid": isInvalid ? true : void 0,
|
|
978
|
+
"aria-required": isRequiredState ? true : void 0,
|
|
979
|
+
"aria-readonly": isReadOnly ? true : void 0,
|
|
980
|
+
onKeyDown: handleKeyDown,
|
|
981
|
+
onClick: handleClick,
|
|
982
|
+
onBlur: handleBlur,
|
|
983
|
+
...rest
|
|
984
|
+
};
|
|
985
|
+
const placeholderText = /* @__PURE__ */ jsx(Text, {
|
|
986
|
+
as: "span",
|
|
987
|
+
size: textSize,
|
|
988
|
+
variant: isDisabled ? TextVariant.DISABLED : TextVariant.TERTIARY,
|
|
989
|
+
lineClamp: 1,
|
|
990
|
+
children: placeholder
|
|
991
|
+
});
|
|
992
|
+
const control = isSearchable ? /* @__PURE__ */ jsx(ControlInput, {
|
|
993
|
+
...sharedControlProps,
|
|
994
|
+
type: "text",
|
|
995
|
+
autoComplete: "off",
|
|
996
|
+
autoCorrect: "off",
|
|
997
|
+
spellCheck: false,
|
|
998
|
+
"aria-autocomplete": "list",
|
|
999
|
+
value: isShown ? rawTerm : selectionText,
|
|
1000
|
+
placeholder,
|
|
1001
|
+
disabled: isDisabled,
|
|
1002
|
+
readOnly: isReadOnly,
|
|
1003
|
+
onChange: handleInputChange,
|
|
1004
|
+
onPaste: handlePaste,
|
|
1005
|
+
className: cx(controlClassName, SELECT_INPUT_SIZE_TO_FONT_CSS_MAP[size], isShown && TYPING_CSS, !isShown && hasSelection && VALUE_OVERLAID_CSS)
|
|
1006
|
+
}) : /* @__PURE__ */ jsx(Control, {
|
|
1007
|
+
...sharedControlProps,
|
|
1008
|
+
tabIndex: isDisabled ? -1 : 0,
|
|
1009
|
+
"aria-disabled": isDisabled ? true : void 0,
|
|
1010
|
+
className: controlClassName,
|
|
1011
|
+
children: hasSelection ? /* @__PURE__ */ jsx(SelectionSlot, { children: renderSelection(selectedOptions) }) : /* @__PURE__ */ jsx(ValueDisplay, {
|
|
1012
|
+
className: SELECT_INPUT_SIZE_TO_VALUE_CSS_MAP[size],
|
|
1013
|
+
children: placeholderText
|
|
1014
|
+
})
|
|
1015
|
+
});
|
|
1016
|
+
/** One option or Create row. */
|
|
1017
|
+
const renderNavRow = (item, virtualStart) => {
|
|
1018
|
+
const isActive = item.navIndex === activeIndex;
|
|
1019
|
+
const isRowDisabled = item.kind === "option" && item.isDisabled;
|
|
1020
|
+
const isSelected = item.kind === "option" && item.isSelected;
|
|
1021
|
+
const virtualProps = virtualStart === void 0 ? {} : {
|
|
1022
|
+
"data-index": item.flatIndex,
|
|
1023
|
+
ref: virtualizer.measureElement,
|
|
1024
|
+
style: { "--gx-select-row-y": `${virtualStart}px` },
|
|
1025
|
+
...item.kind === "option" ? {
|
|
1026
|
+
"aria-setsize": model.optionCount,
|
|
1027
|
+
"aria-posinset": item.position
|
|
1028
|
+
} : {}
|
|
1029
|
+
};
|
|
1030
|
+
let content;
|
|
1031
|
+
if (item.kind === "create") content = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Icon, {
|
|
1032
|
+
component: PlusIcon,
|
|
1033
|
+
size: rowIconPx,
|
|
1034
|
+
variant: IconVariant.SECONDARY,
|
|
1035
|
+
className: NO_SHRINK_CSS
|
|
1036
|
+
}), /* @__PURE__ */ jsx(Content, { children: /* @__PURE__ */ jsxs(Text, {
|
|
1037
|
+
as: "span",
|
|
1038
|
+
size: rowTextSize,
|
|
1039
|
+
lineClamp: 1,
|
|
1040
|
+
children: [
|
|
1041
|
+
"Create \"",
|
|
1042
|
+
/* @__PURE__ */ jsx(Span, {
|
|
1043
|
+
weight: TextWeight.MEDIUM,
|
|
1044
|
+
children: item.label
|
|
1045
|
+
}),
|
|
1046
|
+
"\""
|
|
1047
|
+
]
|
|
1048
|
+
}) })] });
|
|
1049
|
+
else if (renderOption !== void 0) content = /* @__PURE__ */ jsx(Content, { children: renderOption(item.option, {
|
|
1050
|
+
isSelected,
|
|
1051
|
+
isActive
|
|
1052
|
+
}) });
|
|
1053
|
+
else {
|
|
1054
|
+
const { option } = item;
|
|
1055
|
+
content = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
1056
|
+
hasContent(option.leading) && /* @__PURE__ */ jsx(Slot, { children: option.leading }),
|
|
1057
|
+
option.icon !== void 0 && /* @__PURE__ */ jsx(Icon, {
|
|
1058
|
+
component: option.icon,
|
|
1059
|
+
size: rowIconPx,
|
|
1060
|
+
variant: isRowDisabled ? IconVariant.DISABLED : IconVariant.SECONDARY,
|
|
1061
|
+
className: NO_SHRINK_CSS
|
|
1062
|
+
}),
|
|
1063
|
+
/* @__PURE__ */ jsxs(Content, { children: [/* @__PURE__ */ jsx(Text, {
|
|
1064
|
+
as: "span",
|
|
1065
|
+
size: rowTextSize,
|
|
1066
|
+
variant: isRowDisabled ? TextVariant.DISABLED : TextVariant.PRIMARY,
|
|
1067
|
+
lineClamp: 1,
|
|
1068
|
+
children: option.label
|
|
1069
|
+
}), isFilled(option.description) && /* @__PURE__ */ jsx(Text, {
|
|
1070
|
+
as: "span",
|
|
1071
|
+
size: rowSecondarySize,
|
|
1072
|
+
variant: isRowDisabled ? TextVariant.DISABLED : TextVariant.TERTIARY,
|
|
1073
|
+
lineClamp: 2,
|
|
1074
|
+
children: option.description
|
|
1075
|
+
})] })
|
|
1076
|
+
] });
|
|
1077
|
+
}
|
|
1078
|
+
const row = /* @__PURE__ */ jsxs(OptionRow, {
|
|
1079
|
+
...virtualProps,
|
|
1080
|
+
id: item.domId,
|
|
1081
|
+
role: "option",
|
|
1082
|
+
"aria-selected": isSelected,
|
|
1083
|
+
...isRowDisabled ? { "aria-disabled": true } : {},
|
|
1084
|
+
onPointerMove: () => {
|
|
1085
|
+
if (item.navIndex !== activeIndex) setActive(item.navIndex, false);
|
|
1086
|
+
},
|
|
1087
|
+
onClick: () => choose(item.navIndex),
|
|
1088
|
+
className: cx(SELECT_INPUT_SIZE_TO_ROW_CSS_MAP[size], isActive && ACTIVE_ROW_CSS, isActive && !isRowDisabled && ACTIVE_ROW_PRESS_CSS, isRowDisabled && ROW_DISABLED_CSS, virtualStart !== void 0 && VIRTUAL_ROW_CSS),
|
|
1089
|
+
children: [content, /* @__PURE__ */ jsx(CheckSlot, {
|
|
1090
|
+
className: SELECT_INPUT_SIZE_TO_CHECK_SLOT_CSS_MAP[size],
|
|
1091
|
+
children: isSelected && /* @__PURE__ */ jsx(Icon, {
|
|
1092
|
+
component: CheckIcon,
|
|
1093
|
+
size: rowIconPx,
|
|
1094
|
+
variant: isRowDisabled ? IconVariant.DISABLED : IconVariant.PRIMARY,
|
|
1095
|
+
"aria-hidden": true
|
|
1096
|
+
})
|
|
1097
|
+
})]
|
|
1098
|
+
}, item.key);
|
|
1099
|
+
if (item.kind !== "option" || !item.isDisabled || !isFilled(item.disabledReason)) return row;
|
|
1100
|
+
return /* @__PURE__ */ jsx(Tooltip, {
|
|
1101
|
+
body: item.disabledReason,
|
|
1102
|
+
placement: Placement.RIGHT,
|
|
1103
|
+
isOpen: isShown && isActive && !isReasonDismissed,
|
|
1104
|
+
onOpenChange: (next) => {
|
|
1105
|
+
if (!next && isActive) setIsReasonDismissed(true);
|
|
1106
|
+
},
|
|
1107
|
+
children: row
|
|
1108
|
+
}, item.key);
|
|
1109
|
+
};
|
|
1110
|
+
const renderHeading = (heading, virtual) => /* @__PURE__ */ jsx(GroupLabel, {
|
|
1111
|
+
role: "presentation",
|
|
1112
|
+
id: heading.id,
|
|
1113
|
+
className: cx(SELECT_INPUT_SIZE_TO_GROUP_LABEL_CSS_MAP[size], virtual !== void 0 && VIRTUAL_ROW_CSS),
|
|
1114
|
+
...virtual === void 0 ? {} : {
|
|
1115
|
+
"data-index": virtual.index,
|
|
1116
|
+
ref: virtualizer.measureElement,
|
|
1117
|
+
style: { "--gx-select-row-y": `${virtual.start}px` }
|
|
1118
|
+
},
|
|
1119
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1120
|
+
as: "span",
|
|
1121
|
+
size: rowSecondarySize,
|
|
1122
|
+
weight: TextWeight.MEDIUM,
|
|
1123
|
+
variant: TextVariant.TERTIARY,
|
|
1124
|
+
lineClamp: 1,
|
|
1125
|
+
children: heading.label
|
|
1126
|
+
})
|
|
1127
|
+
}, heading.key);
|
|
1128
|
+
const renderSeparator = (key, virtual) => /* @__PURE__ */ jsx("div", {
|
|
1129
|
+
role: "presentation",
|
|
1130
|
+
className: cx(SEPARATOR_CSS, virtual !== void 0 && VIRTUAL_ROW_CSS),
|
|
1131
|
+
...virtual === void 0 ? {} : {
|
|
1132
|
+
"data-index": virtual.index,
|
|
1133
|
+
ref: virtualizer.measureElement,
|
|
1134
|
+
style: { "--gx-select-row-y": `${virtual.start}px` }
|
|
1135
|
+
},
|
|
1136
|
+
children: /* @__PURE__ */ jsx(Divider, { "aria-hidden": true })
|
|
1137
|
+
}, key);
|
|
1138
|
+
const renderStatus = (kind, virtual) => /* @__PURE__ */ jsx(StatusRow, {
|
|
1139
|
+
"aria-hidden": true,
|
|
1140
|
+
className: cx(SELECT_INPUT_SIZE_TO_ROW_CSS_MAP[size], virtual !== void 0 && VIRTUAL_ROW_CSS),
|
|
1141
|
+
...virtual === void 0 ? {} : {
|
|
1142
|
+
"data-index": virtual.index,
|
|
1143
|
+
ref: virtualizer.measureElement,
|
|
1144
|
+
style: { "--gx-select-row-y": `${virtual.start}px` }
|
|
1145
|
+
},
|
|
1146
|
+
children: kind === "loading" ? /* @__PURE__ */ jsx(Spinner, {
|
|
1147
|
+
size: SELECT_INPUT_SIZE_TO_SPINNER_SIZE_MAP[size],
|
|
1148
|
+
label: "Loading",
|
|
1149
|
+
"aria-hidden": true
|
|
1150
|
+
}) : /* @__PURE__ */ jsx(Text, {
|
|
1151
|
+
size: rowTextSize,
|
|
1152
|
+
variant: TextVariant.TERTIARY,
|
|
1153
|
+
children: emptyMessage
|
|
1154
|
+
})
|
|
1155
|
+
}, kind);
|
|
1156
|
+
let listContent;
|
|
1157
|
+
if (isVirtualized) listContent = /* @__PURE__ */ jsx(VirtualBody, {
|
|
1158
|
+
style: { "--gx-select-list-height": `${virtualizer.getTotalSize()}px` },
|
|
1159
|
+
children: virtualItems.map((virtualItem) => {
|
|
1160
|
+
const item = model.flat[virtualItem.index];
|
|
1161
|
+
if (item === void 0) return null;
|
|
1162
|
+
const virtual = {
|
|
1163
|
+
index: virtualItem.index,
|
|
1164
|
+
start: virtualItem.start
|
|
1165
|
+
};
|
|
1166
|
+
switch (item.kind) {
|
|
1167
|
+
case "option":
|
|
1168
|
+
case "create": return renderNavRow(item, virtualItem.start);
|
|
1169
|
+
case "heading": return renderHeading(item, virtual);
|
|
1170
|
+
case "separator": return renderSeparator(item.key, virtual);
|
|
1171
|
+
default: return renderStatus(item.kind, virtual);
|
|
1172
|
+
}
|
|
1173
|
+
})
|
|
1174
|
+
});
|
|
1175
|
+
else listContent = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
1176
|
+
model.pinned.map((item) => renderNavRow(item)),
|
|
1177
|
+
model.hasSeparator && renderSeparator("separator"),
|
|
1178
|
+
model.sections.map((section) => section.heading === null ? section.rows.map((item) => renderNavRow(item)) : /* @__PURE__ */ jsxs(GroupRoot, {
|
|
1179
|
+
role: "group",
|
|
1180
|
+
"aria-labelledby": section.heading.id,
|
|
1181
|
+
children: [renderHeading(section.heading), section.rows.map((item) => renderNavRow(item))]
|
|
1182
|
+
}, section.key)),
|
|
1183
|
+
model.create !== null && renderNavRow(model.create),
|
|
1184
|
+
model.status !== null && renderStatus(model.status)
|
|
1185
|
+
] });
|
|
1186
|
+
const body = /* @__PURE__ */ jsx(ListFrame, {
|
|
1187
|
+
className: cx(isVirtualized && VIRTUALIZED_FRAME_CSS),
|
|
1188
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
1189
|
+
children: /* @__PURE__ */ jsx(ScrollArea, {
|
|
1190
|
+
ref: setViewportElement,
|
|
1191
|
+
onScroll: handleScroll,
|
|
1192
|
+
children: /* @__PURE__ */ jsx(List, {
|
|
1193
|
+
className: cx(isVirtualized && VIRTUAL_LIST_CSS),
|
|
1194
|
+
children: listContent
|
|
1195
|
+
})
|
|
1196
|
+
})
|
|
1197
|
+
});
|
|
1198
|
+
const labelId = field?.labelId;
|
|
1199
|
+
const explicitLabelledBy = rest["aria-labelledby"];
|
|
1200
|
+
return /* @__PURE__ */ jsxs(Wrapper, {
|
|
1201
|
+
ref: wrapperRef,
|
|
1202
|
+
className: cx(fillWidth && FILL_WIDTH_CSS, isRoot ? className : void 0),
|
|
1203
|
+
...rootStyle(isRoot, style),
|
|
382
1204
|
children: [
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
1205
|
+
/* @__PURE__ */ jsx(Popover, {
|
|
1206
|
+
ref: panelRef,
|
|
1207
|
+
trigger: PopoverTrigger.MANUAL,
|
|
1208
|
+
role: PopoverRole.LISTBOX,
|
|
1209
|
+
variant: isGhost ? PopoverVariant.BASE : SELECT_INPUT_VARIANT_TO_POPOVER_VARIANT_MAP[variant],
|
|
1210
|
+
placement,
|
|
1211
|
+
isOpen: isShown,
|
|
1212
|
+
onOpenChange: setOpen,
|
|
1213
|
+
isDisabled: !isEditable,
|
|
1214
|
+
id: listboxId,
|
|
1215
|
+
...isFilled(explicitLabelledBy) ? { "aria-labelledby": explicitLabelledBy } : labelId !== void 0 ? { "aria-labelledby": labelId } : isFilled(ariaLabel) ? { ariaLabel } : {},
|
|
1216
|
+
...isMultiple ? { "aria-multiselectable": true } : {},
|
|
1217
|
+
...isLoading ? { "aria-busy": true } : {},
|
|
1218
|
+
body: /* @__PURE__ */ jsx(BareFieldContext.Provider, {
|
|
1219
|
+
value: false,
|
|
1220
|
+
children: body
|
|
1221
|
+
}),
|
|
1222
|
+
children: control
|
|
386
1223
|
}),
|
|
387
|
-
/* @__PURE__ */ jsx(
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
onClose: handleClose,
|
|
392
|
-
minWidth: 0,
|
|
393
|
-
offset: [0, 3],
|
|
394
|
-
fillWidth,
|
|
395
|
-
noPadding: true,
|
|
396
|
-
children: isSearchable && visible ? /* @__PURE__ */ jsxs(StyledSelectInput, {
|
|
397
|
-
ref: triggerRef,
|
|
398
|
-
$variant: variant,
|
|
399
|
-
$size: size,
|
|
400
|
-
$isActive: visible ?? false,
|
|
401
|
-
$isDisabled: effectiveIsDisabled,
|
|
402
|
-
$isError: !!error,
|
|
403
|
-
$width: width,
|
|
404
|
-
$fillWidth: fillWidth,
|
|
405
|
-
onMouseEnter: handleMouseEnter,
|
|
406
|
-
onMouseLeave: handleMouseLeave,
|
|
407
|
-
onClick: handleToggle,
|
|
408
|
-
children: [/* @__PURE__ */ jsx(StyledSearchInput, {
|
|
409
|
-
ref: inputRef,
|
|
410
|
-
$size: size,
|
|
411
|
-
$isActive: visible ?? false,
|
|
412
|
-
$isDisabled: effectiveIsDisabled,
|
|
413
|
-
$isError: !!error,
|
|
414
|
-
$width: width,
|
|
415
|
-
$fillWidth: fillWidth,
|
|
416
|
-
type: "text",
|
|
417
|
-
placeholder,
|
|
418
|
-
value: searchTerm,
|
|
419
|
-
onChange: handleInputChange,
|
|
420
|
-
onKeyDown: handleSearchKeyDown,
|
|
421
|
-
autoComplete: "off",
|
|
422
|
-
autoCorrect: "off",
|
|
423
|
-
disabled: effectiveIsDisabled,
|
|
424
|
-
onClick: (e) => e.stopPropagation()
|
|
425
|
-
}), /* @__PURE__ */ jsx(FlexWrapper, {
|
|
426
|
-
shrink: 0,
|
|
427
|
-
children: /* @__PURE__ */ jsx(Rotate, {
|
|
428
|
-
isRotated: visible,
|
|
429
|
-
deg: -180,
|
|
430
|
-
children: /* @__PURE__ */ jsx(Icon, {
|
|
431
|
-
component: CaretDownIcon,
|
|
432
|
-
size: getInputIconSize(size),
|
|
433
|
-
variant: !effectiveIsDisabled && (isHovered || visible) ? IconVariant.SECONDARY : IconVariant.TERTIARY
|
|
434
|
-
})
|
|
435
|
-
})
|
|
436
|
-
})]
|
|
437
|
-
}) : /* @__PURE__ */ jsxs(StyledSelectInput, {
|
|
438
|
-
ref: triggerRef,
|
|
439
|
-
$variant: variant,
|
|
440
|
-
$size: size,
|
|
441
|
-
$isActive: visible ?? false,
|
|
442
|
-
$isDisabled: effectiveIsDisabled,
|
|
443
|
-
$isError: !!error,
|
|
444
|
-
$width: width,
|
|
445
|
-
$fillWidth: fillWidth,
|
|
446
|
-
onClick: handleToggle,
|
|
447
|
-
onKeyDown: handleKeyDown,
|
|
448
|
-
onMouseEnter: handleMouseEnter,
|
|
449
|
-
onMouseLeave: handleMouseLeave,
|
|
450
|
-
role: "button",
|
|
451
|
-
tabIndex: effectiveIsDisabled ? -1 : 0,
|
|
452
|
-
"aria-haspopup": "listbox",
|
|
453
|
-
"aria-expanded": visible,
|
|
454
|
-
"aria-label": selectedOption ? selectedOption.label : placeholder,
|
|
455
|
-
children: [/* @__PURE__ */ jsxs(FlexWrapper, {
|
|
456
|
-
alignItems: AlignItems.CENTER,
|
|
457
|
-
gap: 8,
|
|
458
|
-
minWidth: 0,
|
|
459
|
-
grow: 1,
|
|
460
|
-
children: [selectedOption?.icon && /* @__PURE__ */ jsx(OptionIconSlot, { children: selectedOption.icon }), /* @__PURE__ */ jsx(FlexItem, {
|
|
461
|
-
grow: 1,
|
|
462
|
-
shrink: 1,
|
|
463
|
-
minWidth: 0,
|
|
464
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
465
|
-
variant: textVariant,
|
|
466
|
-
size: getInputTextSize(size),
|
|
467
|
-
cursor: !effectiveIsDisabled ? "pointer" : "not-allowed",
|
|
468
|
-
isEllipsis: true,
|
|
469
|
-
children: selectedOption ? selectedOption.label : placeholder
|
|
470
|
-
})
|
|
471
|
-
})]
|
|
472
|
-
}), /* @__PURE__ */ jsx(FlexWrapper, {
|
|
473
|
-
shrink: 0,
|
|
474
|
-
children: /* @__PURE__ */ jsx(Rotate, {
|
|
475
|
-
isRotated: visible,
|
|
476
|
-
deg: -180,
|
|
477
|
-
children: /* @__PURE__ */ jsx(Icon, {
|
|
478
|
-
component: CaretDownIcon,
|
|
479
|
-
size: getInputIconSize(size),
|
|
480
|
-
variant: !effectiveIsDisabled && (isHovered || visible) ? IconVariant.SECONDARY : IconVariant.TERTIARY
|
|
481
|
-
})
|
|
482
|
-
})
|
|
483
|
-
})]
|
|
484
|
-
})
|
|
1224
|
+
isSearchable && !isShown && hasSelection && /* @__PURE__ */ jsx(ValueOverlay, {
|
|
1225
|
+
"aria-hidden": true,
|
|
1226
|
+
className: cx(SELECT_INPUT_SIZE_TO_OVERLAY_CSS_MAP[size], isClearable && SELECT_INPUT_SIZE_TO_CLEARABLE_CSS_MAP[size]),
|
|
1227
|
+
children: /* @__PURE__ */ jsx(SelectionSlot, { children: renderSelection(selectedOptions) })
|
|
485
1228
|
}),
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
1229
|
+
/* @__PURE__ */ jsxs(Trailing, {
|
|
1230
|
+
className: SELECT_INPUT_SIZE_TO_TRAILING_CSS_MAP[size],
|
|
1231
|
+
children: [isClearable && /* @__PURE__ */ jsx(ClearSlot, {
|
|
1232
|
+
className: cx(isClearHidden && CLEAR_HIDDEN_CSS),
|
|
1233
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
1234
|
+
variant: ButtonVariant.TERTIARY,
|
|
1235
|
+
size: SELECT_INPUT_SIZE_TO_BUTTON_SIZE_MAP[size],
|
|
1236
|
+
icon: XIcon,
|
|
1237
|
+
ariaLabel: "Clear",
|
|
1238
|
+
onClick: clear,
|
|
1239
|
+
className: cx(size === "X_SMALL" && "gx-innerButton-X_SMALL_INNER_BUTTON_CSS-x1ep0pcd")
|
|
1240
|
+
})
|
|
1241
|
+
}), /* @__PURE__ */ jsx(Rotate, {
|
|
1242
|
+
isRotated: isShown,
|
|
1243
|
+
"aria-hidden": true,
|
|
1244
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
1245
|
+
component: CaretDownIcon,
|
|
1246
|
+
size: caretPx,
|
|
1247
|
+
variant: isDisabled ? IconVariant.DISABLED : IconVariant.SECONDARY
|
|
1248
|
+
})
|
|
1249
|
+
})]
|
|
1250
|
+
}),
|
|
1251
|
+
/* @__PURE__ */ jsx(StatusRegion, {
|
|
1252
|
+
role: "status",
|
|
1253
|
+
"aria-live": "polite",
|
|
1254
|
+
children: statusText
|
|
490
1255
|
})
|
|
491
1256
|
]
|
|
492
1257
|
});
|
|
493
|
-
}
|
|
1258
|
+
}
|
|
1259
|
+
/**
|
|
1260
|
+
* @internal Shared by SelectInput and MultiSelectInput; apps use those. The engine: the field
|
|
1261
|
+
* (with its own `Field` for `label` / `error`), the listbox panel, the rows, search, typeahead and
|
|
1262
|
+
* virtualization.
|
|
1263
|
+
*/
|
|
1264
|
+
var SelectInputRootInner = forwardRef(function SelectInputRoot(props, ref) {
|
|
1265
|
+
const { label, error, isRequired = false, componentName = "SelectInput", size = "MEDIUM", fillWidth = false } = props;
|
|
1266
|
+
const { id, className, style } = props;
|
|
1267
|
+
const ownField = useOwnField({
|
|
1268
|
+
componentName,
|
|
1269
|
+
label,
|
|
1270
|
+
error,
|
|
1271
|
+
isRequired,
|
|
1272
|
+
size: SELECT_INPUT_SIZE_TO_FIELD_SIZE_MAP[size],
|
|
1273
|
+
id,
|
|
1274
|
+
className,
|
|
1275
|
+
style,
|
|
1276
|
+
rootClassName: fillWidth && FILL_WIDTH_CSS
|
|
1277
|
+
});
|
|
1278
|
+
if (ownField !== null) return /* @__PURE__ */ jsx(Field, {
|
|
1279
|
+
...ownField,
|
|
1280
|
+
children: /* @__PURE__ */ jsx(SelectInputFrame, {
|
|
1281
|
+
rootProps: props,
|
|
1282
|
+
forwardedRef: ref,
|
|
1283
|
+
isRoot: false
|
|
1284
|
+
})
|
|
1285
|
+
});
|
|
1286
|
+
return /* @__PURE__ */ jsx(SelectInputFrame, {
|
|
1287
|
+
rootProps: props,
|
|
1288
|
+
forwardedRef: ref,
|
|
1289
|
+
isRoot: true
|
|
1290
|
+
});
|
|
1291
|
+
});
|
|
1292
|
+
SelectInputRootInner.displayName = "SelectInputRoot";
|
|
1293
|
+
/** @internal Shared by SelectInput and MultiSelectInput; apps use those. */
|
|
1294
|
+
var SelectInputRoot = SelectInputRootInner;
|
|
1295
|
+
/** The default value display: the option's leading, icon and label. */
|
|
1296
|
+
function OptionValue({ option, size, isDisabled }) {
|
|
1297
|
+
return /* @__PURE__ */ jsxs(ValueDisplay, {
|
|
1298
|
+
className: SELECT_INPUT_SIZE_TO_VALUE_CSS_MAP[size],
|
|
1299
|
+
children: [
|
|
1300
|
+
hasContent(option.leading) && /* @__PURE__ */ jsx(Slot, { children: option.leading }),
|
|
1301
|
+
option.icon !== void 0 && /* @__PURE__ */ jsx(Icon, {
|
|
1302
|
+
component: option.icon,
|
|
1303
|
+
size: SELECT_INPUT_SIZE_TO_ICON_PX_MAP[size],
|
|
1304
|
+
variant: isDisabled ? IconVariant.DISABLED : IconVariant.SECONDARY,
|
|
1305
|
+
className: NO_SHRINK_CSS,
|
|
1306
|
+
"aria-hidden": true
|
|
1307
|
+
}),
|
|
1308
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1309
|
+
as: "span",
|
|
1310
|
+
size: SELECT_INPUT_SIZE_TO_TEXT_SIZE_MAP[size],
|
|
1311
|
+
variant: isDisabled ? TextVariant.DISABLED : TextVariant.PRIMARY,
|
|
1312
|
+
lineClamp: 1,
|
|
1313
|
+
children: option.label
|
|
1314
|
+
})
|
|
1315
|
+
]
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
/** Choose one value from a list of options, in a field that opens a listbox. */
|
|
1319
|
+
var SelectInputInner = forwardRef(function SelectInput({ value, defaultValue = null, onChange, renderValue, size: sizeProp, isDisabled: isDisabledProp, className, style, ...rest }, ref) {
|
|
1320
|
+
const size = sizeProp ?? "MEDIUM";
|
|
1321
|
+
const isDisabled = isDisabledProp ?? false;
|
|
1322
|
+
const [currentValue, setValue] = useControlledState({
|
|
1323
|
+
value,
|
|
1324
|
+
defaultValue,
|
|
1325
|
+
onChange
|
|
1326
|
+
});
|
|
1327
|
+
return /* @__PURE__ */ jsx(SelectInputRoot, {
|
|
1328
|
+
...rest,
|
|
1329
|
+
ref,
|
|
1330
|
+
size,
|
|
1331
|
+
isDisabled,
|
|
1332
|
+
selectedIds: currentValue === null ? [] : [currentValue],
|
|
1333
|
+
isMultiple: false,
|
|
1334
|
+
onOptionSelect: setValue,
|
|
1335
|
+
onClear: () => setValue(null),
|
|
1336
|
+
renderSelection: (selected) => {
|
|
1337
|
+
const option = selected[0];
|
|
1338
|
+
if (option === void 0) return null;
|
|
1339
|
+
if (renderValue !== void 0) return renderValue(option);
|
|
1340
|
+
return /* @__PURE__ */ jsx(OptionValue, {
|
|
1341
|
+
option,
|
|
1342
|
+
size,
|
|
1343
|
+
isDisabled
|
|
1344
|
+
});
|
|
1345
|
+
},
|
|
1346
|
+
getSelectionText: (selected) => selected[0]?.label ?? "",
|
|
1347
|
+
limitReason: void 0,
|
|
1348
|
+
onCreate: void 0,
|
|
1349
|
+
...className === void 0 ? {} : { className },
|
|
1350
|
+
...style === void 0 ? {} : { style }
|
|
1351
|
+
});
|
|
1352
|
+
});
|
|
1353
|
+
SelectInputInner.displayName = "SelectInput";
|
|
1354
|
+
/** Choose one value from a list of options, in a field that opens a listbox. */
|
|
1355
|
+
var SelectInput = SelectInputInner;
|
|
494
1356
|
//#endregion
|
|
495
|
-
export {
|
|
1357
|
+
export { SelectInputRoot, SelectInputSize, SelectInputVariant, SelectInput as default };
|