@astryxdesign/core 0.1.2 → 0.1.3-canary.6d57c8d
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 +165 -0
- package/README.md +12 -12
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +11 -5
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +24 -5
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +2 -4
- package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
- package/dist/ButtonGroup/ButtonGroup.js +0 -1
- package/dist/Calendar/Calendar.d.ts +5 -4
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +82 -43
- package/dist/Calendar/hooks/index.d.ts +0 -2
- package/dist/Calendar/hooks/index.d.ts.map +1 -1
- package/dist/Calendar/hooks/index.js +1 -2
- package/dist/Calendar/index.d.ts +3 -3
- package/dist/Calendar/index.d.ts.map +1 -1
- package/dist/Calendar/index.js +1 -1
- package/dist/Calendar/styles.d.ts +3 -9
- package/dist/Calendar/styles.d.ts.map +1 -1
- package/dist/Calendar/styles.js +3 -11
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +17 -3
- package/dist/Center/Center.d.ts +11 -1
- package/dist/Center/Center.d.ts.map +1 -1
- package/dist/Center/Center.js +9 -3
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +0 -1
- package/dist/Chat/ChatMessageList.d.ts +14 -1
- package/dist/Chat/ChatMessageList.d.ts.map +1 -1
- package/dist/Chat/ChatMessageList.js +2 -0
- package/dist/Chat/useTriggerMenu.d.ts +8 -1
- package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
- package/dist/Chat/useTriggerMenu.js +15 -3
- package/dist/CheckboxInput/CheckboxInput.d.ts +23 -2
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +51 -9
- package/dist/CheckboxList/CheckboxList.d.ts +13 -1
- package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxList.js +39 -9
- package/dist/CheckboxList/CheckboxListContext.d.ts +7 -0
- package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.js +0 -1
- package/dist/Citation/Citation.d.ts.map +1 -1
- package/dist/Citation/Citation.js +9 -5
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +51 -37
- package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPalette.js +5 -5
- package/dist/ContextMenu/ContextMenu.d.ts +4 -5
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +93 -11
- package/dist/DateInput/DateInput.d.ts +23 -1
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +167 -99
- package/dist/DateRangeInput/DateRangeInput.d.ts +23 -1
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +42 -9
- package/dist/DateTimeInput/DateTimeInput.d.ts +35 -2
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +78 -15
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +12 -0
- package/dist/DropdownMenu/DropdownMenu.d.ts +1 -8
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +42 -14
- package/dist/Field/Field.d.ts +19 -2
- package/dist/Field/Field.d.ts.map +1 -1
- package/dist/Field/Field.js +4 -0
- package/dist/Field/FieldLabel.d.ts +21 -2
- package/dist/Field/FieldLabel.d.ts.map +1 -1
- package/dist/Field/FieldLabel.js +41 -26
- package/dist/FileInput/FileInput.d.ts +22 -1
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +56 -10
- package/dist/Grid/Grid.d.ts +11 -1
- package/dist/Grid/Grid.d.ts.map +1 -1
- package/dist/Grid/Grid.js +32 -6
- package/dist/InputGroup/InputGroup.d.ts +4 -3
- package/dist/InputGroup/InputGroup.d.ts.map +1 -1
- package/dist/InputGroup/InputGroup.js +16 -6
- package/dist/InputGroup/InputGroupContext.d.ts +4 -0
- package/dist/InputGroup/InputGroupContext.d.ts.map +1 -1
- package/dist/InputGroup/InputGroupContext.js +2 -2
- package/dist/InputGroup/groupStyles.d.ts +1 -1
- package/dist/InputGroup/groupStyles.d.ts.map +1 -1
- package/dist/InputGroup/groupStyles.js +3 -3
- package/dist/InputGroup/index.d.ts +1 -0
- package/dist/InputGroup/index.d.ts.map +1 -1
- package/dist/Kbd/Kbd.d.ts.map +1 -1
- package/dist/Kbd/Kbd.js +33 -1
- package/dist/Layer/layerAnimations.stylex.d.ts +4 -0
- package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
- package/dist/Layer/layerAnimations.stylex.js +8 -4
- package/dist/Layer/useLayer.d.ts +10 -0
- package/dist/Layer/useLayer.d.ts.map +1 -1
- package/dist/Layer/useLayer.js +64 -9
- package/dist/Layout/Layout.d.ts +4 -11
- package/dist/Layout/Layout.d.ts.map +1 -1
- package/dist/Layout/Layout.js +10 -11
- package/dist/Layout/LayoutFooter.d.ts +2 -2
- package/dist/Layout/LayoutFooter.d.ts.map +1 -1
- package/dist/Layout/LayoutHeader.d.ts +2 -2
- package/dist/Layout/LayoutHeader.d.ts.map +1 -1
- package/dist/Layout/LayoutPanel.d.ts +2 -2
- package/dist/Layout/LayoutPanel.d.ts.map +1 -1
- package/dist/Layout/padding.stylex.d.ts +51 -0
- package/dist/Layout/padding.stylex.d.ts.map +1 -1
- package/dist/Layout/padding.stylex.js +63 -0
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +4 -2
- package/dist/Link/Link.d.ts +16 -1
- package/dist/Link/Link.d.ts.map +1 -1
- package/dist/Link/Link.js +18 -7
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.js +7 -0
- package/dist/MoreMenu/MoreMenu.d.ts +1 -7
- package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
- package/dist/MoreMenu/MoreMenu.js +0 -2
- package/dist/MultiSelector/MultiSelector.d.ts +27 -2
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +141 -42
- package/dist/MultiSelector/hooks.d.ts +12 -1
- package/dist/MultiSelector/hooks.d.ts.map +1 -1
- package/dist/MultiSelector/hooks.js +14 -1
- package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
- package/dist/NavMenu/NavHeadingMenu.js +34 -3
- package/dist/NumberInput/NumberInput.d.ts +23 -2
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +62 -12
- package/dist/Pagination/Pagination.d.ts +1 -1
- package/dist/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Pagination/Pagination.js +15 -1
- package/dist/Popover/Popover.d.ts +18 -2
- package/dist/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +5 -2
- package/dist/Popover/usePopover.d.ts +38 -3
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +24 -9
- package/dist/PowerSearch/PowerSearch.d.ts +11 -1
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +6 -1
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +3 -5
- package/dist/RadioList/RadioList.d.ts +20 -2
- package/dist/RadioList/RadioList.d.ts.map +1 -1
- package/dist/RadioList/RadioList.js +105 -9
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +13 -2
- package/dist/SegmentedControl/SegmentedControl.d.ts +13 -2
- package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControl.js +78 -44
- package/dist/SegmentedControl/SegmentedControlContext.d.ts +7 -0
- package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +13 -2
- package/dist/Selector/Selector.d.ts +24 -1
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +122 -43
- package/dist/Selector/hooks.d.ts +8 -1
- package/dist/Selector/hooks.d.ts.map +1 -1
- package/dist/Selector/hooks.js +14 -1
- package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
- package/dist/SideNav/SideNavHeading.js +2 -0
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +1 -1
- package/dist/Skeleton/Skeleton.d.ts.map +1 -1
- package/dist/Skeleton/Skeleton.js +8 -2
- package/dist/Slider/Slider.d.ts +21 -0
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +36 -9
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +1 -1
- package/dist/Stack/Stack.d.ts +38 -23
- package/dist/Stack/Stack.d.ts.map +1 -1
- package/dist/Stack/Stack.js +25 -1
- package/dist/Stack/StackItem.d.ts +12 -23
- package/dist/Stack/StackItem.d.ts.map +1 -1
- package/dist/Stack/StackItem.js +8 -1
- package/dist/Switch/Switch.d.ts +23 -2
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +41 -11
- package/dist/TabList/Tab.d.ts.map +1 -1
- package/dist/TabList/Tab.js +6 -0
- package/dist/TabList/TabList.d.ts +16 -4
- package/dist/TabList/TabList.d.ts.map +1 -1
- package/dist/TabList/TabList.js +87 -10
- package/dist/TabList/TabListContext.d.ts +5 -0
- package/dist/TabList/TabListContext.d.ts.map +1 -1
- package/dist/TabList/TabListContext.js +5 -0
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +6 -1
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +11 -8
- package/dist/Table/Table.d.ts.map +1 -1
- package/dist/Table/Table.js +10 -1
- package/dist/Table/TableCell.d.ts +8 -1
- package/dist/Table/TableCell.d.ts.map +1 -1
- package/dist/Table/TableCell.js +10 -10
- package/dist/Table/TableHeaderCell.d.ts +8 -1
- package/dist/Table/TableHeaderCell.d.ts.map +1 -1
- package/dist/Table/TableHeaderCell.js +16 -16
- package/dist/Table/index.d.ts +2 -1
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +2 -2
- package/dist/Table/plugins/pagination/paginateData.d.ts +2 -1
- package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
- package/dist/Table/plugins/pagination/paginateData.js +8 -3
- package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
- package/dist/Table/plugins/pagination/useTablePagination.js +6 -1
- package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortable.js +59 -3
- package/dist/Table/tableContextMenu.d.ts +33 -0
- package/dist/Table/tableContextMenu.d.ts.map +1 -0
- package/dist/Table/tableContextMenu.js +133 -0
- package/dist/Table/types.d.ts +66 -0
- package/dist/Table/types.d.ts.map +1 -1
- package/dist/TextArea/TextArea.d.ts +22 -1
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +41 -9
- package/dist/TextInput/TextInput.d.ts +23 -2
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +55 -10
- package/dist/TimeInput/TimeInput.d.ts +23 -1
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +147 -76
- package/dist/Toast/Toast.d.ts.map +1 -1
- package/dist/Toast/Toast.js +14 -0
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +131 -3
- package/dist/Tokenizer/Tokenizer.d.ts +12 -1
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +26 -4
- package/dist/Toolbar/Toolbar.d.ts +3 -3
- package/dist/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/Toolbar/Toolbar.js +51 -7
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +56 -9
- package/dist/TopNav/TopNavHeading.d.ts +7 -1
- package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
- package/dist/TopNav/TopNavHeading.js +8 -0
- package/dist/TreeList/TreeList.d.ts +1 -1
- package/dist/TreeList/TreeList.d.ts.map +1 -1
- package/dist/TreeList/TreeList.js +72 -2
- package/dist/TreeList/TreeListItem.d.ts +11 -1
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +51 -12
- package/dist/TreeList/treeListItem.markers.stylex.d.ts +16 -0
- package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +1 -0
- package/dist/TreeList/treeListItem.markers.stylex.js +22 -0
- package/dist/Typeahead/BaseTypeahead.d.ts +13 -0
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +69 -6
- package/dist/Typeahead/Typeahead.d.ts +26 -3
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +65 -29
- package/dist/VisuallyHidden/VisuallyHidden.d.ts +61 -0
- package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -0
- package/dist/VisuallyHidden/VisuallyHidden.js +65 -0
- package/dist/VisuallyHidden/index.d.ts +11 -0
- package/dist/VisuallyHidden/index.d.ts.map +1 -0
- package/dist/VisuallyHidden/index.js +13 -0
- package/dist/astryx.css +18 -0
- package/dist/astryx.umd.js +55 -55
- package/dist/astryx.umd.js.map +4 -4
- package/dist/hooks/focusableSelector.d.ts +22 -0
- package/dist/hooks/focusableSelector.d.ts.map +1 -0
- package/dist/hooks/focusableSelector.js +24 -0
- package/dist/hooks/index.d.ts +12 -2
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +7 -2
- package/dist/hooks/useAnnounce.d.ts +40 -0
- package/dist/hooks/useAnnounce.d.ts.map +1 -0
- package/dist/hooks/useAnnounce.js +142 -0
- package/dist/hooks/useClickableContainer.d.ts +16 -1
- package/dist/hooks/useClickableContainer.d.ts.map +1 -1
- package/dist/hooks/useClickableContainer.js +16 -4
- package/dist/hooks/useEntryAnimation.d.ts.map +1 -1
- package/dist/hooks/useEntryAnimation.js +4 -4
- package/dist/hooks/useFocusTrap.d.ts +18 -0
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +87 -3
- package/dist/hooks/useGridFocus.d.ts +88 -1
- package/dist/hooks/useGridFocus.d.ts.map +1 -1
- package/dist/hooks/useGridFocus.js +261 -62
- package/dist/hooks/useKeyboardHint.d.ts +82 -0
- package/dist/hooks/useKeyboardHint.d.ts.map +1 -0
- package/dist/hooks/useKeyboardHint.js +220 -0
- package/dist/hooks/useListFocus.d.ts +78 -7
- package/dist/hooks/useListFocus.d.ts.map +1 -1
- package/dist/hooks/useListFocus.js +305 -52
- package/dist/hooks/useLongPress.d.ts +21 -0
- package/dist/hooks/useLongPress.d.ts.map +1 -0
- package/dist/hooks/useLongPress.js +89 -0
- package/dist/hooks/useTreeFocus.d.ts +157 -0
- package/dist/hooks/useTreeFocus.d.ts.map +1 -0
- package/dist/hooks/useTreeFocus.js +337 -0
- package/dist/hooks/useTypeahead.d.ts +63 -0
- package/dist/hooks/useTypeahead.d.ts.map +1 -0
- package/dist/hooks/useTypeahead.js +117 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/utils/dateTypes.d.ts +12 -0
- package/dist/utils/dateTypes.d.ts.map +1 -1
- package/dist/utils/dateTypes.js +41 -1
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -1
- package/dist/utils/inputAria.d.ts +29 -0
- package/dist/utils/inputAria.d.ts.map +1 -0
- package/dist/utils/inputAria.js +30 -0
- package/dist/utils/timeParser.d.ts.map +1 -1
- package/dist/utils/timeParser.js +5 -2
- package/docs.mjs +2 -2
- package/package.json +12 -8
- package/src/Avatar/Avatar.test.tsx +37 -0
- package/src/Avatar/Avatar.tsx +10 -5
- package/src/Breadcrumbs/BreadcrumbItem.tsx +25 -7
- package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -10
- package/src/Button/Button.tsx +3 -16
- package/src/ButtonGroup/ButtonGroup.test.tsx +9 -9
- package/src/ButtonGroup/ButtonGroup.tsx +0 -1
- package/src/Calendar/Calendar.doc.mjs +4 -4
- package/src/Calendar/Calendar.test.tsx +168 -30
- package/src/Calendar/Calendar.tsx +121 -66
- package/src/Calendar/hooks/index.ts +0 -6
- package/src/Calendar/index.ts +1 -3
- package/src/Calendar/styles.ts +4 -9
- package/src/Carousel/Carousel.doc.mjs +7 -1
- package/src/Carousel/Carousel.test.tsx +20 -0
- package/src/Carousel/Carousel.tsx +16 -1
- package/src/Center/Center.doc.mjs +12 -2
- package/src/Center/Center.tsx +28 -3
- package/src/Chat/ChatComposerInput.test.tsx +26 -7
- package/src/Chat/ChatComposerInput.tsx +0 -1
- package/src/Chat/ChatDictationButton.doc.mjs +1 -1
- package/src/Chat/ChatMessageList.doc.mjs +8 -0
- package/src/Chat/ChatMessageList.test.tsx +18 -0
- package/src/Chat/ChatMessageList.tsx +16 -0
- package/src/Chat/useTriggerMenu.tsx +22 -4
- package/src/CheckboxInput/CheckboxInput.doc.mjs +10 -0
- package/src/CheckboxInput/CheckboxInput.test.tsx +172 -27
- package/src/CheckboxInput/CheckboxInput.tsx +78 -6
- package/src/CheckboxList/CheckboxList.doc.mjs +9 -0
- package/src/CheckboxList/CheckboxList.test.tsx +192 -26
- package/src/CheckboxList/CheckboxList.tsx +56 -3
- package/src/CheckboxList/CheckboxListContext.tsx +7 -0
- package/src/CheckboxList/CheckboxListItem.tsx +0 -3
- package/src/Citation/Citation.doc.mjs +10 -0
- package/src/Citation/Citation.test.tsx +121 -0
- package/src/Citation/Citation.tsx +15 -3
- package/src/CodeBlock/CodeBlock.test.tsx +115 -0
- package/src/CodeBlock/CodeBlock.tsx +63 -33
- package/src/CommandPalette/CommandPalette.test.tsx +47 -1
- package/src/CommandPalette/CommandPalette.tsx +9 -21
- package/src/ContextMenu/ContextMenu.doc.mjs +4 -4
- package/src/ContextMenu/ContextMenu.test.tsx +155 -1
- package/src/ContextMenu/ContextMenu.tsx +113 -16
- package/src/DateInput/DateInput.doc.mjs +286 -56
- package/src/DateInput/DateInput.test.tsx +248 -2
- package/src/DateInput/DateInput.tsx +196 -100
- package/src/DateRangeInput/DateRangeInput.doc.mjs +251 -46
- package/src/DateRangeInput/DateRangeInput.test.tsx +182 -20
- package/src/DateRangeInput/DateRangeInput.tsx +63 -5
- package/src/DateTimeInput/DateTimeInput.doc.mjs +368 -67
- package/src/DateTimeInput/DateTimeInput.test.tsx +279 -11
- package/src/DateTimeInput/DateTimeInput.tsx +125 -17
- package/src/Dialog/Dialog.doc.mjs +2 -2
- package/src/Dialog/Dialog.tsx +12 -0
- package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -8
- package/src/DropdownMenu/DropdownMenu.test.tsx +56 -42
- package/src/DropdownMenu/DropdownMenu.tsx +52 -18
- package/src/Field/Field.doc.mjs +59 -18
- package/src/Field/Field.test.tsx +32 -0
- package/src/Field/Field.tsx +23 -5
- package/src/Field/FieldLabel.tsx +55 -17
- package/src/FileInput/FileInput.doc.mjs +17 -0
- package/src/FileInput/FileInput.test.tsx +283 -49
- package/src/FileInput/FileInput.tsx +76 -4
- package/src/Grid/Grid.doc.mjs +17 -4
- package/src/Grid/Grid.test.tsx +43 -30
- package/src/Grid/Grid.tsx +38 -3
- package/src/Icon/Icon.doc.mjs +3 -3
- package/src/InputGroup/InputGroup.doc.mjs +111 -23
- package/src/InputGroup/InputGroup.test.tsx +257 -60
- package/src/InputGroup/InputGroup.tsx +23 -5
- package/src/InputGroup/InputGroupContext.ts +9 -4
- package/src/InputGroup/groupStyles.ts +7 -1
- package/src/InputGroup/index.ts +1 -0
- package/src/Kbd/Kbd.test.tsx +22 -4
- package/src/Kbd/Kbd.tsx +35 -3
- package/src/Layer/layerAnimations.stylex.ts +20 -4
- package/src/Layer/useLayer.doc.mjs +5 -0
- package/src/Layer/useLayer.test.tsx +111 -0
- package/src/Layer/useLayer.tsx +90 -9
- package/src/Layout/Layout.tsx +8 -16
- package/src/Layout/LayoutFooter.doc.mjs +4 -4
- package/src/Layout/LayoutFooter.tsx +3 -3
- package/src/Layout/LayoutHeader.doc.mjs +4 -4
- package/src/Layout/LayoutHeader.tsx +3 -3
- package/src/Layout/LayoutPanel.tsx +3 -3
- package/src/Layout/padding.stylex.ts +52 -0
- package/src/Lightbox/Lightbox.doc.mjs +9 -0
- package/src/Lightbox/Lightbox.test.tsx +53 -5
- package/src/Lightbox/Lightbox.tsx +9 -4
- package/src/Link/Link.doc.mjs +14 -0
- package/src/Link/Link.test.tsx +50 -0
- package/src/Link/Link.tsx +39 -4
- package/src/Markdown/Markdown.test.tsx +11 -0
- package/src/Markdown/Markdown.tsx +7 -0
- package/src/MetadataList/MetadataListItem.doc.mjs +4 -0
- package/src/MoreMenu/MoreMenu.doc.mjs +2 -17
- package/src/MoreMenu/MoreMenu.tsx +0 -9
- package/src/MultiSelector/MultiSelector.doc.mjs +30 -0
- package/src/MultiSelector/MultiSelector.test.tsx +375 -8
- package/src/MultiSelector/MultiSelector.tsx +194 -48
- package/src/MultiSelector/hooks.ts +27 -0
- package/src/NavMenu/NavHeadingMenu.test.tsx +77 -1
- package/src/NavMenu/NavHeadingMenu.tsx +50 -3
- package/src/NumberInput/NumberInput.doc.mjs +17 -0
- package/src/NumberInput/NumberInput.test.tsx +242 -40
- package/src/NumberInput/NumberInput.tsx +89 -11
- package/src/Pagination/Pagination.doc.mjs +3 -3
- package/src/Pagination/Pagination.test.tsx +117 -5
- package/src/Pagination/Pagination.tsx +21 -1
- package/src/Popover/Popover.doc.mjs +26 -0
- package/src/Popover/Popover.test.tsx +121 -0
- package/src/Popover/Popover.tsx +23 -2
- package/src/Popover/usePopover.doc.mjs +9 -3
- package/src/Popover/usePopover.tsx +70 -12
- package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
- package/src/PowerSearch/PowerSearch.test.tsx +97 -1
- package/src/PowerSearch/PowerSearch.tsx +16 -3
- package/src/ProgressBar/ProgressBar.test.tsx +34 -26
- package/src/ProgressBar/ProgressBar.tsx +3 -4
- package/src/RadioList/RadioList.doc.mjs +9 -0
- package/src/RadioList/RadioList.test.tsx +252 -16
- package/src/RadioList/RadioList.tsx +166 -10
- package/src/RadioList/RadioListItem.tsx +14 -2
- package/src/SegmentedControl/SegmentedControl.doc.mjs +9 -0
- package/src/SegmentedControl/SegmentedControl.test.tsx +214 -42
- package/src/SegmentedControl/SegmentedControl.tsx +102 -54
- package/src/SegmentedControl/SegmentedControlContext.ts +7 -0
- package/src/SegmentedControl/SegmentedControlItem.tsx +14 -1
- package/src/SelectableCard/SelectableCard.doc.mjs +1 -1
- package/src/Selector/Selector.doc.mjs +26 -0
- package/src/Selector/Selector.test.tsx +387 -15
- package/src/Selector/Selector.tsx +175 -67
- package/src/Selector/hooks.ts +23 -0
- package/src/SideNav/SideNav.test.tsx +48 -0
- package/src/SideNav/SideNavHeading.tsx +8 -2
- package/src/SideNav/SideNavItem.tsx +2 -4
- package/src/Skeleton/Skeleton.test.tsx +25 -0
- package/src/Skeleton/Skeleton.tsx +11 -1
- package/src/Slider/Slider.doc.mjs +15 -0
- package/src/Slider/Slider.test.tsx +155 -2
- package/src/Slider/Slider.tsx +53 -5
- package/src/Spinner/Spinner.tsx +7 -1
- package/src/Stack/Stack.doc.mjs +77 -0
- package/src/Stack/Stack.test.tsx +55 -5
- package/src/Stack/Stack.tsx +73 -24
- package/src/Stack/StackItem.test.tsx +23 -0
- package/src/Stack/StackItem.tsx +20 -23
- package/src/Switch/Switch.doc.mjs +15 -0
- package/src/Switch/Switch.test.tsx +162 -27
- package/src/Switch/Switch.tsx +61 -21
- package/src/TabList/Tab.tsx +6 -0
- package/src/TabList/TabList.doc.mjs +6 -0
- package/src/TabList/TabList.test.tsx +251 -0
- package/src/TabList/TabList.tsx +103 -12
- package/src/TabList/TabListContext.ts +7 -3
- package/src/TabList/TabMenu.tsx +6 -3
- package/src/Table/BaseTable.tsx +21 -17
- package/src/Table/Table.doc.mjs +2 -0
- package/src/Table/Table.test.tsx +10 -0
- package/src/Table/Table.tsx +8 -0
- package/src/Table/TableCell.tsx +25 -23
- package/src/Table/TableHeaderCell.tsx +28 -29
- package/src/Table/index.ts +3 -2
- package/src/Table/plugins/pagination/paginateData.ts +10 -3
- package/src/Table/plugins/pagination/useTablePagination.test.tsx +16 -0
- package/src/Table/plugins/pagination/useTablePagination.tsx +8 -1
- package/src/Table/plugins/sortable/useTableSortable.test.tsx +91 -2
- package/src/Table/plugins/sortable/useTableSortable.tsx +53 -1
- package/src/Table/tableContextMenu.test.tsx +157 -0
- package/src/Table/tableContextMenu.tsx +142 -0
- package/src/Table/types.ts +74 -0
- package/src/Table/useTableColumnResize.doc.mjs +3 -3
- package/src/Table/useTableStickyColumns.doc.mjs +2 -2
- package/src/Text/Text.doc.mjs +18 -18
- package/src/TextArea/TextArea.doc.mjs +17 -0
- package/src/TextArea/TextArea.test.tsx +167 -35
- package/src/TextArea/TextArea.tsx +62 -15
- package/src/TextInput/TextInput.doc.mjs +17 -0
- package/src/TextInput/TextInput.test.tsx +168 -34
- package/src/TextInput/TextInput.tsx +77 -9
- package/src/Thumbnail/Thumbnail.doc.mjs +4 -4
- package/src/TimeInput/TimeInput.doc.mjs +340 -60
- package/src/TimeInput/TimeInput.test.tsx +255 -7
- package/src/TimeInput/TimeInput.tsx +176 -70
- package/src/Timestamp/Timestamp.doc.mjs +19 -19
- package/src/Toast/Toast.tsx +14 -0
- package/src/Toast/ToastViewport.test.tsx +214 -0
- package/src/Toast/ToastViewport.tsx +167 -21
- package/src/Tokenizer/Tokenizer.doc.mjs +29 -14
- package/src/Tokenizer/Tokenizer.test.tsx +105 -1
- package/src/Tokenizer/Tokenizer.tsx +37 -1
- package/src/Toolbar/Toolbar.test.tsx +109 -6
- package/src/Toolbar/Toolbar.tsx +59 -5
- package/src/Tooltip/Tooltip.test.tsx +68 -0
- package/src/Tooltip/useTooltip.tsx +55 -8
- package/src/TopNav/TopNav.test.tsx +50 -0
- package/src/TopNav/TopNavHeading.tsx +17 -1
- package/src/TreeList/TreeList.test.tsx +295 -6
- package/src/TreeList/TreeList.tsx +73 -1
- package/src/TreeList/TreeListItem.tsx +51 -5
- package/src/TreeList/treeListItem.markers.stylex.ts +20 -0
- package/src/Typeahead/BaseTypeahead.tsx +93 -9
- package/src/Typeahead/Typeahead.doc.mjs +141 -30
- package/src/Typeahead/Typeahead.test.tsx +331 -1
- package/src/Typeahead/Typeahead.tsx +105 -33
- package/src/VisuallyHidden/VisuallyHidden.doc.mjs +80 -0
- package/src/VisuallyHidden/VisuallyHidden.test.tsx +76 -0
- package/src/VisuallyHidden/VisuallyHidden.tsx +113 -0
- package/src/VisuallyHidden/index.ts +15 -0
- package/src/docs-script.test.mjs +50 -0
- package/src/hooks/focusableSelector.ts +25 -0
- package/src/hooks/index.ts +28 -2
- package/src/hooks/useAnnounce.test.tsx +101 -0
- package/src/hooks/useAnnounce.ts +168 -0
- package/src/hooks/useClickableContainer.ts +16 -4
- package/src/hooks/useEntryAnimation.ts +16 -4
- package/src/hooks/useFocusTrap.test.tsx +132 -0
- package/src/hooks/useFocusTrap.ts +98 -4
- package/src/hooks/useGridFocus.doc.mjs +40 -2
- package/src/hooks/useGridFocus.test.tsx +132 -0
- package/src/hooks/useGridFocus.ts +343 -67
- package/src/hooks/useKeyboardHint.doc.mjs +101 -0
- package/src/hooks/useKeyboardHint.test.tsx +70 -0
- package/src/hooks/useKeyboardHint.tsx +332 -0
- package/src/hooks/useListFocus.doc.mjs +50 -10
- package/src/hooks/useListFocus.test.tsx +375 -0
- package/src/hooks/useListFocus.ts +395 -57
- package/src/hooks/useLongPress.test.tsx +134 -0
- package/src/hooks/useLongPress.ts +119 -0
- package/src/hooks/useTreeFocus.doc.mjs +140 -0
- package/src/hooks/useTreeFocus.test.tsx +214 -0
- package/src/hooks/useTreeFocus.ts +559 -0
- package/src/hooks/useTypeahead.test.tsx +109 -0
- package/src/hooks/useTypeahead.ts +183 -0
- package/src/index.ts +1 -0
- package/src/theme/derivedVarRegistry.test.ts +10 -5
- package/src/utils/dateTypes.test.ts +40 -0
- package/src/utils/dateTypes.ts +40 -0
- package/src/utils/index.ts +9 -0
- package/src/utils/inputAria.test.ts +39 -0
- package/src/utils/inputAria.ts +58 -0
- package/src/utils/timeParser.test.ts +10 -0
- package/src/utils/timeParser.ts +5 -2
- package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts +0 -57
- package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts.map +0 -1
- package/dist/Calendar/hooks/useCalendarRovingTabindex.js +0 -96
- package/src/Calendar/hooks/useCalendarRovingTabindex.ts +0 -118
|
@@ -4,15 +4,25 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file useListFocus.ts
|
|
7
|
-
* @input Uses React useCallback, useRef
|
|
7
|
+
* @input Uses React useCallback, useRef, useIsomorphicLayoutEffect
|
|
8
8
|
* @output Exports useListFocus hook for linear list keyboard navigation
|
|
9
|
-
* @position Core hook; used by TabMenu for dropdown menu navigation, Toolbar
|
|
9
|
+
* @position Core hook; used by TabMenu for dropdown menu navigation, Toolbar
|
|
10
|
+
* for roving tabindex, ButtonGroup, ContextMenu, NavHeadingMenu, and more.
|
|
10
11
|
*
|
|
11
12
|
* SYNC: When modified, update:
|
|
12
13
|
* - /packages/core/src/hooks/index.ts
|
|
13
14
|
*/
|
|
14
15
|
|
|
15
16
|
import {useCallback, useRef} from 'react';
|
|
17
|
+
import {useIsomorphicLayoutEffect} from './useIsomorphicLayoutEffect';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Navigation orientation for a linear list.
|
|
21
|
+
* - `'horizontal'`: ArrowLeft/ArrowRight move between items.
|
|
22
|
+
* - `'vertical'`: ArrowUp/ArrowDown move between items.
|
|
23
|
+
* - `'both'`: all four arrows move between items (in linear DOM order).
|
|
24
|
+
*/
|
|
25
|
+
export type ListFocusOrientation = 'horizontal' | 'vertical' | 'both';
|
|
16
26
|
|
|
17
27
|
/**
|
|
18
28
|
* Configuration for list focus behavior
|
|
@@ -36,11 +46,51 @@ export interface UseListFocusOptions {
|
|
|
36
46
|
onEscape?: () => void;
|
|
37
47
|
|
|
38
48
|
/**
|
|
39
|
-
* Navigation orientation. 'horizontal' uses ArrowLeft/ArrowRight,
|
|
40
|
-
* 'vertical' uses ArrowUp/ArrowDown.
|
|
49
|
+
* Navigation orientation. `'horizontal'` uses ArrowLeft/ArrowRight,
|
|
50
|
+
* `'vertical'` uses ArrowUp/ArrowDown, `'both'` accepts all four arrows.
|
|
41
51
|
* @default 'vertical'
|
|
42
52
|
*/
|
|
43
|
-
orientation?:
|
|
53
|
+
orientation?: ListFocusOrientation;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Whether Home/End jump to the first/last enabled item.
|
|
57
|
+
* @default true
|
|
58
|
+
*/
|
|
59
|
+
hasHomeEnd?: boolean;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Whether the list is in a right-to-left context. When true, ArrowLeft and
|
|
63
|
+
* ArrowRight are swapped so horizontal navigation follows visual direction.
|
|
64
|
+
* Only affects horizontal (`'horizontal'`/`'both'`) navigation.
|
|
65
|
+
* @default false
|
|
66
|
+
*/
|
|
67
|
+
isRtl?: boolean;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Roving-tabindex ownership. When true, the hook manages a single tab stop
|
|
71
|
+
* across the items: exactly one enabled item carries `tabindex="0"` and the
|
|
72
|
+
* rest `tabindex="-1"`. The tab stop is stamped on mount and repaired
|
|
73
|
+
* whenever items mount/unmount or toggle disabled, and moves with arrow
|
|
74
|
+
* navigation. Attach the returned {@link UseListFocusReturn.handleFocus} to
|
|
75
|
+
* the container's `onFocus` to keep the stop in sync after clicks or
|
|
76
|
+
* programmatic focus.
|
|
77
|
+
*
|
|
78
|
+
* When false (the default), the hook only *moves* focus (`.focus()`) and
|
|
79
|
+
* never touches `tabindex` — the caller owns tab-stop management.
|
|
80
|
+
* @default false
|
|
81
|
+
*/
|
|
82
|
+
hasRovingTabIndex?: boolean;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* When true, arrow keys are not stolen from a nested text input/textarea
|
|
86
|
+
* whose caret is not at the boundary in the direction of travel (or that has
|
|
87
|
+
* a non-collapsed selection), and are never stolen from a nested
|
|
88
|
+
* `contenteditable` (rich-text editor / chat composer). This preserves
|
|
89
|
+
* normal caret movement while the user is editing inline within the list
|
|
90
|
+
* (e.g. a toolbar search field or composer).
|
|
91
|
+
* @default false
|
|
92
|
+
*/
|
|
93
|
+
hasCaretGuard?: boolean;
|
|
44
94
|
}
|
|
45
95
|
|
|
46
96
|
/**
|
|
@@ -57,20 +107,120 @@ export interface UseListFocusReturn<T extends HTMLElement = HTMLElement> {
|
|
|
57
107
|
*/
|
|
58
108
|
handleKeyDown: (e: React.KeyboardEvent) => void;
|
|
59
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Focus handler to attach to the container's `onFocus`. Keeps the roving tab
|
|
112
|
+
* stop in sync when `hasRovingTabIndex` is enabled; a no-op otherwise, so it is
|
|
113
|
+
* always safe to attach.
|
|
114
|
+
*/
|
|
115
|
+
handleFocus: (e: React.FocusEvent) => void;
|
|
116
|
+
|
|
60
117
|
/**
|
|
61
118
|
* Focus a specific item by index.
|
|
62
119
|
*/
|
|
63
120
|
focusItem: (index: number) => void;
|
|
64
121
|
|
|
65
122
|
/**
|
|
66
|
-
* Focus the first
|
|
123
|
+
* Focus the first enabled item. Returns true when an item was focused.
|
|
67
124
|
*/
|
|
68
|
-
focusFirst: () =>
|
|
125
|
+
focusFirst: () => boolean;
|
|
69
126
|
|
|
70
127
|
/**
|
|
71
|
-
* Focus the last
|
|
128
|
+
* Focus the last enabled item. Returns true when an item was focused.
|
|
72
129
|
*/
|
|
73
|
-
focusLast: () =>
|
|
130
|
+
focusLast: () => boolean;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Text-editing inputs whose caret must not be hijacked by arrow navigation.
|
|
134
|
+
const TEXT_INPUT_TYPES = new Set([
|
|
135
|
+
'text',
|
|
136
|
+
'search',
|
|
137
|
+
'url',
|
|
138
|
+
'tel',
|
|
139
|
+
'email',
|
|
140
|
+
'password',
|
|
141
|
+
'number',
|
|
142
|
+
]);
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* The nearest `contenteditable` root for `el`, or null when `el` is not inside
|
|
146
|
+
* an editable region. Prefers the browser's `isContentEditable` property and
|
|
147
|
+
* falls back to the closest `[contenteditable]` ancestor whose value is not
|
|
148
|
+
* `"false"` (so environments without `isContentEditable` still work).
|
|
149
|
+
*/
|
|
150
|
+
function getContentEditableRoot(el: HTMLElement): HTMLElement | null {
|
|
151
|
+
if (el.isContentEditable) {
|
|
152
|
+
return el;
|
|
153
|
+
}
|
|
154
|
+
const candidate = el.closest<HTMLElement>('[contenteditable]');
|
|
155
|
+
if (candidate && candidate.getAttribute('contenteditable') !== 'false') {
|
|
156
|
+
return candidate;
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Whether an arrow/Home/End key should be left to the browser because the
|
|
163
|
+
* event target is a text-editing element whose caret is not yet at the boundary
|
|
164
|
+
* in the direction of travel (or a selection is present). Returns true when the
|
|
165
|
+
* list should NOT steal the key.
|
|
166
|
+
*
|
|
167
|
+
* Covers three editing surfaces:
|
|
168
|
+
* - `<textarea>` and text-type `<input>` — use `selectionStart`/`selectionEnd`
|
|
169
|
+
* to steal only at the boundary in the travel direction.
|
|
170
|
+
* - `contenteditable` (rich-text editor / chat composer) — precise caret
|
|
171
|
+
* boundary detection in an arbitrary editable subtree is fragile, so we err
|
|
172
|
+
* on the side of never hijacking an active editor: defer on every caret key
|
|
173
|
+
* whenever focus is inside a non-empty contenteditable. (An empty editable
|
|
174
|
+
* has nothing to caret through, so navigation may proceed.)
|
|
175
|
+
*/
|
|
176
|
+
function shouldDeferToCaret(target: EventTarget | null, key: string): boolean {
|
|
177
|
+
if (!(target instanceof HTMLElement)) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// contenteditable (covers an element with contenteditable="" / "true" and
|
|
182
|
+
// descendants that inherit it). `isContentEditable` is the reliable property
|
|
183
|
+
// in the browser; fall back to the nearest `contenteditable` ancestor for
|
|
184
|
+
// environments (e.g. jsdom) that don't implement the property.
|
|
185
|
+
const editableRoot = getContentEditableRoot(target);
|
|
186
|
+
if (editableRoot) {
|
|
187
|
+
const selection =
|
|
188
|
+
typeof window !== 'undefined' ? window.getSelection() : null;
|
|
189
|
+
// A non-collapsed selection means the user is selecting text — never steal.
|
|
190
|
+
if (selection && selection.rangeCount > 0 && !selection.isCollapsed) {
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
// Collapsed caret (or no selection API): defer whenever the editor has
|
|
194
|
+
// content, so arrow keys stay with the active rich-text editor.
|
|
195
|
+
return (editableRoot.textContent ?? '').length > 0;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const isTextarea = target.tagName === 'TEXTAREA';
|
|
199
|
+
const isTextInput =
|
|
200
|
+
target.tagName === 'INPUT' &&
|
|
201
|
+
TEXT_INPUT_TYPES.has((target as HTMLInputElement).type);
|
|
202
|
+
if (!isTextarea && !isTextInput) {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
const input = target as HTMLInputElement | HTMLTextAreaElement;
|
|
206
|
+
const {selectionStart, selectionEnd, value} = input;
|
|
207
|
+
// A non-collapsed selection means the user is selecting text — never steal.
|
|
208
|
+
if (selectionStart !== selectionEnd) {
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
// number inputs report null selection; treat as "always defer" for caret keys.
|
|
212
|
+
if (selectionStart == null) {
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
if (key === 'ArrowLeft' || key === 'ArrowUp' || key === 'Home') {
|
|
216
|
+
// Defer unless the caret is at the very start.
|
|
217
|
+
return selectionStart > 0;
|
|
218
|
+
}
|
|
219
|
+
if (key === 'ArrowRight' || key === 'ArrowDown' || key === 'End') {
|
|
220
|
+
// Defer unless the caret is at the very end.
|
|
221
|
+
return selectionStart < value.length;
|
|
222
|
+
}
|
|
223
|
+
return false;
|
|
74
224
|
}
|
|
75
225
|
|
|
76
226
|
/**
|
|
@@ -83,6 +233,12 @@ export interface UseListFocusReturn<T extends HTMLElement = HTMLElement> {
|
|
|
83
233
|
* - End: Move to last item
|
|
84
234
|
* - Escape: Custom callback (e.g., close menu)
|
|
85
235
|
*
|
|
236
|
+
* By default the hook only *moves* focus and leaves `tabindex` management to
|
|
237
|
+
* the caller. Opt into {@link UseListFocusOptions.hasRovingTabIndex} for a hook
|
|
238
|
+
* that owns a single tab stop (roving tabindex) across the items — stamping and
|
|
239
|
+
* repairing it as items mount/unmount or toggle disabled — for toolbars,
|
|
240
|
+
* segmented controls, tab strips, and similar composite widgets.
|
|
241
|
+
*
|
|
86
242
|
* @example
|
|
87
243
|
* ```
|
|
88
244
|
* const {listRef, handleKeyDown} = useListFocus({
|
|
@@ -93,6 +249,22 @@ export interface UseListFocusReturn<T extends HTMLElement = HTMLElement> {
|
|
|
93
249
|
* {items.map(item => <div role="menuitem" tabIndex={0}>{item}</div>)}
|
|
94
250
|
* </div>
|
|
95
251
|
* ```
|
|
252
|
+
*
|
|
253
|
+
* Roving-tabindex composite (e.g. a toolbar):
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* ```
|
|
257
|
+
* const {listRef, handleKeyDown, handleFocus} = useListFocus<HTMLDivElement>({
|
|
258
|
+
* itemSelector: 'button, input, [tabindex]',
|
|
259
|
+
* orientation: 'horizontal',
|
|
260
|
+
* hasRovingTabIndex: true,
|
|
261
|
+
* hasCaretGuard: true,
|
|
262
|
+
* });
|
|
263
|
+
*
|
|
264
|
+
* <div ref={listRef} role="toolbar" onKeyDown={handleKeyDown} onFocus={handleFocus}>
|
|
265
|
+
* {children}
|
|
266
|
+
* </div>
|
|
267
|
+
* ```
|
|
96
268
|
*/
|
|
97
269
|
export function useListFocus<T extends HTMLElement = HTMLElement>(
|
|
98
270
|
options: UseListFocusOptions = {},
|
|
@@ -102,10 +274,27 @@ export function useListFocus<T extends HTMLElement = HTMLElement>(
|
|
|
102
274
|
wrap = true,
|
|
103
275
|
onEscape,
|
|
104
276
|
orientation = 'vertical',
|
|
277
|
+
hasHomeEnd = true,
|
|
278
|
+
isRtl = false,
|
|
279
|
+
hasRovingTabIndex = false,
|
|
280
|
+
hasCaretGuard = false,
|
|
105
281
|
} = options;
|
|
106
282
|
|
|
107
283
|
const listRef = useRef<T>(null);
|
|
108
284
|
|
|
285
|
+
/**
|
|
286
|
+
* Whether an item is disabled and therefore cannot receive DOM focus.
|
|
287
|
+
* A `.focus()` call on such an element silently no-ops, so navigation must
|
|
288
|
+
* skip these to avoid freezing on a disabled item (menus-4, navigation-5).
|
|
289
|
+
*/
|
|
290
|
+
const isItemDisabled = useCallback((el: HTMLElement): boolean => {
|
|
291
|
+
return (
|
|
292
|
+
el.getAttribute('aria-disabled') === 'true' ||
|
|
293
|
+
(el as HTMLButtonElement).disabled === true ||
|
|
294
|
+
el.hasAttribute('disabled')
|
|
295
|
+
);
|
|
296
|
+
}, []);
|
|
297
|
+
|
|
109
298
|
/**
|
|
110
299
|
* Get all focusable items in the list.
|
|
111
300
|
*/
|
|
@@ -118,6 +307,42 @@ export function useListFocus<T extends HTMLElement = HTMLElement>(
|
|
|
118
307
|
);
|
|
119
308
|
}, [itemSelector]);
|
|
120
309
|
|
|
310
|
+
/**
|
|
311
|
+
* Find the next enabled item index from `start`, moving by `step`, optionally
|
|
312
|
+
* wrapping. Returns -1 when no enabled item exists in range. Skipping
|
|
313
|
+
* disabled items here (rather than relying on the selector) keeps navigation
|
|
314
|
+
* from stalling on an item whose `.focus()` silently no-ops.
|
|
315
|
+
*/
|
|
316
|
+
const findEnabledIndex = useCallback(
|
|
317
|
+
(
|
|
318
|
+
items: HTMLElement[],
|
|
319
|
+
start: number,
|
|
320
|
+
step: 1 | -1,
|
|
321
|
+
shouldWrap: boolean,
|
|
322
|
+
): number => {
|
|
323
|
+
const count = items.length;
|
|
324
|
+
if (count === 0) {
|
|
325
|
+
return -1;
|
|
326
|
+
}
|
|
327
|
+
let index = start;
|
|
328
|
+
for (let i = 0; i < count; i++) {
|
|
329
|
+
if (index < 0 || index >= count) {
|
|
330
|
+
if (!shouldWrap) {
|
|
331
|
+
return -1;
|
|
332
|
+
}
|
|
333
|
+
index = (index + count) % count;
|
|
334
|
+
}
|
|
335
|
+
const item = items[index];
|
|
336
|
+
if (item && !isItemDisabled(item)) {
|
|
337
|
+
return index;
|
|
338
|
+
}
|
|
339
|
+
index += step;
|
|
340
|
+
}
|
|
341
|
+
return -1;
|
|
342
|
+
},
|
|
343
|
+
[isItemDisabled],
|
|
344
|
+
);
|
|
345
|
+
|
|
121
346
|
/**
|
|
122
347
|
* Get the currently focused item index.
|
|
123
348
|
*/
|
|
@@ -127,6 +352,65 @@ export function useListFocus<T extends HTMLElement = HTMLElement>(
|
|
|
127
352
|
return items.findIndex(item => item === active || item.contains(active));
|
|
128
353
|
}, [getItems]);
|
|
129
354
|
|
|
355
|
+
// --- Roving tabindex ownership (opt-in via `hasRovingTabIndex`) -------------
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Set `tabindex` on an item, but only when it differs (avoids redundant DOM
|
|
359
|
+
* writes). Uses setAttribute so the value reflects even for elements (like
|
|
360
|
+
* `<button>`) whose default tabIndex is already 0.
|
|
361
|
+
*/
|
|
362
|
+
const setTabIndex = useCallback((el: HTMLElement, value: 0 | -1) => {
|
|
363
|
+
if (el.getAttribute('tabindex') !== String(value)) {
|
|
364
|
+
el.setAttribute('tabindex', String(value));
|
|
365
|
+
}
|
|
366
|
+
}, []);
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Stamp the roving tab stop: exactly one enabled item is tabbable (0), the
|
|
370
|
+
* rest are -1. Prefer keeping the currently-tabbable item if it is still
|
|
371
|
+
* enabled; otherwise promote the first enabled item (tab-stop repair).
|
|
372
|
+
*/
|
|
373
|
+
const syncTabStops = useCallback(() => {
|
|
374
|
+
const items = getItems();
|
|
375
|
+
const enabled = items.filter(el => !isItemDisabled(el));
|
|
376
|
+
if (enabled.length === 0) {
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
const current = enabled.find(el => el.getAttribute('tabindex') === '0');
|
|
380
|
+
const tabbable = current ?? enabled[0];
|
|
381
|
+
for (const el of items) {
|
|
382
|
+
setTabIndex(el, el === tabbable ? 0 : -1);
|
|
383
|
+
}
|
|
384
|
+
}, [getItems, isItemDisabled, setTabIndex]);
|
|
385
|
+
|
|
386
|
+
// Keep the tab stop valid across renders (items added/removed, disabled
|
|
387
|
+
// toggled). Runs after every commit but only when roving tabindex is on.
|
|
388
|
+
useIsomorphicLayoutEffect(() => {
|
|
389
|
+
if (hasRovingTabIndex) {
|
|
390
|
+
syncTabStops();
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Move focus to `items[index]`. When roving tabindex is enabled, also move
|
|
396
|
+
* the tab stop so `index` becomes the sole tabbable item.
|
|
397
|
+
*/
|
|
398
|
+
const focusIndex = useCallback(
|
|
399
|
+
(items: HTMLElement[], index: number) => {
|
|
400
|
+
const target = items[index];
|
|
401
|
+
if (!target) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
if (hasRovingTabIndex) {
|
|
405
|
+
for (const el of items) {
|
|
406
|
+
setTabIndex(el, el === target ? 0 : -1);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
target.focus();
|
|
410
|
+
},
|
|
411
|
+
[hasRovingTabIndex, setTabIndex],
|
|
412
|
+
);
|
|
413
|
+
|
|
130
414
|
/**
|
|
131
415
|
* Focus an item by index, clamping to valid range.
|
|
132
416
|
*/
|
|
@@ -137,82 +421,135 @@ export function useListFocus<T extends HTMLElement = HTMLElement>(
|
|
|
137
421
|
return;
|
|
138
422
|
}
|
|
139
423
|
const clampedIndex = Math.max(0, Math.min(index, items.length - 1));
|
|
140
|
-
items
|
|
424
|
+
focusIndex(items, clampedIndex);
|
|
141
425
|
},
|
|
142
|
-
[getItems],
|
|
426
|
+
[getItems, focusIndex],
|
|
143
427
|
);
|
|
144
428
|
|
|
145
429
|
/**
|
|
146
|
-
* Focus the first item.
|
|
430
|
+
* Focus the first enabled item. Returns true when an item was focused.
|
|
147
431
|
*/
|
|
148
|
-
const focusFirst = useCallback(() => {
|
|
432
|
+
const focusFirst = useCallback((): boolean => {
|
|
149
433
|
const items = getItems();
|
|
150
|
-
items
|
|
151
|
-
|
|
434
|
+
const index = findEnabledIndex(items, 0, 1, false);
|
|
435
|
+
if (index !== -1) {
|
|
436
|
+
focusIndex(items, index);
|
|
437
|
+
return true;
|
|
438
|
+
}
|
|
439
|
+
return false;
|
|
440
|
+
}, [getItems, findEnabledIndex, focusIndex]);
|
|
152
441
|
|
|
153
442
|
/**
|
|
154
|
-
* Focus the last item.
|
|
443
|
+
* Focus the last enabled item. Returns true when an item was focused.
|
|
155
444
|
*/
|
|
156
|
-
const focusLast = useCallback(() => {
|
|
445
|
+
const focusLast = useCallback((): boolean => {
|
|
157
446
|
const items = getItems();
|
|
158
|
-
items
|
|
159
|
-
|
|
447
|
+
const index = findEnabledIndex(items, items.length - 1, -1, false);
|
|
448
|
+
if (index !== -1) {
|
|
449
|
+
focusIndex(items, index);
|
|
450
|
+
return true;
|
|
451
|
+
}
|
|
452
|
+
return false;
|
|
453
|
+
}, [getItems, findEnabledIndex, focusIndex]);
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Keep the roving stop pointing at whatever ended up focused (e.g. a click
|
|
457
|
+
* or programmatic focus) so the next Tab behaves correctly. No-op unless
|
|
458
|
+
* roving tabindex is enabled.
|
|
459
|
+
*/
|
|
460
|
+
const handleFocus = useCallback(() => {
|
|
461
|
+
if (hasRovingTabIndex) {
|
|
462
|
+
syncTabStops();
|
|
463
|
+
}
|
|
464
|
+
}, [hasRovingTabIndex, syncTabStops]);
|
|
160
465
|
|
|
161
466
|
/**
|
|
162
467
|
* Handle keyboard navigation.
|
|
163
468
|
*/
|
|
164
469
|
const handleKeyDown = useCallback(
|
|
165
470
|
(e: React.KeyboardEvent) => {
|
|
471
|
+
// Let browser/OS shortcut chords (Ctrl/Cmd/Alt + key) through untouched.
|
|
472
|
+
if (e.ctrlKey || e.metaKey || e.altKey) {
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// Escape is handled regardless of orientation. Preserve the historical
|
|
477
|
+
// behavior of always consuming Escape here (preventDefault) so consumers
|
|
478
|
+
// that relied on it are unaffected.
|
|
479
|
+
if (e.key === 'Escape') {
|
|
480
|
+
e.preventDefault();
|
|
481
|
+
onEscape?.();
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const horizontal = orientation === 'horizontal' || orientation === 'both';
|
|
486
|
+
const vertical = orientation === 'vertical' || orientation === 'both';
|
|
487
|
+
|
|
488
|
+
// Resolve which keys advance vs retreat, honoring RTL for horizontal.
|
|
489
|
+
const nextKeys: string[] = [];
|
|
490
|
+
const prevKeys: string[] = [];
|
|
491
|
+
if (horizontal) {
|
|
492
|
+
nextKeys.push(isRtl ? 'ArrowLeft' : 'ArrowRight');
|
|
493
|
+
prevKeys.push(isRtl ? 'ArrowRight' : 'ArrowLeft');
|
|
494
|
+
}
|
|
495
|
+
if (vertical) {
|
|
496
|
+
nextKeys.push('ArrowDown');
|
|
497
|
+
prevKeys.push('ArrowUp');
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const isNext = nextKeys.includes(e.key);
|
|
501
|
+
const isPrev = prevKeys.includes(e.key);
|
|
502
|
+
const isHome = hasHomeEnd && e.key === 'Home';
|
|
503
|
+
const isEnd = hasHomeEnd && e.key === 'End';
|
|
504
|
+
|
|
505
|
+
if (!isNext && !isPrev && !isHome && !isEnd) {
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Never hijack a caret key from a nested text input mid-line. Check both
|
|
510
|
+
// the React event target and the actual focused element (events may
|
|
511
|
+
// bubble up to the container).
|
|
512
|
+
if (
|
|
513
|
+
hasCaretGuard &&
|
|
514
|
+
(shouldDeferToCaret(e.target, e.key) ||
|
|
515
|
+
shouldDeferToCaret(document.activeElement, e.key))
|
|
516
|
+
) {
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
|
|
166
520
|
const currentIndex = getCurrentIndex();
|
|
167
521
|
const items = getItems();
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
case nextKey: {
|
|
175
|
-
if (currentIndex === -1) {
|
|
176
|
-
items[0]?.focus();
|
|
177
|
-
} else if (currentIndex < items.length - 1) {
|
|
178
|
-
items[currentIndex + 1]?.focus();
|
|
179
|
-
} else if (wrap) {
|
|
180
|
-
items[0]?.focus();
|
|
181
|
-
}
|
|
182
|
-
break;
|
|
522
|
+
|
|
523
|
+
if (isNext) {
|
|
524
|
+
const from = currentIndex === -1 ? 0 : currentIndex + 1;
|
|
525
|
+
const next = findEnabledIndex(items, from, 1, wrap);
|
|
526
|
+
if (next !== -1) {
|
|
527
|
+
focusIndex(items, next);
|
|
183
528
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
} else if (wrap) {
|
|
190
|
-
items[items.length - 1]?.focus();
|
|
191
|
-
}
|
|
192
|
-
break;
|
|
529
|
+
} else if (isPrev) {
|
|
530
|
+
const from = currentIndex === -1 ? items.length - 1 : currentIndex - 1;
|
|
531
|
+
const prev = findEnabledIndex(items, from, -1, wrap);
|
|
532
|
+
if (prev !== -1) {
|
|
533
|
+
focusIndex(items, prev);
|
|
193
534
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
focusLast();
|
|
199
|
-
break;
|
|
200
|
-
case 'Escape':
|
|
201
|
-
onEscape?.();
|
|
202
|
-
break;
|
|
203
|
-
default:
|
|
204
|
-
handled = false;
|
|
535
|
+
} else if (isHome) {
|
|
536
|
+
focusFirst();
|
|
537
|
+
} else if (isEnd) {
|
|
538
|
+
focusLast();
|
|
205
539
|
}
|
|
206
540
|
|
|
207
|
-
|
|
208
|
-
e.preventDefault();
|
|
209
|
-
}
|
|
541
|
+
e.preventDefault();
|
|
210
542
|
},
|
|
211
543
|
[
|
|
212
544
|
getCurrentIndex,
|
|
213
545
|
getItems,
|
|
214
546
|
wrap,
|
|
215
547
|
orientation,
|
|
548
|
+
isRtl,
|
|
549
|
+
hasHomeEnd,
|
|
550
|
+
hasCaretGuard,
|
|
551
|
+
findEnabledIndex,
|
|
552
|
+
focusIndex,
|
|
216
553
|
focusFirst,
|
|
217
554
|
focusLast,
|
|
218
555
|
onEscape,
|
|
@@ -222,6 +559,7 @@ export function useListFocus<T extends HTMLElement = HTMLElement>(
|
|
|
222
559
|
return {
|
|
223
560
|
listRef,
|
|
224
561
|
handleKeyDown,
|
|
562
|
+
handleFocus,
|
|
225
563
|
focusItem,
|
|
226
564
|
focusFirst,
|
|
227
565
|
focusLast,
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
|
|
4
|
+
import {renderHook, act} from '@testing-library/react';
|
|
5
|
+
import {useLongPress} from './useLongPress';
|
|
6
|
+
|
|
7
|
+
// Minimal React.TouchEvent stand-in carrying only what the hook reads.
|
|
8
|
+
function touchEvent(touches: {clientX: number; clientY: number}[]) {
|
|
9
|
+
return {touches} as unknown as React.TouchEvent;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe('useLongPress', () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
vi.useFakeTimers();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
vi.useRealTimers();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('fires onLongPress with the start point after the delay', () => {
|
|
22
|
+
const onLongPress = vi.fn();
|
|
23
|
+
const {result} = renderHook(() => useLongPress({onLongPress}));
|
|
24
|
+
|
|
25
|
+
act(() => {
|
|
26
|
+
result.current.onTouchStart(touchEvent([{clientX: 10, clientY: 20}]));
|
|
27
|
+
});
|
|
28
|
+
expect(onLongPress).not.toHaveBeenCalled();
|
|
29
|
+
|
|
30
|
+
act(() => {
|
|
31
|
+
vi.advanceTimersByTime(500);
|
|
32
|
+
});
|
|
33
|
+
expect(onLongPress).toHaveBeenCalledTimes(1);
|
|
34
|
+
expect(onLongPress).toHaveBeenCalledWith({x: 10, y: 20});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('respects a custom delayMs', () => {
|
|
38
|
+
const onLongPress = vi.fn();
|
|
39
|
+
const {result} = renderHook(() =>
|
|
40
|
+
useLongPress({onLongPress, delayMs: 1000}),
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
act(() => {
|
|
44
|
+
result.current.onTouchStart(touchEvent([{clientX: 1, clientY: 2}]));
|
|
45
|
+
vi.advanceTimersByTime(999);
|
|
46
|
+
});
|
|
47
|
+
expect(onLongPress).not.toHaveBeenCalled();
|
|
48
|
+
|
|
49
|
+
act(() => {
|
|
50
|
+
vi.advanceTimersByTime(1);
|
|
51
|
+
});
|
|
52
|
+
expect(onLongPress).toHaveBeenCalledTimes(1);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('does nothing when disabled', () => {
|
|
56
|
+
const onLongPress = vi.fn();
|
|
57
|
+
const {result} = renderHook(() =>
|
|
58
|
+
useLongPress({onLongPress, disabled: true}),
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
act(() => {
|
|
62
|
+
result.current.onTouchStart(touchEvent([{clientX: 0, clientY: 0}]));
|
|
63
|
+
vi.advanceTimersByTime(500);
|
|
64
|
+
});
|
|
65
|
+
expect(onLongPress).not.toHaveBeenCalled();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('ignores multi-touch starts', () => {
|
|
69
|
+
const onLongPress = vi.fn();
|
|
70
|
+
const {result} = renderHook(() => useLongPress({onLongPress}));
|
|
71
|
+
|
|
72
|
+
act(() => {
|
|
73
|
+
result.current.onTouchStart(
|
|
74
|
+
touchEvent([
|
|
75
|
+
{clientX: 0, clientY: 0},
|
|
76
|
+
{clientX: 5, clientY: 5},
|
|
77
|
+
]),
|
|
78
|
+
);
|
|
79
|
+
vi.advanceTimersByTime(500);
|
|
80
|
+
});
|
|
81
|
+
expect(onLongPress).not.toHaveBeenCalled();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('cancels when the finger moves past the threshold', () => {
|
|
85
|
+
const onLongPress = vi.fn();
|
|
86
|
+
const {result} = renderHook(() => useLongPress({onLongPress}));
|
|
87
|
+
|
|
88
|
+
act(() => {
|
|
89
|
+
result.current.onTouchStart(touchEvent([{clientX: 0, clientY: 0}]));
|
|
90
|
+
// Move past the default 10px threshold.
|
|
91
|
+
result.current.onTouchMove(touchEvent([{clientX: 15, clientY: 0}]));
|
|
92
|
+
vi.advanceTimersByTime(500);
|
|
93
|
+
});
|
|
94
|
+
expect(onLongPress).not.toHaveBeenCalled();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('does not cancel for movement within the threshold', () => {
|
|
98
|
+
const onLongPress = vi.fn();
|
|
99
|
+
const {result} = renderHook(() => useLongPress({onLongPress}));
|
|
100
|
+
|
|
101
|
+
act(() => {
|
|
102
|
+
result.current.onTouchStart(touchEvent([{clientX: 0, clientY: 0}]));
|
|
103
|
+
result.current.onTouchMove(touchEvent([{clientX: 5, clientY: 5}]));
|
|
104
|
+
vi.advanceTimersByTime(500);
|
|
105
|
+
});
|
|
106
|
+
expect(onLongPress).toHaveBeenCalledTimes(1);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('cancels on touch end', () => {
|
|
110
|
+
const onLongPress = vi.fn();
|
|
111
|
+
const {result} = renderHook(() => useLongPress({onLongPress}));
|
|
112
|
+
|
|
113
|
+
act(() => {
|
|
114
|
+
result.current.onTouchStart(touchEvent([{clientX: 0, clientY: 0}]));
|
|
115
|
+
result.current.onTouchEnd();
|
|
116
|
+
vi.advanceTimersByTime(500);
|
|
117
|
+
});
|
|
118
|
+
expect(onLongPress).not.toHaveBeenCalled();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('cancels the pending timer on unmount', () => {
|
|
122
|
+
const onLongPress = vi.fn();
|
|
123
|
+
const {result, unmount} = renderHook(() => useLongPress({onLongPress}));
|
|
124
|
+
|
|
125
|
+
act(() => {
|
|
126
|
+
result.current.onTouchStart(touchEvent([{clientX: 0, clientY: 0}]));
|
|
127
|
+
});
|
|
128
|
+
unmount();
|
|
129
|
+
act(() => {
|
|
130
|
+
vi.advanceTimersByTime(500);
|
|
131
|
+
});
|
|
132
|
+
expect(onLongPress).not.toHaveBeenCalled();
|
|
133
|
+
});
|
|
134
|
+
});
|