@astryxdesign/core 0.4.7 → 0.5.0-canary.009bcb3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +161 -1
- package/dist/AlertDialog/AlertDialog.d.ts +1 -1
- package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
- package/dist/AlertDialog/AlertDialog.js +45 -20
- package/dist/AppShell/AppShell.d.ts.map +1 -1
- package/dist/AppShell/AppShell.js +3 -2
- package/dist/AspectRatio/AspectRatio.d.ts +24 -0
- package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
- package/dist/AspectRatio/AspectRatio.js +23 -4
- package/dist/Avatar/Avatar.d.ts +25 -1
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +58 -19
- package/dist/Avatar/index.d.ts +2 -2
- package/dist/Avatar/index.d.ts.map +1 -1
- package/dist/Avatar/index.js +1 -1
- package/dist/AvatarGroup/AvatarGroup.d.ts +9 -2
- package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroup.js +8 -4
- package/dist/AvatarGroup/AvatarGroupContext.d.ts +2 -1
- package/dist/AvatarGroup/AvatarGroupContext.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.js +10 -6
- package/dist/Banner/Banner.d.ts +49 -15
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +62 -20
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheet.js +17 -4
- package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetSwitcher.js +3 -2
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +11 -10
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +11 -6
- package/dist/ButtonGroup/ButtonGroup.d.ts +5 -1
- package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
- package/dist/ButtonGroup/ButtonGroup.js +26 -7
- package/dist/Calendar/Calendar.d.ts +7 -3
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +21 -23
- package/dist/Calendar/getInitialFocusDate.d.ts +46 -0
- package/dist/Calendar/getInitialFocusDate.d.ts.map +1 -0
- package/dist/Calendar/getInitialFocusDate.js +62 -0
- package/dist/Calendar/styles.d.ts +5 -14
- package/dist/Calendar/styles.d.ts.map +1 -1
- package/dist/Calendar/styles.js +15 -15
- package/dist/Card/Card.d.ts +12 -12
- package/dist/Card/Card.d.ts.map +1 -1
- package/dist/Card/Card.js +17 -9
- package/dist/Card/index.d.ts +35 -1
- package/dist/Card/index.d.ts.map +1 -1
- package/dist/Card/index.js +21 -1
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +3 -3
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +2 -3
- package/dist/Chat/ChatLayout.d.ts.map +1 -1
- package/dist/Chat/ChatLayout.js +3 -3
- package/dist/Chat/ChatMessageList.d.ts +22 -3
- package/dist/Chat/ChatMessageList.d.ts.map +1 -1
- package/dist/Chat/ChatMessageList.js +6 -3
- package/dist/Chat/ChatTokenizedText.d.ts +2 -0
- package/dist/Chat/ChatTokenizedText.d.ts.map +1 -1
- package/dist/Chat/ChatTokenizedText.js +11 -6
- package/dist/Chat/useChatComposerTokens.js +1 -1
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +10 -3
- package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
- package/dist/ClickableCard/ClickableCard.js +3 -3
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +15 -4
- package/dist/CodeBlock/highlightRanges.js +1 -1
- package/dist/CodeBlock/highlightStyles.js +13 -13
- package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteInput.js +3 -2
- package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteItem.js +3 -2
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +1 -3
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +4 -3
- package/dist/DateInput/DateInput.d.ts +43 -0
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +22 -2
- package/dist/DateInput/NativeDateField.d.ts +10 -0
- package/dist/DateInput/NativeDateField.d.ts.map +1 -0
- package/dist/DateInput/NativeDateField.js +494 -0
- package/dist/DateInput/TouchDateField.d.ts.map +1 -1
- package/dist/DateInput/TouchDateField.js +82 -23
- package/dist/DateInput/index.d.ts +1 -1
- package/dist/DateInput/index.d.ts.map +1 -1
- package/dist/DateInput/nativeDateSegments.d.ts +22 -0
- package/dist/DateInput/nativeDateSegments.d.ts.map +1 -0
- package/dist/DateInput/nativeDateSegments.js +111 -0
- package/dist/DateRangeInput/DateRangeInput.d.ts +4 -2
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts +21 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +406 -12
- package/dist/DateTimeInput/index.d.ts +1 -1
- package/dist/DateTimeInput/index.d.ts.map +1 -1
- package/dist/Dialog/Dialog.d.ts +21 -1
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +110 -58
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +4 -13
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.js +6 -5
- package/dist/Field/FieldLabel.d.ts.map +1 -1
- package/dist/Field/FieldLabel.js +9 -1
- package/dist/Field/InputClearButton.d.ts +9 -3
- package/dist/Field/InputClearButton.d.ts.map +1 -1
- package/dist/Field/InputClearButton.js +14 -3
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +3 -2
- package/dist/Heading/Heading.d.ts.map +1 -1
- package/dist/Heading/Heading.js +6 -3
- package/dist/HoverCard/HoverCard.d.ts +6 -0
- package/dist/HoverCard/HoverCard.d.ts.map +1 -1
- package/dist/HoverCard/HoverCard.js +104 -3
- package/dist/HoverCard/useHoverCard.d.ts +19 -2
- package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +74 -26
- package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
- package/dist/Indicator/CheckboxIndicator.js +6 -2
- package/dist/Item/Item.d.ts.map +1 -1
- package/dist/Item/Item.js +4 -2
- package/dist/Kbd/Kbd.d.ts.map +1 -1
- package/dist/Kbd/Kbd.js +2 -19
- package/dist/Layer/LayerDepthContext.d.ts +52 -0
- package/dist/Layer/LayerDepthContext.d.ts.map +1 -0
- package/dist/Layer/LayerDepthContext.js +66 -0
- package/dist/Layer/gestureCounter.d.ts +8 -0
- package/dist/Layer/gestureCounter.d.ts.map +1 -0
- package/dist/Layer/gestureCounter.js +53 -0
- package/dist/Layer/index.d.ts +3 -0
- package/dist/Layer/index.d.ts.map +1 -1
- package/dist/Layer/index.js +4 -0
- package/dist/Layer/layerStack.d.ts +98 -0
- package/dist/Layer/layerStack.d.ts.map +1 -0
- package/dist/Layer/layerStack.js +306 -0
- package/dist/Layer/useLayer.d.ts +23 -13
- package/dist/Layer/useLayer.d.ts.map +1 -1
- package/dist/Layer/useLayer.js +128 -8
- package/dist/Layer/useLayerDismissal.d.ts +93 -0
- package/dist/Layer/useLayerDismissal.d.ts.map +1 -0
- package/dist/Layer/useLayerDismissal.js +91 -0
- package/dist/Lightbox/Lightbox.d.ts +1 -0
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +103 -81
- package/dist/List/List.d.ts +1 -1
- package/dist/List/List.d.ts.map +1 -1
- package/dist/List/List.js +15 -4
- package/dist/Markdown/Markdown.d.ts +1 -1
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.js +20 -7
- package/dist/Markdown/index.d.ts +1 -1
- package/dist/Markdown/index.d.ts.map +1 -1
- package/dist/Markdown/parser.d.ts +38 -1
- package/dist/Markdown/parser.d.ts.map +1 -1
- package/dist/Markdown/parser.js +182 -32
- package/dist/MetadataList/MetadataListItem.d.ts +1 -1
- package/dist/MetadataList/MetadataListItem.d.ts.map +1 -1
- package/dist/MetadataList/MetadataListItem.js +4 -1
- package/dist/MobileNav/MobileNav.d.ts +6 -1
- package/dist/MobileNav/MobileNav.d.ts.map +1 -1
- package/dist/MobileNav/MobileNav.js +61 -36
- package/dist/MultiSelector/MultiSelector.d.ts +32 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +73 -15
- package/dist/MultiSelector/hooks.d.ts +7 -1
- package/dist/MultiSelector/hooks.d.ts.map +1 -1
- package/dist/MultiSelector/hooks.js +3 -2
- package/dist/MultiSelector/index.d.ts +1 -1
- package/dist/MultiSelector/index.d.ts.map +1 -1
- package/dist/NavIcon/NavIcon.d.ts.map +1 -1
- package/dist/NavIcon/NavIcon.js +5 -1
- package/dist/NavMenu/NavHeadingMenu.d.ts +1 -1
- package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
- package/dist/NavMenu/NavHeadingMenu.js +9 -5
- package/dist/NumberInput/NumberInput.d.ts +6 -1
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +54 -104
- package/dist/NumberInput/numberInputCommit.d.ts +34 -0
- package/dist/NumberInput/numberInputCommit.d.ts.map +1 -0
- package/dist/NumberInput/numberInputCommit.js +71 -0
- package/dist/NumberInput/numberParser.d.ts +64 -0
- package/dist/NumberInput/numberParser.d.ts.map +1 -0
- package/dist/NumberInput/numberParser.js +350 -0
- package/dist/Outline/Outline.d.ts.map +1 -1
- package/dist/Outline/Outline.js +3 -2
- package/dist/OverflowList/OverflowList.d.ts.map +1 -1
- package/dist/OverflowList/OverflowList.js +3 -2
- package/dist/Overlay/Overlay.d.ts.map +1 -1
- package/dist/Overlay/Overlay.js +3 -2
- package/dist/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +1 -9
- package/dist/Popover/usePopover.d.ts +7 -41
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +40 -21
- package/dist/PowerSearch/PowerSearch.d.ts +8 -2
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +19 -6
- package/dist/PowerSearch/PowerSearchEditPopover.d.ts +3 -1
- package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchEditPopover.js +28 -6
- package/dist/PowerSearch/PowerSearchValueEditor.d.ts +3 -1
- package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchValueEditor.js +110 -27
- package/dist/PowerSearch/types.d.ts +2 -0
- package/dist/PowerSearch/types.d.ts.map +1 -1
- package/dist/PowerSearch/usePowerSearchSource.d.ts +6 -1
- package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
- package/dist/PowerSearch/usePowerSearchSource.js +14 -5
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +18 -5
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +3 -2
- package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControl.js +3 -2
- package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
- package/dist/SelectableCard/SelectableCard.js +12 -3
- package/dist/Selector/Selector.d.ts +18 -1
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +91 -18
- package/dist/Selector/hooks.d.ts +7 -1
- package/dist/Selector/hooks.d.ts.map +1 -1
- package/dist/Selector/hooks.js +3 -2
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +4 -3
- package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
- package/dist/SideNav/SideNavHeading.js +8 -7
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +4 -3
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +37 -5
- package/dist/Spinner/Spinner.d.ts +3 -3
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +130 -78
- package/dist/StatusDot/StatusDot.d.ts.map +1 -1
- package/dist/StatusDot/StatusDot.js +6 -1
- package/dist/Stepper/Step.d.ts +140 -0
- package/dist/Stepper/Step.d.ts.map +1 -0
- package/dist/Stepper/Step.js +1045 -0
- package/dist/Stepper/StepStatus.d.ts +24 -0
- package/dist/Stepper/StepStatus.d.ts.map +1 -0
- package/dist/Stepper/StepStatus.js +1 -0
- package/dist/Stepper/Stepper.d.ts +93 -0
- package/dist/Stepper/Stepper.d.ts.map +1 -0
- package/dist/Stepper/Stepper.js +184 -0
- package/dist/Stepper/StepperContext.d.ts +41 -0
- package/dist/Stepper/StepperContext.d.ts.map +1 -0
- package/dist/Stepper/StepperContext.js +34 -0
- package/dist/Stepper/index.d.ts +8 -0
- package/dist/Stepper/index.d.ts.map +1 -0
- package/dist/Stepper/index.js +7 -0
- package/dist/Stepper/stepper.stylex.d.ts +15 -0
- package/dist/Stepper/stepper.stylex.d.ts.map +1 -0
- package/dist/Stepper/stepper.stylex.js +20 -0
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +8 -3
- package/dist/TabList/Tab.d.ts +15 -2
- package/dist/TabList/Tab.d.ts.map +1 -1
- package/dist/TabList/Tab.js +32 -7
- package/dist/TabList/TabList.d.ts +46 -7
- package/dist/TabList/TabList.d.ts.map +1 -1
- package/dist/TabList/TabList.js +353 -23
- package/dist/TabList/TabListContext.d.ts +11 -1
- package/dist/TabList/TabListContext.d.ts.map +1 -1
- package/dist/TabList/TabListContext.js +11 -2
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +3 -2
- package/dist/TabList/index.d.ts +2 -2
- package/dist/TabList/index.d.ts.map +1 -1
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +10 -2
- package/dist/Table/Table.d.ts.map +1 -1
- package/dist/Table/Table.js +3 -6
- package/dist/Table/TableRow.d.ts +16 -5
- package/dist/Table/TableRow.d.ts.map +1 -1
- package/dist/Table/TableRow.js +16 -5
- package/dist/Table/plugins/filtering/useTableFiltering.d.ts.map +1 -1
- package/dist/Table/plugins/filtering/useTableFiltering.js +73 -4
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts +26 -0
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +54 -11
- package/dist/Table/plugins/pagination/useTablePagination.d.ts +3 -0
- package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
- package/dist/Table/plugins/pagination/useTablePagination.js +21 -4
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +7 -2
- package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -1
- package/dist/Table/plugins/rowStatus/useTableRowStatus.js +10 -3
- package/dist/Table/plugins/selection/useTableSelection.d.ts +16 -0
- package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
- package/dist/Table/plugins/selection/useTableSelection.js +43 -5
- package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
- package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +3 -4
- package/dist/Table/types.d.ts +22 -4
- package/dist/Table/types.d.ts.map +1 -1
- package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
- package/dist/Table/useBaseTablePlugins.js +5 -0
- package/dist/Text/Text.d.ts.map +1 -1
- package/dist/Text/Text.js +6 -3
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +16 -10
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +3 -2
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +3 -2
- package/dist/Timestamp/Timestamp.d.ts +1 -1
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +15 -21
- package/dist/Toast/Toast.d.ts.map +1 -1
- package/dist/Toast/Toast.js +22 -7
- package/dist/Toast/ToastViewport.d.ts +1 -0
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +71 -18
- package/dist/Token/Token.d.ts +1 -1
- package/dist/Token/Token.d.ts.map +1 -1
- package/dist/Token/Token.js +20 -9
- package/dist/Token/TokenLink.d.ts +1 -1
- package/dist/Token/TokenLink.d.ts.map +1 -1
- package/dist/Token/TokenLink.js +7 -4
- package/dist/Tokenizer/Tokenizer.d.ts +14 -1
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +33 -21
- package/dist/Tooltip/Tooltip.d.ts +5 -0
- package/dist/Tooltip/Tooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.d.ts +5 -0
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +39 -24
- package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
- package/dist/TopNav/TopNavHeading.js +5 -4
- package/dist/TopNav/TopNavMegaMenu.d.ts +1 -1
- package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenu.js +31 -11
- package/dist/TopNav/TopNavMenu.d.ts +1 -1
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +22 -7
- package/dist/TreeList/TreeList.d.ts +1 -1
- package/dist/TreeList/TreeList.d.ts.map +1 -1
- package/dist/TreeList/TreeList.js +7 -2
- package/dist/Typeahead/BaseTypeahead.d.ts +35 -0
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +119 -37
- package/dist/Typeahead/Typeahead.d.ts +8 -1
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +5 -2
- package/dist/Typeahead/TypeaheadItem.d.ts +1 -1
- package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
- package/dist/Typeahead/TypeaheadItem.js +7 -2
- package/dist/astryx.css +113 -11
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useFocusTrap.d.ts +7 -4
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +60 -95
- package/dist/hooks/useHotkeys.d.ts.map +1 -1
- package/dist/hooks/useHotkeys.js +3 -18
- package/dist/hooks/useMergedRefs.d.ts +18 -0
- package/dist/hooks/useMergedRefs.d.ts.map +1 -0
- package/dist/hooks/useMergedRefs.js +25 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/theme/Theme.d.ts.map +1 -1
- package/dist/theme/Theme.js +46 -10
- package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
- package/dist/theme/derivedVarRegistry.js +24 -4
- package/dist/theme/generateThemeRules.d.ts +21 -0
- package/dist/theme/generateThemeRules.d.ts.map +1 -1
- package/dist/theme/generateThemeRules.js +26 -0
- package/dist/theme/syntax/tokens.d.ts +1 -1
- package/dist/theme/syntax/tokens.d.ts.map +1 -1
- package/dist/theme/syntax/tokens.js +5 -2
- package/dist/utils/groupItems.d.ts +5 -1
- package/dist/utils/groupItems.d.ts.map +1 -1
- package/dist/utils/groupItems.js +9 -9
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/isApplePlatform.d.ts +18 -0
- package/dist/utils/isApplePlatform.d.ts.map +1 -0
- package/dist/utils/isApplePlatform.js +37 -0
- package/dist/utils/sharedResizeObserver.d.ts.map +1 -1
- package/dist/utils/sharedResizeObserver.js +10 -1
- package/dist/utils/timeParser.d.ts +1 -1
- package/dist/utils/timeParser.d.ts.map +1 -1
- package/dist/utils/timeParser.js +26 -12
- package/locales/af-ZA.json +6 -6
- package/locales/ar-SA.json +6 -6
- package/locales/ca-ES.json +5 -5
- package/locales/cs-CZ.json +6 -6
- package/locales/da-DK.json +6 -6
- package/locales/de-DE.json +6 -6
- package/locales/el-GR.json +6 -6
- package/locales/en.json +163 -11
- package/locales/es-ES.json +5 -5
- package/locales/fi-FI.json +6 -6
- package/locales/fr-FR.json +6 -6
- package/locales/he-IL.json +6 -6
- package/locales/hu-HU.json +6 -6
- package/locales/it-IT.json +6 -6
- package/locales/ja-JP.json +6 -6
- package/locales/ko-KR.json +6 -6
- package/locales/nl-NL.json +6 -6
- package/locales/no-NO.json +6 -6
- package/locales/pl-PL.json +6 -6
- package/locales/pseudo.json +114 -0
- package/locales/pt-BR.json +6 -6
- package/locales/pt-PT.json +6 -6
- package/locales/ro-RO.json +6 -6
- package/locales/ru-RU.json +6 -6
- package/locales/sr-SP.json +6 -6
- package/locales/sv-SE.json +6 -6
- package/locales/tr-TR.json +6 -6
- package/locales/uk-UA.json +6 -6
- package/locales/vi-VN.json +6 -6
- package/locales/zh-CN.json +6 -6
- package/locales/zh-TW.json +6 -6
- package/package.json +8 -3
- package/src/AlertDialog/AlertDialog.doc.mjs +114 -30
- package/src/AlertDialog/AlertDialog.test.tsx +211 -2
- package/src/AlertDialog/AlertDialog.tsx +62 -20
- package/src/AppShell/AppShell.tsx +3 -2
- package/src/AspectRatio/AspectRatio.doc.mjs +6 -3
- package/src/AspectRatio/AspectRatio.test.tsx +57 -10
- package/src/AspectRatio/AspectRatio.tsx +40 -1
- package/src/Avatar/Avatar.doc.mjs +14 -3
- package/src/Avatar/Avatar.test.tsx +97 -0
- package/src/Avatar/Avatar.tsx +125 -52
- package/src/Avatar/index.ts +2 -2
- package/src/AvatarGroup/AvatarGroup.doc.mjs +8 -2
- package/src/AvatarGroup/AvatarGroup.test.tsx +24 -0
- package/src/AvatarGroup/AvatarGroup.tsx +15 -6
- package/src/AvatarGroup/AvatarGroupContext.ts +5 -3
- package/src/AvatarGroup/AvatarGroupOverflow.tsx +10 -4
- package/src/Banner/Banner.doc.mjs +22 -11
- package/src/Banner/Banner.test.tsx +177 -25
- package/src/Banner/Banner.tsx +93 -27
- package/src/BottomSheet/BottomSheet.test.tsx +123 -0
- package/src/BottomSheet/BottomSheet.tsx +17 -4
- package/src/BottomSheet/BottomSheetSwitcher.tsx +3 -2
- package/src/Breadcrumbs/BreadcrumbItem.tsx +9 -5
- package/src/Button/Button.doc.mjs +24 -1
- package/src/Button/Button.test.tsx +32 -4
- package/src/Button/Button.tsx +12 -5
- package/src/ButtonGroup/ButtonGroup.doc.mjs +10 -1
- package/src/ButtonGroup/ButtonGroup.test.tsx +404 -1
- package/src/ButtonGroup/ButtonGroup.tsx +23 -5
- package/src/Calendar/Calendar.doc.mjs +7 -6
- package/src/Calendar/Calendar.test.tsx +143 -0
- package/src/Calendar/Calendar.tsx +30 -26
- package/src/Calendar/getInitialFocusDate.test.ts +94 -0
- package/src/Calendar/getInitialFocusDate.ts +93 -0
- package/src/Calendar/styles.ts +49 -31
- package/src/Card/Card.doc.mjs +10 -7
- package/src/Card/Card.test.tsx +113 -19
- package/src/Card/Card.tsx +67 -89
- package/src/Card/index.ts +36 -1
- package/src/Carousel/Carousel.tsx +3 -2
- package/src/Chat/ChatComposerInput.tsx +5 -1
- package/src/Chat/ChatLayout.tsx +3 -2
- package/src/Chat/ChatMessageList.doc.mjs +9 -1
- package/src/Chat/ChatMessageList.test.tsx +46 -0
- package/src/Chat/ChatMessageList.tsx +28 -4
- package/src/Chat/ChatTokenizedText.tsx +17 -10
- package/src/Chat/useChatComposerTokens.ts +1 -1
- package/src/CheckboxInput/CheckboxInput.doc.mjs +2 -0
- package/src/CheckboxInput/CheckboxInput.test.tsx +15 -0
- package/src/CheckboxInput/CheckboxInput.tsx +8 -2
- package/src/ClickableCard/ClickableCard.tsx +3 -2
- package/src/CodeBlock/CodeBlock.doc.mjs +16 -4
- package/src/CodeBlock/CodeBlock.test.tsx +37 -0
- package/src/CodeBlock/CodeBlock.tsx +20 -4
- package/src/CodeBlock/highlightRanges.ts +1 -1
- package/src/CodeBlock/highlightStyles.ts +13 -13
- package/src/CommandPalette/CommandPaletteInput.tsx +3 -2
- package/src/CommandPalette/CommandPaletteItem.tsx +3 -2
- package/src/ComplexSelector/ComplexSelector.test.tsx +32 -0
- package/src/ComplexSelector/ComplexSelector.tsx +1 -3
- package/src/ContextMenu/ContextMenu.doc.mjs +5 -0
- package/src/ContextMenu/ContextMenu.tsx +4 -3
- package/src/DateInput/DateInput.doc.mjs +16 -0
- package/src/DateInput/DateInput.tsx +55 -2
- package/src/DateInput/DateInputTouch.test.tsx +146 -32
- package/src/DateInput/NativeDateField.test.tsx +635 -0
- package/src/DateInput/NativeDateField.tsx +641 -0
- package/src/DateInput/TouchDateField.tsx +108 -24
- package/src/DateInput/index.ts +1 -0
- package/src/DateInput/nativeDateSegments.ts +127 -0
- package/src/DateRangeInput/DateRangeInput.doc.mjs +2 -2
- package/src/DateRangeInput/DateRangeInput.test.tsx +22 -0
- package/src/DateRangeInput/DateRangeInput.tsx +4 -2
- package/src/DateTimeInput/DateTimeInput.doc.mjs +18 -0
- package/src/DateTimeInput/DateTimeInput.test.tsx +1088 -0
- package/src/DateTimeInput/DateTimeInput.tsx +556 -24
- package/src/DateTimeInput/index.ts +1 -0
- package/src/Dialog/Dialog.doc.mjs +8 -3
- package/src/Dialog/Dialog.test.tsx +429 -2
- package/src/Dialog/Dialog.tsx +133 -58
- package/src/DropdownMenu/DropdownMenu.doc.mjs +6 -0
- package/src/DropdownMenu/DropdownMenu.test.tsx +44 -10
- package/src/DropdownMenu/DropdownMenu.tsx +4 -13
- package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +8 -5
- package/src/DropdownMenu/DropdownMenuSubMenu.tsx +8 -11
- package/src/Field/Field.doc.mjs +13 -2
- package/src/Field/FieldLabel.test.tsx +29 -0
- package/src/Field/FieldLabel.tsx +7 -0
- package/src/Field/InputClearButton.test.tsx +15 -1
- package/src/Field/InputClearButton.tsx +28 -4
- package/src/FileInput/FileInput.tsx +3 -2
- package/src/Heading/Heading.test.tsx +23 -1
- package/src/Heading/Heading.tsx +6 -3
- package/src/HoverCard/HoverCard.doc.mjs +13 -2
- package/src/HoverCard/HoverCard.test.tsx +154 -4
- package/src/HoverCard/HoverCard.tsx +143 -3
- package/src/HoverCard/useHoverCard.tsx +104 -33
- package/src/Indicator/CheckboxIndicator.tsx +14 -8
- package/src/Indicator/Indicator.doc.mjs +2 -0
- package/src/Item/Item.tsx +5 -4
- package/src/Kbd/Kbd.tsx +2 -19
- package/src/Layer/LayerDepthContext.tsx +62 -0
- package/src/Layer/gestureCounter.ts +57 -0
- package/src/Layer/index.ts +9 -0
- package/src/Layer/layerDismissalFamilies.test.tsx +359 -0
- package/src/Layer/layerDismissalInvariants.test.tsx +417 -0
- package/src/Layer/layerStack.ts +362 -0
- package/src/Layer/useLayer.test.tsx +324 -15
- package/src/Layer/useLayer.tsx +164 -8
- package/src/Layer/useLayerDismissal.test.tsx +469 -0
- package/src/Layer/useLayerDismissal.ts +173 -0
- package/src/Layout/Layout.doc.mjs +2 -2
- package/src/Layout/__tests__/edgeCompensation.test.tsx +1 -1
- package/src/Lightbox/Lightbox.doc.mjs +12 -0
- package/src/Lightbox/Lightbox.tsx +124 -102
- package/src/Link/Link.doc.mjs +39 -0
- package/src/List/List.test.tsx +38 -0
- package/src/List/List.tsx +8 -1
- package/src/Markdown/Markdown.doc.mjs +5 -0
- package/src/Markdown/Markdown.test.tsx +37 -0
- package/src/Markdown/Markdown.tsx +16 -5
- package/src/Markdown/incremental.test.ts +16 -2
- package/src/Markdown/index.ts +1 -0
- package/src/Markdown/parser.perf.test.ts +96 -1
- package/src/Markdown/parser.test.ts +178 -2
- package/src/Markdown/parser.ts +253 -32
- package/src/MetadataList/MetadataList.test.tsx +75 -2
- package/src/MetadataList/MetadataListItem.tsx +5 -2
- package/src/MobileNav/MobileNav.tsx +62 -36
- package/src/MoreMenu/MoreMenu.doc.mjs +7 -1
- package/src/MultiSelector/MultiSelector.doc.mjs +49 -1
- package/src/MultiSelector/MultiSelector.test.tsx +311 -0
- package/src/MultiSelector/MultiSelector.tsx +151 -15
- package/src/MultiSelector/hooks.ts +9 -2
- package/src/MultiSelector/index.ts +1 -0
- package/src/NavIcon/NavIcon.doc.mjs +8 -2
- package/src/NavIcon/NavIcon.test.tsx +9 -0
- package/src/NavIcon/NavIcon.tsx +5 -1
- package/src/NavMenu/NavHeadingMenu.test.tsx +30 -0
- package/src/NavMenu/NavHeadingMenu.tsx +9 -5
- package/src/NumberInput/NumberInput.doc.mjs +31 -7
- package/src/NumberInput/NumberInput.test.tsx +436 -8
- package/src/NumberInput/NumberInput.tsx +62 -114
- package/src/NumberInput/numberInputCommit.test.ts +72 -0
- package/src/NumberInput/numberInputCommit.ts +103 -0
- package/src/NumberInput/numberParser.docblock.test.ts +234 -0
- package/src/NumberInput/numberParser.test.ts +222 -0
- package/src/NumberInput/numberParser.ts +403 -0
- package/src/Outline/Outline.tsx +3 -2
- package/src/OverflowList/OverflowList.tsx +3 -2
- package/src/Overlay/Overlay.tsx +3 -2
- package/src/Pagination/Pagination.test.tsx +3 -2
- package/src/Popover/Popover.test.tsx +14 -0
- package/src/Popover/Popover.tsx +1 -9
- package/src/Popover/usePopover.tsx +57 -32
- package/src/PowerSearch/PowerSearch.doc.mjs +160 -28
- package/src/PowerSearch/PowerSearch.test.tsx +240 -1
- package/src/PowerSearch/PowerSearch.tsx +34 -5
- package/src/PowerSearch/PowerSearchEditPopover.test.tsx +288 -0
- package/src/PowerSearch/PowerSearchEditPopover.tsx +29 -2
- package/src/PowerSearch/PowerSearchValueEditor.test.tsx +111 -1
- package/src/PowerSearch/PowerSearchValueEditor.tsx +124 -10
- package/src/PowerSearch/types.ts +2 -0
- package/src/PowerSearch/usePowerSearchSource.test.ts +82 -2
- package/src/PowerSearch/usePowerSearchSource.ts +13 -3
- package/src/ProgressBar/ProgressBar.doc.mjs +42 -6
- package/src/ProgressBar/ProgressBar.test.tsx +24 -0
- package/src/ProgressBar/ProgressBar.tsx +28 -8
- package/src/Resizable/ResizeHandle.tsx +3 -2
- package/src/SegmentedControl/SegmentedControl.tsx +3 -2
- package/src/SelectableCard/SelectableCard.doc.mjs +3 -0
- package/src/SelectableCard/SelectableCard.tsx +19 -2
- package/src/Selector/Selector.doc.mjs +26 -1
- package/src/Selector/Selector.test.tsx +346 -1
- package/src/Selector/Selector.tsx +148 -21
- package/src/Selector/hooks.ts +17 -2
- package/src/SideNav/SideNav.doc.mjs +6 -6
- package/src/SideNav/SideNav.tsx +4 -2
- package/src/SideNav/SideNavHeading.tsx +12 -12
- package/src/SideNav/SideNavItem.tsx +4 -2
- package/src/Slider/Slider.test.tsx +106 -1
- package/src/Slider/Slider.tsx +55 -3
- package/src/Spinner/Spinner.test.tsx +147 -1
- package/src/Spinner/Spinner.tsx +120 -101
- package/src/StatusDot/StatusDot.doc.mjs +8 -2
- package/src/StatusDot/StatusDot.test.tsx +9 -0
- package/src/StatusDot/StatusDot.tsx +7 -1
- package/src/Stepper/Step.doc.mjs +98 -0
- package/src/Stepper/Step.tsx +1638 -0
- package/src/Stepper/StepStatus.ts +26 -0
- package/src/Stepper/Stepper.doc.mjs +370 -0
- package/src/Stepper/Stepper.test.tsx +1024 -0
- package/src/Stepper/Stepper.tsx +258 -0
- package/src/Stepper/StepperContext.ts +71 -0
- package/src/Stepper/index.ts +18 -0
- package/src/Stepper/stepper.stylex.ts +19 -0
- package/src/Switch/Switch.doc.mjs +2 -0
- package/src/Switch/Switch.test.tsx +10 -0
- package/src/Switch/Switch.tsx +6 -2
- package/src/TabList/Tab.doc.mjs +13 -1
- package/src/TabList/Tab.tsx +67 -7
- package/src/TabList/TabList.doc.mjs +22 -6
- package/src/TabList/TabList.test.tsx +559 -0
- package/src/TabList/TabList.tsx +490 -22
- package/src/TabList/TabListContext.ts +13 -2
- package/src/TabList/TabMenu.tsx +3 -2
- package/src/TabList/index.ts +6 -2
- package/src/Table/BaseTable.tsx +12 -4
- package/src/Table/Table.doc.mjs +9 -2
- package/src/Table/Table.test.tsx +132 -14
- package/src/Table/Table.tsx +3 -8
- package/src/Table/TableBody.doc.mjs +40 -0
- package/src/Table/TableFooter.doc.mjs +40 -0
- package/src/Table/TableHeader.doc.mjs +40 -0
- package/src/Table/TableRow.tsx +16 -5
- package/src/Table/plugins/filtering/useTableFiltering.test.tsx +44 -6
- package/src/Table/plugins/filtering/useTableFiltering.tsx +100 -4
- package/src/Table/plugins/groupedRows/useTableGroupedRows-perf.test.tsx +112 -0
- package/src/Table/plugins/groupedRows/useTableGroupedRows.test.tsx +100 -0
- package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +67 -12
- package/src/Table/plugins/pagination/useTablePagination.test.tsx +34 -4
- package/src/Table/plugins/pagination/useTablePagination.tsx +35 -3
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +9 -2
- package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +13 -7
- package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +12 -3
- package/src/Table/plugins/selection/useTableSelection.test.tsx +138 -0
- package/src/Table/plugins/selection/useTableSelection.tsx +63 -7
- package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +1 -2
- package/src/Table/types.ts +22 -4
- package/src/Table/useBaseTablePlugins.ts +5 -0
- package/src/Table/useTableGroupedRows.doc.mjs +5 -4
- package/src/Table/useTablePagination.doc.mjs +7 -0
- package/src/Table/useTableRowStatus.doc.mjs +1 -1
- package/src/Table/useTableSelection.doc.mjs +31 -0
- package/src/Text/Text.test.tsx +67 -1
- package/src/Text/Text.tsx +6 -3
- package/src/TextArea/TextArea.doc.mjs +12 -2
- package/src/TextArea/TextArea.test.tsx +11 -0
- package/src/TextArea/TextArea.tsx +37 -24
- package/src/TextInput/TextInput.doc.mjs +10 -0
- package/src/TextInput/TextInput.tsx +3 -2
- package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
- package/src/TimeInput/TimeInput.tsx +2 -2
- package/src/Timestamp/Timestamp.test.tsx +58 -0
- package/src/Timestamp/Timestamp.tsx +12 -10
- package/src/Toast/Toast.doc.mjs +26 -12
- package/src/Toast/Toast.tsx +28 -4
- package/src/Toast/ToastViewport.test.tsx +536 -8
- package/src/Toast/ToastViewport.tsx +81 -12
- package/src/Token/Token.test.tsx +95 -0
- package/src/Token/Token.tsx +15 -10
- package/src/Token/TokenLink.tsx +14 -5
- package/src/Tokenizer/Tokenizer.doc.mjs +43 -3
- package/src/Tokenizer/Tokenizer.test.tsx +161 -0
- package/src/Tokenizer/Tokenizer.tsx +53 -24
- package/src/Toolbar/Toolbar.doc.mjs +5 -0
- package/src/Tooltip/Tooltip.doc.mjs +5 -0
- package/src/Tooltip/Tooltip.test.tsx +32 -0
- package/src/Tooltip/Tooltip.tsx +5 -0
- package/src/Tooltip/useTooltip.tsx +47 -24
- package/src/TopNav/TopNavHeading.tsx +5 -4
- package/src/TopNav/TopNavMegaMenu.test.tsx +104 -0
- package/src/TopNav/TopNavMegaMenu.tsx +47 -5
- package/src/TopNav/TopNavMenu.test.tsx +133 -1
- package/src/TopNav/TopNavMenu.tsx +38 -7
- package/src/TreeList/TreeList.test.tsx +49 -0
- package/src/TreeList/TreeList.tsx +7 -2
- package/src/Typeahead/BaseTypeahead.doc.mjs +24 -0
- package/src/Typeahead/BaseTypeahead.tsx +181 -39
- package/src/Typeahead/Typeahead.doc.mjs +11 -0
- package/src/Typeahead/Typeahead.test.tsx +203 -0
- package/src/Typeahead/Typeahead.tsx +12 -2
- package/src/Typeahead/TypeaheadItem.test.tsx +43 -0
- package/src/Typeahead/TypeaheadItem.tsx +8 -1
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useAnnounce.doc.mjs +96 -0
- package/src/hooks/useContainerReveal.doc.mjs +2 -2
- package/src/hooks/useDevWarning.doc.mjs +96 -0
- package/src/hooks/useFocusTrap.test.tsx +14 -0
- package/src/hooks/useFocusTrap.ts +62 -111
- package/src/hooks/useFocusTrapEscapeShim.test.tsx +165 -0
- package/src/hooks/useHotkeys.ts +3 -18
- package/src/hooks/useIndicatorFocusRing.doc.mjs +104 -0
- package/src/hooks/useInputStatusIcon.doc.mjs +142 -0
- package/src/hooks/useInteractiveRole.doc.mjs +119 -0
- package/src/hooks/useLongPress.doc.mjs +123 -0
- package/src/hooks/useMergedRefs.doc.mjs +79 -0
- package/src/hooks/useMergedRefs.test.tsx +62 -0
- package/src/hooks/useMergedRefs.ts +36 -0
- package/src/hooks/useTypeahead.doc.mjs +153 -0
- package/src/index.ts +1 -0
- package/src/raw-import.d.ts +14 -0
- package/src/theme/MediaTheme.doc.mjs +5 -5
- package/src/theme/Theme.test.tsx +104 -2
- package/src/theme/Theme.tsx +49 -14
- package/src/theme/defineTheme.test.ts +23 -0
- package/src/theme/derivedVarRegistry.test.ts +26 -3
- package/src/theme/derivedVarRegistry.ts +23 -4
- package/src/theme/generateThemeRules.test.ts +99 -1
- package/src/theme/generateThemeRules.ts +28 -0
- package/src/theme/syntax/tokens.ts +5 -2
- package/src/utils/groupItems.ts +12 -6
- package/src/utils/index.ts +1 -1
- package/src/utils/isApplePlatform.test.ts +61 -0
- package/src/utils/isApplePlatform.ts +37 -0
- package/src/utils/sharedResizeObserver.ts +10 -2
- package/src/utils/timeParser.test.ts +6 -1
- package/src/utils/timeParser.ts +35 -13
- package/src/__tests__/inputWidthContract.test.tsx +0 -69
|
@@ -29,7 +29,7 @@ import React, {
|
|
|
29
29
|
type ReactNode,
|
|
30
30
|
} from 'react';
|
|
31
31
|
import * as stylex from '@stylexjs/stylex';
|
|
32
|
-
import {
|
|
32
|
+
import {usePopoverInternal} from '../Popover/usePopover';
|
|
33
33
|
import {useTooltip} from '../Tooltip';
|
|
34
34
|
import {Icon, renderIconSlot, type IconType} from '../Icon';
|
|
35
35
|
import {useIndicator} from '../Indicator';
|
|
@@ -37,7 +37,6 @@ import type {IndicatorPosition} from '../Indicator';
|
|
|
37
37
|
import type {IconName} from '../Icon';
|
|
38
38
|
import {
|
|
39
39
|
Field,
|
|
40
|
-
InputClearButton,
|
|
41
40
|
inputStatusBorderStyles,
|
|
42
41
|
inputStatusHoverShadowStyles,
|
|
43
42
|
inputWrapperStyles,
|
|
@@ -45,7 +44,8 @@ import {
|
|
|
45
44
|
} from '../Field';
|
|
46
45
|
import {Divider} from '../Divider';
|
|
47
46
|
import {layerAnimations} from '../Layer/layerAnimations.stylex';
|
|
48
|
-
import type
|
|
47
|
+
import {useKeepLayerOpenProps, type LayerPlacement} from '../Layer/useLayer';
|
|
48
|
+
import {InternalInputClearButton} from '../Field/InputClearButton';
|
|
49
49
|
import {Spinner} from '../Spinner';
|
|
50
50
|
import {PanelSearchInput} from '../Field/PanelSearchInput';
|
|
51
51
|
import {useAnnounce} from '../hooks/useAnnounce';
|
|
@@ -648,6 +648,25 @@ interface SelectorPropsBase<
|
|
|
648
648
|
*/
|
|
649
649
|
searchPlaceholder?: string;
|
|
650
650
|
|
|
651
|
+
/**
|
|
652
|
+
* Content shown in the panel when there are no options to show, and
|
|
653
|
+
* announced in a polite live region when the panel opens. Not shown while
|
|
654
|
+
* `isLoading` — the options have not arrived yet.
|
|
655
|
+
* @default 'No options'
|
|
656
|
+
*/
|
|
657
|
+
emptyText?: ReactNode;
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Content shown in the panel when a search query matches no options, and
|
|
661
|
+
* announced in a polite live region at the same time.
|
|
662
|
+
*
|
|
663
|
+
* The panel message is `role="presentation"`, so the live region is the only
|
|
664
|
+
* route to assistive tech: a string is announced verbatim, a richer node
|
|
665
|
+
* falls back to the default text since it cannot be spoken.
|
|
666
|
+
* @default 'No results found'
|
|
667
|
+
*/
|
|
668
|
+
emptySearchText?: ReactNode;
|
|
669
|
+
|
|
651
670
|
/**
|
|
652
671
|
* Position placement relative to the trigger.
|
|
653
672
|
*
|
|
@@ -794,6 +813,8 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
794
813
|
indicatorPosition = 'end',
|
|
795
814
|
hasSearch = false,
|
|
796
815
|
searchPlaceholder: searchPlaceholderFromProps,
|
|
816
|
+
emptyText: emptyTextFromProps,
|
|
817
|
+
emptySearchText: emptySearchTextFromProps,
|
|
797
818
|
placement,
|
|
798
819
|
isDefaultOpen = false,
|
|
799
820
|
'data-testid': testId,
|
|
@@ -802,12 +823,16 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
802
823
|
className,
|
|
803
824
|
style,
|
|
804
825
|
hasClear: hasClearProp,
|
|
826
|
+
id,
|
|
805
827
|
...rest
|
|
806
828
|
} = props as SelectorPropsClearable<T>;
|
|
807
829
|
const isEffectivelyRequired = useResolvedRequired({isRequired, isOptional});
|
|
808
830
|
const placeholder = placeholderFromProps ?? t('@astryx.selector.placeholder');
|
|
809
831
|
const searchPlaceholder =
|
|
810
832
|
searchPlaceholderFromProps ?? t('@astryx.selector.searchPlaceholder');
|
|
833
|
+
const emptyText = emptyTextFromProps ?? t('@astryx.selector.empty');
|
|
834
|
+
const emptySearchText =
|
|
835
|
+
emptySearchTextFromProps ?? t('@astryx.selector.emptySearchResults');
|
|
811
836
|
const hasClear = hasClearProp === true;
|
|
812
837
|
const size = useSize(sizeProp, 'md');
|
|
813
838
|
const effectiveStatusVariant =
|
|
@@ -817,7 +842,10 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
817
842
|
|
|
818
843
|
// Normalize null to undefined for internal use (null is the clear sentinel)
|
|
819
844
|
const normalizedValue = value === null ? undefined : value;
|
|
820
|
-
const
|
|
845
|
+
const generatedTriggerId = useId();
|
|
846
|
+
// A caller's `id` lands on the trigger either way, so the internal identity
|
|
847
|
+
// has to be that same value or the label and listbox point at nothing.
|
|
848
|
+
const triggerId = id ?? generatedTriggerId;
|
|
821
849
|
const listboxId = useId();
|
|
822
850
|
const descriptionId = useId();
|
|
823
851
|
const statusMessageId = useId();
|
|
@@ -831,6 +859,9 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
831
859
|
const inputGroup = useInputGroup();
|
|
832
860
|
|
|
833
861
|
const [searchQuery, setSearchQuery] = useState('');
|
|
862
|
+
// Mirrors searchQuery for the seed path, which runs before focus reaches the
|
|
863
|
+
// input and so cannot read the rendered state.
|
|
864
|
+
const searchQueryRef = useRef('');
|
|
834
865
|
// A typed query shows the search row's clear (✕) button, which becomes
|
|
835
866
|
// the next tab stop after the search input.
|
|
836
867
|
const hasQuery = searchQuery.length > 0;
|
|
@@ -840,6 +871,17 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
840
871
|
const isBusy = isLoading || optimisticValue !== normalizedValue;
|
|
841
872
|
const announce = useAnnounce();
|
|
842
873
|
|
|
874
|
+
// The panel's empty message is role="presentation" and reaches assistive tech
|
|
875
|
+
// only through this live region, so the region has to speak whatever the
|
|
876
|
+
// panel shows. A ReactNode override cannot be spoken; fall back to the
|
|
877
|
+
// catalog copy for that case rather than announcing nothing.
|
|
878
|
+
const emptyAnnouncement =
|
|
879
|
+
typeof emptyText === 'string' ? emptyText : t('@astryx.selector.empty');
|
|
880
|
+
const emptySearchAnnouncement =
|
|
881
|
+
typeof emptySearchText === 'string'
|
|
882
|
+
? emptySearchText
|
|
883
|
+
: t('@astryx.selector.emptySearchResults');
|
|
884
|
+
|
|
843
885
|
// Disabled-reason tooltip. Disabled controls swallow pointer events, so the
|
|
844
886
|
// tooltip listeners attach to the trigger container (which already exists)
|
|
845
887
|
// and the trigger button stays perceivable via aria-disabled instead of the
|
|
@@ -907,6 +949,7 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
907
949
|
// Layer for dropdown positioning
|
|
908
950
|
const handleLayerHide = useCallback(() => {
|
|
909
951
|
setSearchQuery('');
|
|
952
|
+
searchQueryRef.current = '';
|
|
910
953
|
resetTypeaheadRef.current();
|
|
911
954
|
// Clear any lingering result count when the popover closes so stale status
|
|
912
955
|
// text does not linger in the a11y tree.
|
|
@@ -914,7 +957,7 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
914
957
|
triggerRef.current?.focus();
|
|
915
958
|
}, [announce]);
|
|
916
959
|
|
|
917
|
-
const popover =
|
|
960
|
+
const popover = usePopoverInternal({
|
|
918
961
|
onHide: handleLayerHide,
|
|
919
962
|
hasLightDismiss: true,
|
|
920
963
|
hasCloseButton: false,
|
|
@@ -926,6 +969,7 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
926
969
|
// `usePopover` owns — not on the scrolling list inside it.
|
|
927
970
|
surfaceTarget: 'selector-popup',
|
|
928
971
|
});
|
|
972
|
+
const keepOpenProps = useKeepLayerOpenProps(popover.id, popover.isOpen);
|
|
929
973
|
|
|
930
974
|
// Open dropdown on mount when isDefaultOpen is true
|
|
931
975
|
useEffect(() => {
|
|
@@ -935,28 +979,76 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
935
979
|
// eslint-disable-next-line @eslint-react/exhaustive-deps -- mount-only: isDefaultOpen is not reactive
|
|
936
980
|
}, []);
|
|
937
981
|
|
|
938
|
-
// Announce the filtered result count from the query-change
|
|
982
|
+
// Announce the filtered result count from the query-change handlers (matching
|
|
939
983
|
// BaseTypeahead) rather than a reactive effect: computing the count for the
|
|
940
984
|
// next query here fires the announcement exactly once per keystroke and does
|
|
941
|
-
// not re-speak on unrelated re-renders.
|
|
942
|
-
|
|
985
|
+
// not re-speak on unrelated re-renders. Split out from handleSearchChange so
|
|
986
|
+
// the type-to-open seed below reaches the same live region — a query the user
|
|
987
|
+
// typed is a query however it arrived.
|
|
988
|
+
const announceSearchResults = useCallback(
|
|
943
989
|
(nextQuery: string) => {
|
|
944
|
-
setSearchQuery(nextQuery);
|
|
945
990
|
if (nextQuery.length === 0) {
|
|
946
991
|
// Emptying the query clears the region rather than announcing a count.
|
|
947
992
|
announce('');
|
|
948
993
|
return;
|
|
949
994
|
}
|
|
995
|
+
// While isLoading the panel deliberately shows nothing, so announcing a
|
|
996
|
+
// result would put a claim in the one channel the screen has gone quiet
|
|
997
|
+
// for.
|
|
998
|
+
if (isLoading) {
|
|
999
|
+
announce('');
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
950
1002
|
const count = filterOptionsByQuery(selectableItems, nextQuery).length;
|
|
951
1003
|
announce(
|
|
952
1004
|
count === 0
|
|
953
|
-
?
|
|
1005
|
+
? emptySearchAnnouncement
|
|
954
1006
|
: t('@astryx.selector.resultCount', {count}),
|
|
955
1007
|
);
|
|
956
1008
|
},
|
|
957
|
-
[announce, selectableItems, t],
|
|
1009
|
+
[announce, isLoading, selectableItems, emptySearchAnnouncement, t],
|
|
1010
|
+
);
|
|
1011
|
+
|
|
1012
|
+
const handleSearchChange = useCallback(
|
|
1013
|
+
(nextQuery: string) => {
|
|
1014
|
+
setSearchQuery(nextQuery);
|
|
1015
|
+
searchQueryRef.current = nextQuery;
|
|
1016
|
+
announceSearchResults(nextQuery);
|
|
1017
|
+
},
|
|
1018
|
+
[announceSearchResults],
|
|
958
1019
|
);
|
|
959
1020
|
|
|
1021
|
+
// The panel's empty message is role="presentation", so this region is the
|
|
1022
|
+
// only route to assistive tech. It has to watch the STATE rather than the
|
|
1023
|
+
// open event: the panel can become empty either on open or when a fetch
|
|
1024
|
+
// lands with nothing in it, and an open-only announcement leaves the second
|
|
1025
|
+
// case silent while the message sits on screen. The ref makes it fire once
|
|
1026
|
+
// per arrival at that state rather than on every re-render.
|
|
1027
|
+
const announcedEmptyRef = useRef<string | null>(null);
|
|
1028
|
+
useEffect(() => {
|
|
1029
|
+
const isPanelEmpty =
|
|
1030
|
+
popover.isOpen &&
|
|
1031
|
+
!isLoading &&
|
|
1032
|
+
searchQuery === '' &&
|
|
1033
|
+
selectableItems.length === 0;
|
|
1034
|
+
if (!isPanelEmpty) {
|
|
1035
|
+
announcedEmptyRef.current = null;
|
|
1036
|
+
return;
|
|
1037
|
+
}
|
|
1038
|
+
if (announcedEmptyRef.current === emptyAnnouncement) {
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
announcedEmptyRef.current = emptyAnnouncement;
|
|
1042
|
+
announce(emptyAnnouncement);
|
|
1043
|
+
}, [
|
|
1044
|
+
popover.isOpen,
|
|
1045
|
+
isLoading,
|
|
1046
|
+
searchQuery,
|
|
1047
|
+
selectableItems.length,
|
|
1048
|
+
emptyAnnouncement,
|
|
1049
|
+
announce,
|
|
1050
|
+
]);
|
|
1051
|
+
|
|
960
1052
|
// Calculate offset to position selected item over trigger. Explicit
|
|
961
1053
|
// placement opts out of the selector-specific overlay behavior and uses the
|
|
962
1054
|
// standard layer positioning API instead.
|
|
@@ -992,10 +1084,20 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
992
1084
|
}, [onChange, changeAction, startTransition, setOptimisticValue]);
|
|
993
1085
|
|
|
994
1086
|
// Type-to-find appends to the query rather than replacing it: characters
|
|
995
|
-
// typed before focus reaches the search input must not be dropped.
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1087
|
+
// typed before focus reaches the search input must not be dropped. The ref
|
|
1088
|
+
// is what makes that safe without a state updater — it advances
|
|
1089
|
+
// synchronously, so a second character seeded in the same tick still sees
|
|
1090
|
+
// the first, and the announcement can be computed here rather than inside a
|
|
1091
|
+
// setState callback.
|
|
1092
|
+
const appendSearchQuery = useCallback(
|
|
1093
|
+
(char: string) => {
|
|
1094
|
+
const nextQuery = searchQueryRef.current + char;
|
|
1095
|
+
searchQueryRef.current = nextQuery;
|
|
1096
|
+
setSearchQuery(nextQuery);
|
|
1097
|
+
announceSearchResults(nextQuery);
|
|
1098
|
+
},
|
|
1099
|
+
[announceSearchResults],
|
|
1100
|
+
);
|
|
999
1101
|
|
|
1000
1102
|
const commitValue = useCallback(
|
|
1001
1103
|
(newValue: string) => {
|
|
@@ -1021,6 +1123,7 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
1021
1123
|
onItemMouseEnter,
|
|
1022
1124
|
} = useCombobox({
|
|
1023
1125
|
selectableItems: filteredItems,
|
|
1126
|
+
wasJustDismissed: popover.wasJustDismissed,
|
|
1024
1127
|
// The optimistic value, not the raw prop: with a pending changeAction the
|
|
1025
1128
|
// prop still holds the old selection, so the popup would open with the
|
|
1026
1129
|
// highlight on it and Delete/Backspace could clear a value the action has
|
|
@@ -1313,8 +1416,11 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
1313
1416
|
const renderOptions = useCallback(() => {
|
|
1314
1417
|
const isSearching = hasSearch && Boolean(searchQuery);
|
|
1315
1418
|
|
|
1316
|
-
// Nothing
|
|
1317
|
-
|
|
1419
|
+
// Nothing to show — either the query matched nothing, or no options were
|
|
1420
|
+
// given at all. Both render the same slot with different copy. While
|
|
1421
|
+
// isLoading the options have not arrived yet, so asserting either would be
|
|
1422
|
+
// a claim the component cannot make; the trigger's spinner covers it.
|
|
1423
|
+
if (filteredItems.length === 0 && !isLoading) {
|
|
1318
1424
|
// role="presentation" keeps the message out of the listbox's
|
|
1319
1425
|
// accessibility tree (role="listbox" only permits option/group
|
|
1320
1426
|
// children); the no-results outcome is announced via the
|
|
@@ -1327,7 +1433,7 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
1327
1433
|
themeProps('selector-empty-state'),
|
|
1328
1434
|
stylex.props(styles.emptyState),
|
|
1329
1435
|
)}>
|
|
1330
|
-
|
|
1436
|
+
{isSearching ? emptySearchText : emptyText}
|
|
1331
1437
|
</div>,
|
|
1332
1438
|
];
|
|
1333
1439
|
}
|
|
@@ -1344,7 +1450,17 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
1344
1450
|
if (isSearching) {
|
|
1345
1451
|
continue;
|
|
1346
1452
|
}
|
|
1347
|
-
|
|
1453
|
+
// role="listbox" only permits option/group children; the divider
|
|
1454
|
+
// carries no information the options don't, so it's hidden from the
|
|
1455
|
+
// accessibility tree entirely rather than exposing role="separator"
|
|
1456
|
+
// as a disallowed listbox child (axe aria-required-children).
|
|
1457
|
+
elements.push(
|
|
1458
|
+
<Divider
|
|
1459
|
+
key={`divider-${i}`}
|
|
1460
|
+
aria-hidden="true"
|
|
1461
|
+
xstyle={styles.divider}
|
|
1462
|
+
/>,
|
|
1463
|
+
);
|
|
1348
1464
|
} else if (isSection(option)) {
|
|
1349
1465
|
const sectionItems: ReactNode[] = [];
|
|
1350
1466
|
for (const opt of option.options) {
|
|
@@ -1391,7 +1507,16 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
1391
1507
|
}
|
|
1392
1508
|
|
|
1393
1509
|
return elements;
|
|
1394
|
-
}, [
|
|
1510
|
+
}, [
|
|
1511
|
+
options,
|
|
1512
|
+
renderItem,
|
|
1513
|
+
hasSearch,
|
|
1514
|
+
searchQuery,
|
|
1515
|
+
filteredItems,
|
|
1516
|
+
isLoading,
|
|
1517
|
+
emptyText,
|
|
1518
|
+
emptySearchText,
|
|
1519
|
+
]);
|
|
1395
1520
|
|
|
1396
1521
|
// The detached message box renders its own leading status icon, so the
|
|
1397
1522
|
// on-field icon would duplicate it — keep the chevron indicator instead.
|
|
@@ -1529,7 +1654,8 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
1529
1654
|
)}
|
|
1530
1655
|
{isBusy && <Spinner size="sm" />}
|
|
1531
1656
|
{hasClear && value != null && !isDisabled && (
|
|
1532
|
-
<
|
|
1657
|
+
<InternalInputClearButton
|
|
1658
|
+
{...keepOpenProps}
|
|
1533
1659
|
label={t('@astryx.selector.clearLabel', {label})}
|
|
1534
1660
|
onClick={handleClear}
|
|
1535
1661
|
iconClassName={stableClassName('selector-clear-icon')}
|
|
@@ -1548,6 +1674,7 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
1548
1674
|
type="button"
|
|
1549
1675
|
aria-label={t(STATUS_BUTTON_LABEL_KEY[status.type])}
|
|
1550
1676
|
aria-describedby={statusTooltip.describedBy}
|
|
1677
|
+
{...keepOpenProps}
|
|
1551
1678
|
onClick={e => e.stopPropagation()}
|
|
1552
1679
|
{...stylex.props(
|
|
1553
1680
|
focusOutlineStyles.focusVisible,
|
package/src/Selector/hooks.ts
CHANGED
|
@@ -176,6 +176,12 @@ interface UseComboboxOptions {
|
|
|
176
176
|
* lands in the search input, which then owns its own typing.
|
|
177
177
|
*/
|
|
178
178
|
onSearchSeed?: (char: string) => void;
|
|
179
|
+
/**
|
|
180
|
+
* Whether the browser's light dismiss just closed the popup. The trigger
|
|
181
|
+
* click that follows belongs to that same press, so acting on it would
|
|
182
|
+
* reopen the popup the user just closed.
|
|
183
|
+
*/
|
|
184
|
+
wasJustDismissed?: () => boolean;
|
|
179
185
|
listboxId: string;
|
|
180
186
|
}
|
|
181
187
|
|
|
@@ -208,6 +214,7 @@ export function useCombobox({
|
|
|
208
214
|
onSelect,
|
|
209
215
|
onClear,
|
|
210
216
|
onSearchSeed,
|
|
217
|
+
wasJustDismissed,
|
|
211
218
|
listboxId,
|
|
212
219
|
}: UseComboboxOptions): UseComboboxResult {
|
|
213
220
|
const [highlightedIndex, setHighlightedIndex] = useState<number>(-1);
|
|
@@ -244,7 +251,7 @@ export function useCombobox({
|
|
|
244
251
|
);
|
|
245
252
|
|
|
246
253
|
const onTriggerClick = useCallback(() => {
|
|
247
|
-
if (isDisabled) {
|
|
254
|
+
if (isDisabled || wasJustDismissed?.()) {
|
|
248
255
|
return;
|
|
249
256
|
}
|
|
250
257
|
if (isOpen) {
|
|
@@ -256,7 +263,15 @@ export function useCombobox({
|
|
|
256
263
|
setHighlightedIndex(selectedIndex >= 0 ? selectedIndex : 0);
|
|
257
264
|
}
|
|
258
265
|
}
|
|
259
|
-
}, [
|
|
266
|
+
}, [
|
|
267
|
+
isDisabled,
|
|
268
|
+
wasJustDismissed,
|
|
269
|
+
isOpen,
|
|
270
|
+
onOpen,
|
|
271
|
+
closeAndReset,
|
|
272
|
+
findSelectedIndex,
|
|
273
|
+
hasSearch,
|
|
274
|
+
]);
|
|
260
275
|
|
|
261
276
|
const onItemMouseEnter = useCallback(
|
|
262
277
|
(item: SelectorOptionData, index: number) => {
|
|
@@ -131,8 +131,8 @@ export const docs = {
|
|
|
131
131
|
bestPractices: [
|
|
132
132
|
{guidance: true, description: 'Use sections to group related navigation items and help users scan for their destination.'},
|
|
133
133
|
{guidance: true, description: 'Pair outline and filled icon variants so the selected state is visually distinct.'},
|
|
134
|
-
{guidance: true, description: 'Mark the current page with isSelected
|
|
135
|
-
{guidance: true, description: 'SideNav renders a navigation landmark, and a collapsible item follows the WAI-ARIA APG Disclosure pattern (https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/): the toggle carries aria-expanded and aria-controls, and the group it owns is inert while collapsed. Keep item labels short
|
|
134
|
+
{guidance: true, description: 'Mark the current page with isSelected: it sets aria-current="page", so the current destination is announced rather than carried by color alone.'},
|
|
135
|
+
{guidance: true, description: 'SideNav renders a navigation landmark, and a collapsible item follows the WAI-ARIA APG Disclosure pattern (https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/): the toggle carries aria-expanded and aria-controls, and the group it owns is inert while collapsed. Keep item labels short; they are the accessible name in both expanded and icon-only modes.'},
|
|
136
136
|
{guidance: true, description: 'While the nav is collapsed, an item with children shows them in a submenu flyout. On a device that can hover, pointing at the item opens it after a short delay and moving away closes it; a flyout opened by clicking stays open until it is dismissed. On touch, it opens on tap. Do not put an action in there that has no other route to it.'},
|
|
137
137
|
{guidance: false, description: 'Include a SideNavHeading when a TopNav is already providing app identity; this duplicates branding.'},
|
|
138
138
|
{guidance: false, description: 'Use for filtering content; use tabs or filter buttons instead.'},
|
|
@@ -153,8 +153,8 @@ export const docsZh = {
|
|
|
153
153
|
bestPractices: [
|
|
154
154
|
{guidance: true, description: 'Use sections to group related navigation items and help users scan for their destination.'},
|
|
155
155
|
{guidance: true, description: 'Pair outline and filled icon variants so the selected state is visually distinct.'},
|
|
156
|
-
{guidance: true, description: 'Mark the current page with isSelected
|
|
157
|
-
{guidance: true, description: 'SideNav renders a navigation landmark, and a collapsible item follows the WAI-ARIA APG Disclosure pattern (https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/): the toggle carries aria-expanded and aria-controls, and the group it owns is inert while collapsed. Keep item labels short
|
|
156
|
+
{guidance: true, description: 'Mark the current page with isSelected: it sets aria-current="page", so the current destination is announced rather than carried by color alone.'},
|
|
157
|
+
{guidance: true, description: 'SideNav renders a navigation landmark, and a collapsible item follows the WAI-ARIA APG Disclosure pattern (https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/): the toggle carries aria-expanded and aria-controls, and the group it owns is inert while collapsed. Keep item labels short; they are the accessible name in both expanded and icon-only modes.'},
|
|
158
158
|
{guidance: true, description: 'While the nav is collapsed, an item with children shows them in a submenu flyout. On a device that can hover, pointing at the item opens it after a short delay and moving away closes it; a flyout opened by clicking stays open until it is dismissed. On touch, it opens on tap. Do not put an action in there that has no other route to it.'},
|
|
159
159
|
{guidance: false, description: 'Include a SideNavHeading when a TopNav is already providing app identity; this duplicates branding.'},
|
|
160
160
|
{guidance: false, description: 'Use for filtering content; use tabs or filter buttons instead.'},
|
|
@@ -175,8 +175,8 @@ export const docsDense = {
|
|
|
175
175
|
bestPractices: [
|
|
176
176
|
{guidance: true, description: 'Use sections to group related navigation items and help users scan for their destination.'},
|
|
177
177
|
{guidance: true, description: 'Pair outline and filled icon variants so the selected state is visually distinct.'},
|
|
178
|
-
{guidance: true, description: 'Mark the current page with isSelected
|
|
179
|
-
{guidance: true, description: 'SideNav renders a navigation landmark, and a collapsible item follows the WAI-ARIA APG Disclosure pattern (https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/): the toggle carries aria-expanded and aria-controls, and the group it owns is inert while collapsed. Keep item labels short
|
|
178
|
+
{guidance: true, description: 'Mark the current page with isSelected: it sets aria-current="page", so the current destination is announced rather than carried by color alone.'},
|
|
179
|
+
{guidance: true, description: 'SideNav renders a navigation landmark, and a collapsible item follows the WAI-ARIA APG Disclosure pattern (https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/): the toggle carries aria-expanded and aria-controls, and the group it owns is inert while collapsed. Keep item labels short; they are the accessible name in both expanded and icon-only modes.'},
|
|
180
180
|
{guidance: true, description: 'While the nav is collapsed, an item with children shows them in a submenu flyout. On a device that can hover, pointing at the item opens it after a short delay and moving away closes it; a flyout opened by clicking stays open until it is dismissed. On touch, it opens on tap. Do not put an action in there that has no other route to it.'},
|
|
181
181
|
{guidance: false, description: 'Include a SideNavHeading when a TopNav is already providing app identity; this duplicates branding.'},
|
|
182
182
|
{guidance: false, description: 'Use for filtering content; use tabs or filter buttons instead.'},
|
package/src/SideNav/SideNav.tsx
CHANGED
|
@@ -32,7 +32,7 @@ import type {BaseProps} from '../BaseProps';
|
|
|
32
32
|
import * as stylex from '@stylexjs/stylex';
|
|
33
33
|
import type {StyleXStyles} from '@stylexjs/stylex';
|
|
34
34
|
import {spacingVars} from '../theme/tokens.stylex';
|
|
35
|
-
import {mergeProps
|
|
35
|
+
import {mergeProps} from '../utils';
|
|
36
36
|
import {
|
|
37
37
|
SideNavCollapseContext,
|
|
38
38
|
type SideNavCollapseState,
|
|
@@ -49,6 +49,7 @@ import {themeProps} from '../utils/themeProps';
|
|
|
49
49
|
import {SizeProvider} from '../SizeContext/SizeContext';
|
|
50
50
|
import {useTranslator} from '../i18n';
|
|
51
51
|
|
|
52
|
+
import {useMergedRefs} from '../hooks/useMergedRefs';
|
|
52
53
|
// =============================================================================
|
|
53
54
|
// Constants
|
|
54
55
|
// =============================================================================
|
|
@@ -349,6 +350,7 @@ export function SideNav({
|
|
|
349
350
|
useState(defaultIsCollapsed);
|
|
350
351
|
const collapsed = isControlled ? controlledCollapsed : uncontrolledCollapsed;
|
|
351
352
|
const navRef = useRef<HTMLElement>(null);
|
|
353
|
+
const mergedNavRef = useMergedRefs(ref, navRef);
|
|
352
354
|
const collapseStateRef = useRef<SideNavCollapseState>({
|
|
353
355
|
isCollapsed: collapsed,
|
|
354
356
|
toggle: () => {},
|
|
@@ -517,7 +519,7 @@ export function SideNav({
|
|
|
517
519
|
|
|
518
520
|
const navElement = (
|
|
519
521
|
<nav
|
|
520
|
-
ref={
|
|
522
|
+
ref={mergedNavRef}
|
|
521
523
|
role="navigation"
|
|
522
524
|
aria-label={t('@astryx.sideNav.label')}
|
|
523
525
|
data-testid={testId}
|
|
@@ -40,7 +40,8 @@ import {focusOutlineProps} from '../utils/focusOutline.stylex';
|
|
|
40
40
|
import {useSideNavCollapse} from './SideNavCollapseContext';
|
|
41
41
|
import {useLinkComponent} from '../Link/useLinkComponent';
|
|
42
42
|
import type {LinkComponentType} from '../Link/types';
|
|
43
|
-
import {mergeProps
|
|
43
|
+
import {mergeProps} from '../utils';
|
|
44
|
+
import {useMergedRefs} from '../hooks/useMergedRefs';
|
|
44
45
|
import type {BaseProps} from '../BaseProps';
|
|
45
46
|
import {useMenuHover} from '../hooks/useMenuHover';
|
|
46
47
|
import {NavHeadingCloseContext} from '../NavMenu/NavMenuContext';
|
|
@@ -59,7 +60,7 @@ const styles = stylex.create({
|
|
|
59
60
|
minHeight: spacingVars['--spacing-8'],
|
|
60
61
|
paddingInlineStart: {
|
|
61
62
|
default: spacingVars['--spacing-2'],
|
|
62
|
-
':has(.astryx-
|
|
63
|
+
':has(.astryx-nav-icon)': 0,
|
|
63
64
|
},
|
|
64
65
|
paddingInlineEnd: spacingVars['--spacing-2'],
|
|
65
66
|
paddingBlock: 0,
|
|
@@ -211,7 +212,7 @@ const styles = stylex.create({
|
|
|
211
212
|
minHeight: spacingVars['--spacing-8'],
|
|
212
213
|
paddingInlineStart: {
|
|
213
214
|
default: spacingVars['--spacing-2'],
|
|
214
|
-
':has(.astryx-
|
|
215
|
+
':has(.astryx-nav-icon)': 0,
|
|
215
216
|
},
|
|
216
217
|
paddingInlineEnd: spacingVars['--spacing-2'],
|
|
217
218
|
paddingBlock: 0,
|
|
@@ -399,11 +400,18 @@ export function SideNavHeading({
|
|
|
399
400
|
// setTriggerEl belongs on the chevron button, not this root: it is the
|
|
400
401
|
// focus-restore target and a <div> cannot take focus. triggerRef stays here
|
|
401
402
|
// because the panel anchors to the whole heading.
|
|
402
|
-
const setRef =
|
|
403
|
+
const setRef = useMergedRefs<HTMLDivElement>(
|
|
403
404
|
rootRef,
|
|
404
405
|
ref,
|
|
405
406
|
menu ? popover.triggerRef : undefined,
|
|
406
407
|
);
|
|
408
|
+
const collapsedSetRef = useMergedRefs<HTMLElement>(
|
|
409
|
+
collapsedItemRef,
|
|
410
|
+
ref,
|
|
411
|
+
// Collapsed, this button is the trigger, so it takes both roles.
|
|
412
|
+
menu ? popover.triggerRef : undefined,
|
|
413
|
+
menu ? setTriggerEl : undefined,
|
|
414
|
+
);
|
|
407
415
|
|
|
408
416
|
// In collapsed mode: hide if no icon, show icon-only if has icon
|
|
409
417
|
if (isCollapsed && !icon) {
|
|
@@ -412,14 +420,6 @@ export function SideNavHeading({
|
|
|
412
420
|
if (isCollapsed && icon) {
|
|
413
421
|
const collapsedIcon = <span {...stylex.props(styles.icon)}>{icon}</span>;
|
|
414
422
|
|
|
415
|
-
const collapsedSetRef = mergeRefs<HTMLElement>(
|
|
416
|
-
collapsedItemRef,
|
|
417
|
-
ref,
|
|
418
|
-
// Collapsed, this button is the trigger, so it takes both roles.
|
|
419
|
-
menu ? popover.triggerRef : undefined,
|
|
420
|
-
menu ? setTriggerEl : undefined,
|
|
421
|
-
);
|
|
422
|
-
|
|
423
423
|
let collapsedElement: ReactNode;
|
|
424
424
|
|
|
425
425
|
if (headingHref) {
|
|
@@ -47,7 +47,7 @@ import {useLinkComponent} from '../Link/useLinkComponent';
|
|
|
47
47
|
import type {LinkComponentType} from '../Link/types';
|
|
48
48
|
import {usePopover} from '../Popover/usePopover';
|
|
49
49
|
import {useMenuHover} from '../hooks/useMenuHover';
|
|
50
|
-
import {mergeProps
|
|
50
|
+
import {mergeProps} from '../utils';
|
|
51
51
|
import type {BaseProps} from '../BaseProps';
|
|
52
52
|
import {Tooltip} from '../Tooltip';
|
|
53
53
|
import {navItemStyles, type NavItemSize} from '../NavItem/navItemStyles.stylex';
|
|
@@ -61,6 +61,7 @@ import {useAppShellMobile} from '../AppShell/AppShellMobileContext';
|
|
|
61
61
|
import {themeProps} from '../utils/themeProps';
|
|
62
62
|
import {useTranslator} from '../i18n';
|
|
63
63
|
|
|
64
|
+
import {useMergedRefs} from '../hooks/useMergedRefs';
|
|
64
65
|
// =============================================================================
|
|
65
66
|
// Styles
|
|
66
67
|
// =============================================================================
|
|
@@ -408,6 +409,7 @@ export function SideNavItem({
|
|
|
408
409
|
hasCloseButton: false,
|
|
409
410
|
dialogLabel: t('@astryx.sideNavItem.submenuLabel', {label}),
|
|
410
411
|
});
|
|
412
|
+
const mergedTriggerRef = useMergedRefs(ref, popover.triggerRef);
|
|
411
413
|
|
|
412
414
|
// Collapse state for items with children
|
|
413
415
|
const itemCollapsibleConfig = useMemo(
|
|
@@ -524,7 +526,7 @@ export function SideNavItem({
|
|
|
524
526
|
return (
|
|
525
527
|
<div {...stylex.props(styles.root, xstyle)}>
|
|
526
528
|
<button
|
|
527
|
-
ref={
|
|
529
|
+
ref={mergedTriggerRef}
|
|
528
530
|
type="button"
|
|
529
531
|
{...rest}
|
|
530
532
|
{...hoverTriggerProps}
|
|
@@ -13,7 +13,10 @@ import {useState} from 'react';
|
|
|
13
13
|
import {describe, it, expect, vi, beforeEach} from 'vitest';
|
|
14
14
|
import {render, screen, act, fireEvent, waitFor} from '@testing-library/react';
|
|
15
15
|
import userEvent from '@testing-library/user-event';
|
|
16
|
+
import * as stylex from '@stylexjs/stylex';
|
|
16
17
|
import {Slider} from './Slider';
|
|
18
|
+
import {focusOutlineStyles} from '../utils/focusOutline.stylex';
|
|
19
|
+
import {__resetInteractionModalityForTest} from '../utils/interactionModality';
|
|
17
20
|
|
|
18
21
|
// Mock showPopover/hidePopover (not implemented in jsdom) so the tooltip layer
|
|
19
22
|
// reflects its open state via a `popover-open` attribute the tests can assert.
|
|
@@ -854,7 +857,7 @@ describe('Slider', () => {
|
|
|
854
857
|
/>,
|
|
855
858
|
);
|
|
856
859
|
const thumb = screen.getByRole('slider');
|
|
857
|
-
thumb.focus();
|
|
860
|
+
act(() => thumb.focus());
|
|
858
861
|
await user.keyboard('{ArrowRight}');
|
|
859
862
|
expect(onChange).not.toHaveBeenCalled();
|
|
860
863
|
});
|
|
@@ -902,4 +905,106 @@ describe('Slider', () => {
|
|
|
902
905
|
]).toEqual([]);
|
|
903
906
|
});
|
|
904
907
|
});
|
|
908
|
+
|
|
909
|
+
describe('focus ring modality', () => {
|
|
910
|
+
beforeEach(() => {
|
|
911
|
+
__resetInteractionModalityForTest();
|
|
912
|
+
});
|
|
913
|
+
|
|
914
|
+
// The ring is a stylex class, so derive it from the same source the
|
|
915
|
+
// component applies rather than hardcoding a hash.
|
|
916
|
+
const RING = stylex
|
|
917
|
+
.props(focusOutlineStyles.focusVisible)
|
|
918
|
+
.className!.split(' ');
|
|
919
|
+
const isRinged = (el: HTMLElement) =>
|
|
920
|
+
RING.every(c => el.classList.contains(c));
|
|
921
|
+
|
|
922
|
+
// The track has no layout in jsdom; pointer maths needs a real rect.
|
|
923
|
+
const grabTrack = (thumb: HTMLElement) => {
|
|
924
|
+
const track = thumb.parentElement!;
|
|
925
|
+
track.getBoundingClientRect = () => ({
|
|
926
|
+
left: 0,
|
|
927
|
+
top: 0,
|
|
928
|
+
right: 200,
|
|
929
|
+
bottom: 20,
|
|
930
|
+
width: 200,
|
|
931
|
+
height: 20,
|
|
932
|
+
x: 0,
|
|
933
|
+
y: 0,
|
|
934
|
+
toJSON: () => {},
|
|
935
|
+
});
|
|
936
|
+
fireEvent.pointerDown(track, {clientX: 100, clientY: 10, pointerId: 1});
|
|
937
|
+
};
|
|
938
|
+
|
|
939
|
+
it('rings when the thumb is reached with the keyboard', async () => {
|
|
940
|
+
const user = userEvent.setup();
|
|
941
|
+
render(<Slider label="Volume" value={50} onChange={vi.fn()} />);
|
|
942
|
+
await user.tab();
|
|
943
|
+
expect(isRinged(screen.getByRole('slider'))).toBe(true);
|
|
944
|
+
});
|
|
945
|
+
|
|
946
|
+
it('does not ring when the thumb is grabbed with the mouse', () => {
|
|
947
|
+
render(<Slider label="Volume" value={50} onChange={vi.fn()} />);
|
|
948
|
+
const thumb = screen.getByRole('slider');
|
|
949
|
+
grabTrack(thumb);
|
|
950
|
+
expect(thumb).toHaveFocus();
|
|
951
|
+
expect(isRinged(thumb)).toBe(false);
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
it('drops the ring when the mouse grabs a thumb that already had it', async () => {
|
|
955
|
+
const user = userEvent.setup();
|
|
956
|
+
render(<Slider label="Volume" value={50} onChange={vi.fn()} />);
|
|
957
|
+
await user.tab();
|
|
958
|
+
const thumb = screen.getByRole('slider');
|
|
959
|
+
expect(isRinged(thumb)).toBe(true);
|
|
960
|
+
// Already focused, so focus() fires no focus event.
|
|
961
|
+
grabTrack(thumb);
|
|
962
|
+
expect(isRinged(thumb)).toBe(false);
|
|
963
|
+
});
|
|
964
|
+
|
|
965
|
+
it('brings the ring back when an arrow key follows a mouse drag', () => {
|
|
966
|
+
render(<Slider label="Volume" value={50} onChange={vi.fn()} />);
|
|
967
|
+
const thumb = screen.getByRole('slider');
|
|
968
|
+
grabTrack(thumb);
|
|
969
|
+
expect(isRinged(thumb)).toBe(false);
|
|
970
|
+
fireEvent.keyDown(thumb, {key: 'ArrowRight'});
|
|
971
|
+
expect(isRinged(thumb)).toBe(true);
|
|
972
|
+
});
|
|
973
|
+
|
|
974
|
+
it('leaves the ring off for a modifier chord after a mouse drag', () => {
|
|
975
|
+
render(<Slider label="Volume" value={50} onChange={vi.fn()} />);
|
|
976
|
+
const thumb = screen.getByRole('slider');
|
|
977
|
+
grabTrack(thumb);
|
|
978
|
+
// Copying or reloading is not navigation, and the mouse is still on it.
|
|
979
|
+
fireEvent.keyDown(thumb, {key: 'c', metaKey: true});
|
|
980
|
+
expect(isRinged(thumb)).toBe(false);
|
|
981
|
+
});
|
|
982
|
+
|
|
983
|
+
it('drops the ring on blur', async () => {
|
|
984
|
+
const user = userEvent.setup();
|
|
985
|
+
render(<Slider label="Volume" value={50} onChange={vi.fn()} />);
|
|
986
|
+
await user.tab();
|
|
987
|
+
const thumb = screen.getByRole('slider');
|
|
988
|
+
expect(isRinged(thumb)).toBe(true);
|
|
989
|
+
act(() => thumb.blur());
|
|
990
|
+
expect(isRinged(thumb)).toBe(false);
|
|
991
|
+
});
|
|
992
|
+
|
|
993
|
+
it('rings the range thumb the keyboard reached, not its sibling', async () => {
|
|
994
|
+
const user = userEvent.setup();
|
|
995
|
+
render(
|
|
996
|
+
<Slider
|
|
997
|
+
label="Price"
|
|
998
|
+
value={[20, 80] as [number, number]}
|
|
999
|
+
onChange={vi.fn()}
|
|
1000
|
+
/>,
|
|
1001
|
+
);
|
|
1002
|
+
await user.tab();
|
|
1003
|
+
await user.tab();
|
|
1004
|
+
const thumbs = screen.getAllByRole('slider');
|
|
1005
|
+
expect(thumbs[1]).toHaveFocus();
|
|
1006
|
+
expect(isRinged(thumbs[1])).toBe(true);
|
|
1007
|
+
expect(isRinged(thumbs[0])).toBe(false);
|
|
1008
|
+
});
|
|
1009
|
+
});
|
|
905
1010
|
});
|