@astryxdesign/core 0.1.2 → 0.1.3
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,14 +4,23 @@
|
|
|
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
|
import { useCallback, useRef } from 'react';
|
|
16
|
+
import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect.js";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Navigation orientation for a linear list.
|
|
20
|
+
* - `'horizontal'`: ArrowLeft/ArrowRight move between items.
|
|
21
|
+
* - `'vertical'`: ArrowUp/ArrowDown move between items.
|
|
22
|
+
* - `'both'`: all four arrows move between items (in linear DOM order).
|
|
23
|
+
*/
|
|
15
24
|
|
|
16
25
|
/**
|
|
17
26
|
* Configuration for list focus behavior
|
|
@@ -21,6 +30,91 @@ import { useCallback, useRef } from 'react';
|
|
|
21
30
|
* Return type for useListFocus hook
|
|
22
31
|
*/
|
|
23
32
|
|
|
33
|
+
// Text-editing inputs whose caret must not be hijacked by arrow navigation.
|
|
34
|
+
const TEXT_INPUT_TYPES = new Set(['text', 'search', 'url', 'tel', 'email', 'password', 'number']);
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The nearest `contenteditable` root for `el`, or null when `el` is not inside
|
|
38
|
+
* an editable region. Prefers the browser's `isContentEditable` property and
|
|
39
|
+
* falls back to the closest `[contenteditable]` ancestor whose value is not
|
|
40
|
+
* `"false"` (so environments without `isContentEditable` still work).
|
|
41
|
+
*/
|
|
42
|
+
function getContentEditableRoot(el) {
|
|
43
|
+
if (el.isContentEditable) {
|
|
44
|
+
return el;
|
|
45
|
+
}
|
|
46
|
+
const candidate = el.closest('[contenteditable]');
|
|
47
|
+
if (candidate && candidate.getAttribute('contenteditable') !== 'false') {
|
|
48
|
+
return candidate;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Whether an arrow/Home/End key should be left to the browser because the
|
|
55
|
+
* event target is a text-editing element whose caret is not yet at the boundary
|
|
56
|
+
* in the direction of travel (or a selection is present). Returns true when the
|
|
57
|
+
* list should NOT steal the key.
|
|
58
|
+
*
|
|
59
|
+
* Covers three editing surfaces:
|
|
60
|
+
* - `<textarea>` and text-type `<input>` — use `selectionStart`/`selectionEnd`
|
|
61
|
+
* to steal only at the boundary in the travel direction.
|
|
62
|
+
* - `contenteditable` (rich-text editor / chat composer) — precise caret
|
|
63
|
+
* boundary detection in an arbitrary editable subtree is fragile, so we err
|
|
64
|
+
* on the side of never hijacking an active editor: defer on every caret key
|
|
65
|
+
* whenever focus is inside a non-empty contenteditable. (An empty editable
|
|
66
|
+
* has nothing to caret through, so navigation may proceed.)
|
|
67
|
+
*/
|
|
68
|
+
function shouldDeferToCaret(target, key) {
|
|
69
|
+
if (!(target instanceof HTMLElement)) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// contenteditable (covers an element with contenteditable="" / "true" and
|
|
74
|
+
// descendants that inherit it). `isContentEditable` is the reliable property
|
|
75
|
+
// in the browser; fall back to the nearest `contenteditable` ancestor for
|
|
76
|
+
// environments (e.g. jsdom) that don't implement the property.
|
|
77
|
+
const editableRoot = getContentEditableRoot(target);
|
|
78
|
+
if (editableRoot) {
|
|
79
|
+
const selection = typeof window !== 'undefined' ? window.getSelection() : null;
|
|
80
|
+
// A non-collapsed selection means the user is selecting text — never steal.
|
|
81
|
+
if (selection && selection.rangeCount > 0 && !selection.isCollapsed) {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
// Collapsed caret (or no selection API): defer whenever the editor has
|
|
85
|
+
// content, so arrow keys stay with the active rich-text editor.
|
|
86
|
+
return (editableRoot.textContent ?? '').length > 0;
|
|
87
|
+
}
|
|
88
|
+
const isTextarea = target.tagName === 'TEXTAREA';
|
|
89
|
+
const isTextInput = target.tagName === 'INPUT' && TEXT_INPUT_TYPES.has(target.type);
|
|
90
|
+
if (!isTextarea && !isTextInput) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
const input = target;
|
|
94
|
+
const {
|
|
95
|
+
selectionStart,
|
|
96
|
+
selectionEnd,
|
|
97
|
+
value
|
|
98
|
+
} = input;
|
|
99
|
+
// A non-collapsed selection means the user is selecting text — never steal.
|
|
100
|
+
if (selectionStart !== selectionEnd) {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
// number inputs report null selection; treat as "always defer" for caret keys.
|
|
104
|
+
if (selectionStart == null) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
if (key === 'ArrowLeft' || key === 'ArrowUp' || key === 'Home') {
|
|
108
|
+
// Defer unless the caret is at the very start.
|
|
109
|
+
return selectionStart > 0;
|
|
110
|
+
}
|
|
111
|
+
if (key === 'ArrowRight' || key === 'ArrowDown' || key === 'End') {
|
|
112
|
+
// Defer unless the caret is at the very end.
|
|
113
|
+
return selectionStart < value.length;
|
|
114
|
+
}
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
|
|
24
118
|
/**
|
|
25
119
|
* Hook for managing keyboard navigation within a linear list.
|
|
26
120
|
*
|
|
@@ -31,6 +125,12 @@ import { useCallback, useRef } from 'react';
|
|
|
31
125
|
* - End: Move to last item
|
|
32
126
|
* - Escape: Custom callback (e.g., close menu)
|
|
33
127
|
*
|
|
128
|
+
* By default the hook only *moves* focus and leaves `tabindex` management to
|
|
129
|
+
* the caller. Opt into {@link UseListFocusOptions.hasRovingTabIndex} for a hook
|
|
130
|
+
* that owns a single tab stop (roving tabindex) across the items — stamping and
|
|
131
|
+
* repairing it as items mount/unmount or toggle disabled — for toolbars,
|
|
132
|
+
* segmented controls, tab strips, and similar composite widgets.
|
|
133
|
+
*
|
|
34
134
|
* @example
|
|
35
135
|
* ```
|
|
36
136
|
* const {listRef, handleKeyDown} = useListFocus({
|
|
@@ -41,16 +141,45 @@ import { useCallback, useRef } from 'react';
|
|
|
41
141
|
* {items.map(item => <div role="menuitem" tabIndex={0}>{item}</div>)}
|
|
42
142
|
* </div>
|
|
43
143
|
* ```
|
|
144
|
+
*
|
|
145
|
+
* Roving-tabindex composite (e.g. a toolbar):
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```
|
|
149
|
+
* const {listRef, handleKeyDown, handleFocus} = useListFocus<HTMLDivElement>({
|
|
150
|
+
* itemSelector: 'button, input, [tabindex]',
|
|
151
|
+
* orientation: 'horizontal',
|
|
152
|
+
* hasRovingTabIndex: true,
|
|
153
|
+
* hasCaretGuard: true,
|
|
154
|
+
* });
|
|
155
|
+
*
|
|
156
|
+
* <div ref={listRef} role="toolbar" onKeyDown={handleKeyDown} onFocus={handleFocus}>
|
|
157
|
+
* {children}
|
|
158
|
+
* </div>
|
|
159
|
+
* ```
|
|
44
160
|
*/
|
|
45
161
|
export function useListFocus(options = {}) {
|
|
46
162
|
const {
|
|
47
163
|
itemSelector = '[role="menuitem"]',
|
|
48
164
|
wrap = true,
|
|
49
165
|
onEscape,
|
|
50
|
-
orientation = 'vertical'
|
|
166
|
+
orientation = 'vertical',
|
|
167
|
+
hasHomeEnd = true,
|
|
168
|
+
isRtl = false,
|
|
169
|
+
hasRovingTabIndex = false,
|
|
170
|
+
hasCaretGuard = false
|
|
51
171
|
} = options;
|
|
52
172
|
const listRef = useRef(null);
|
|
53
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Whether an item is disabled and therefore cannot receive DOM focus.
|
|
176
|
+
* A `.focus()` call on such an element silently no-ops, so navigation must
|
|
177
|
+
* skip these to avoid freezing on a disabled item (menus-4, navigation-5).
|
|
178
|
+
*/
|
|
179
|
+
const isItemDisabled = useCallback(el => {
|
|
180
|
+
return el.getAttribute('aria-disabled') === 'true' || el.disabled === true || el.hasAttribute('disabled');
|
|
181
|
+
}, []);
|
|
182
|
+
|
|
54
183
|
/**
|
|
55
184
|
* Get all focusable items in the list.
|
|
56
185
|
*/
|
|
@@ -61,6 +190,34 @@ export function useListFocus(options = {}) {
|
|
|
61
190
|
return Array.from(listRef.current.querySelectorAll(itemSelector));
|
|
62
191
|
}, [itemSelector]);
|
|
63
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Find the next enabled item index from `start`, moving by `step`, optionally
|
|
195
|
+
* wrapping. Returns -1 when no enabled item exists in range. Skipping
|
|
196
|
+
* disabled items here (rather than relying on the selector) keeps navigation
|
|
197
|
+
* from stalling on an item whose `.focus()` silently no-ops.
|
|
198
|
+
*/
|
|
199
|
+
const findEnabledIndex = useCallback((items, start, step, shouldWrap) => {
|
|
200
|
+
const count = items.length;
|
|
201
|
+
if (count === 0) {
|
|
202
|
+
return -1;
|
|
203
|
+
}
|
|
204
|
+
let index = start;
|
|
205
|
+
for (let i = 0; i < count; i++) {
|
|
206
|
+
if (index < 0 || index >= count) {
|
|
207
|
+
if (!shouldWrap) {
|
|
208
|
+
return -1;
|
|
209
|
+
}
|
|
210
|
+
index = (index + count) % count;
|
|
211
|
+
}
|
|
212
|
+
const item = items[index];
|
|
213
|
+
if (item && !isItemDisabled(item)) {
|
|
214
|
+
return index;
|
|
215
|
+
}
|
|
216
|
+
index += step;
|
|
217
|
+
}
|
|
218
|
+
return -1;
|
|
219
|
+
}, [isItemDisabled]);
|
|
220
|
+
|
|
64
221
|
/**
|
|
65
222
|
* Get the currently focused item index.
|
|
66
223
|
*/
|
|
@@ -70,6 +227,62 @@ export function useListFocus(options = {}) {
|
|
|
70
227
|
return items.findIndex(item => item === active || item.contains(active));
|
|
71
228
|
}, [getItems]);
|
|
72
229
|
|
|
230
|
+
// --- Roving tabindex ownership (opt-in via `hasRovingTabIndex`) -------------
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Set `tabindex` on an item, but only when it differs (avoids redundant DOM
|
|
234
|
+
* writes). Uses setAttribute so the value reflects even for elements (like
|
|
235
|
+
* `<button>`) whose default tabIndex is already 0.
|
|
236
|
+
*/
|
|
237
|
+
const setTabIndex = useCallback((el, value) => {
|
|
238
|
+
if (el.getAttribute('tabindex') !== String(value)) {
|
|
239
|
+
el.setAttribute('tabindex', String(value));
|
|
240
|
+
}
|
|
241
|
+
}, []);
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Stamp the roving tab stop: exactly one enabled item is tabbable (0), the
|
|
245
|
+
* rest are -1. Prefer keeping the currently-tabbable item if it is still
|
|
246
|
+
* enabled; otherwise promote the first enabled item (tab-stop repair).
|
|
247
|
+
*/
|
|
248
|
+
const syncTabStops = useCallback(() => {
|
|
249
|
+
const items = getItems();
|
|
250
|
+
const enabled = items.filter(el => !isItemDisabled(el));
|
|
251
|
+
if (enabled.length === 0) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
const current = enabled.find(el => el.getAttribute('tabindex') === '0');
|
|
255
|
+
const tabbable = current ?? enabled[0];
|
|
256
|
+
for (const el of items) {
|
|
257
|
+
setTabIndex(el, el === tabbable ? 0 : -1);
|
|
258
|
+
}
|
|
259
|
+
}, [getItems, isItemDisabled, setTabIndex]);
|
|
260
|
+
|
|
261
|
+
// Keep the tab stop valid across renders (items added/removed, disabled
|
|
262
|
+
// toggled). Runs after every commit but only when roving tabindex is on.
|
|
263
|
+
useIsomorphicLayoutEffect(() => {
|
|
264
|
+
if (hasRovingTabIndex) {
|
|
265
|
+
syncTabStops();
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Move focus to `items[index]`. When roving tabindex is enabled, also move
|
|
271
|
+
* the tab stop so `index` becomes the sole tabbable item.
|
|
272
|
+
*/
|
|
273
|
+
const focusIndex = useCallback((items, index) => {
|
|
274
|
+
const target = items[index];
|
|
275
|
+
if (!target) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
if (hasRovingTabIndex) {
|
|
279
|
+
for (const el of items) {
|
|
280
|
+
setTabIndex(el, el === target ? 0 : -1);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
target.focus();
|
|
284
|
+
}, [hasRovingTabIndex, setTabIndex]);
|
|
285
|
+
|
|
73
286
|
/**
|
|
74
287
|
* Focus an item by index, clamping to valid range.
|
|
75
288
|
*/
|
|
@@ -79,76 +292,116 @@ export function useListFocus(options = {}) {
|
|
|
79
292
|
return;
|
|
80
293
|
}
|
|
81
294
|
const clampedIndex = Math.max(0, Math.min(index, items.length - 1));
|
|
82
|
-
items
|
|
83
|
-
}, [getItems]);
|
|
295
|
+
focusIndex(items, clampedIndex);
|
|
296
|
+
}, [getItems, focusIndex]);
|
|
84
297
|
|
|
85
298
|
/**
|
|
86
|
-
* Focus the first item.
|
|
299
|
+
* Focus the first enabled item. Returns true when an item was focused.
|
|
87
300
|
*/
|
|
88
301
|
const focusFirst = useCallback(() => {
|
|
89
302
|
const items = getItems();
|
|
90
|
-
items
|
|
91
|
-
|
|
303
|
+
const index = findEnabledIndex(items, 0, 1, false);
|
|
304
|
+
if (index !== -1) {
|
|
305
|
+
focusIndex(items, index);
|
|
306
|
+
return true;
|
|
307
|
+
}
|
|
308
|
+
return false;
|
|
309
|
+
}, [getItems, findEnabledIndex, focusIndex]);
|
|
92
310
|
|
|
93
311
|
/**
|
|
94
|
-
* Focus the last item.
|
|
312
|
+
* Focus the last enabled item. Returns true when an item was focused.
|
|
95
313
|
*/
|
|
96
314
|
const focusLast = useCallback(() => {
|
|
97
315
|
const items = getItems();
|
|
98
|
-
items
|
|
99
|
-
|
|
316
|
+
const index = findEnabledIndex(items, items.length - 1, -1, false);
|
|
317
|
+
if (index !== -1) {
|
|
318
|
+
focusIndex(items, index);
|
|
319
|
+
return true;
|
|
320
|
+
}
|
|
321
|
+
return false;
|
|
322
|
+
}, [getItems, findEnabledIndex, focusIndex]);
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Keep the roving stop pointing at whatever ended up focused (e.g. a click
|
|
326
|
+
* or programmatic focus) so the next Tab behaves correctly. No-op unless
|
|
327
|
+
* roving tabindex is enabled.
|
|
328
|
+
*/
|
|
329
|
+
const handleFocus = useCallback(() => {
|
|
330
|
+
if (hasRovingTabIndex) {
|
|
331
|
+
syncTabStops();
|
|
332
|
+
}
|
|
333
|
+
}, [hasRovingTabIndex, syncTabStops]);
|
|
100
334
|
|
|
101
335
|
/**
|
|
102
336
|
* Handle keyboard navigation.
|
|
103
337
|
*/
|
|
104
338
|
const handleKeyDown = useCallback(e => {
|
|
339
|
+
// Let browser/OS shortcut chords (Ctrl/Cmd/Alt + key) through untouched.
|
|
340
|
+
if (e.ctrlKey || e.metaKey || e.altKey) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Escape is handled regardless of orientation. Preserve the historical
|
|
345
|
+
// behavior of always consuming Escape here (preventDefault) so consumers
|
|
346
|
+
// that relied on it are unaffected.
|
|
347
|
+
if (e.key === 'Escape') {
|
|
348
|
+
e.preventDefault();
|
|
349
|
+
onEscape?.();
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
const horizontal = orientation === 'horizontal' || orientation === 'both';
|
|
353
|
+
const vertical = orientation === 'vertical' || orientation === 'both';
|
|
354
|
+
|
|
355
|
+
// Resolve which keys advance vs retreat, honoring RTL for horizontal.
|
|
356
|
+
const nextKeys = [];
|
|
357
|
+
const prevKeys = [];
|
|
358
|
+
if (horizontal) {
|
|
359
|
+
nextKeys.push(isRtl ? 'ArrowLeft' : 'ArrowRight');
|
|
360
|
+
prevKeys.push(isRtl ? 'ArrowRight' : 'ArrowLeft');
|
|
361
|
+
}
|
|
362
|
+
if (vertical) {
|
|
363
|
+
nextKeys.push('ArrowDown');
|
|
364
|
+
prevKeys.push('ArrowUp');
|
|
365
|
+
}
|
|
366
|
+
const isNext = nextKeys.includes(e.key);
|
|
367
|
+
const isPrev = prevKeys.includes(e.key);
|
|
368
|
+
const isHome = hasHomeEnd && e.key === 'Home';
|
|
369
|
+
const isEnd = hasHomeEnd && e.key === 'End';
|
|
370
|
+
if (!isNext && !isPrev && !isHome && !isEnd) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Never hijack a caret key from a nested text input mid-line. Check both
|
|
375
|
+
// the React event target and the actual focused element (events may
|
|
376
|
+
// bubble up to the container).
|
|
377
|
+
if (hasCaretGuard && (shouldDeferToCaret(e.target, e.key) || shouldDeferToCaret(document.activeElement, e.key))) {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
105
380
|
const currentIndex = getCurrentIndex();
|
|
106
381
|
const items = getItems();
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
{
|
|
124
|
-
if (currentIndex === -1) {
|
|
125
|
-
items[items.length - 1]?.focus();
|
|
126
|
-
} else if (currentIndex > 0) {
|
|
127
|
-
items[currentIndex - 1]?.focus();
|
|
128
|
-
} else if (wrap) {
|
|
129
|
-
items[items.length - 1]?.focus();
|
|
130
|
-
}
|
|
131
|
-
break;
|
|
132
|
-
}
|
|
133
|
-
case 'Home':
|
|
134
|
-
focusFirst();
|
|
135
|
-
break;
|
|
136
|
-
case 'End':
|
|
137
|
-
focusLast();
|
|
138
|
-
break;
|
|
139
|
-
case 'Escape':
|
|
140
|
-
onEscape?.();
|
|
141
|
-
break;
|
|
142
|
-
default:
|
|
143
|
-
handled = false;
|
|
144
|
-
}
|
|
145
|
-
if (handled) {
|
|
146
|
-
e.preventDefault();
|
|
382
|
+
if (isNext) {
|
|
383
|
+
const from = currentIndex === -1 ? 0 : currentIndex + 1;
|
|
384
|
+
const next = findEnabledIndex(items, from, 1, wrap);
|
|
385
|
+
if (next !== -1) {
|
|
386
|
+
focusIndex(items, next);
|
|
387
|
+
}
|
|
388
|
+
} else if (isPrev) {
|
|
389
|
+
const from = currentIndex === -1 ? items.length - 1 : currentIndex - 1;
|
|
390
|
+
const prev = findEnabledIndex(items, from, -1, wrap);
|
|
391
|
+
if (prev !== -1) {
|
|
392
|
+
focusIndex(items, prev);
|
|
393
|
+
}
|
|
394
|
+
} else if (isHome) {
|
|
395
|
+
focusFirst();
|
|
396
|
+
} else if (isEnd) {
|
|
397
|
+
focusLast();
|
|
147
398
|
}
|
|
148
|
-
|
|
399
|
+
e.preventDefault();
|
|
400
|
+
}, [getCurrentIndex, getItems, wrap, orientation, isRtl, hasHomeEnd, hasCaretGuard, findEnabledIndex, focusIndex, focusFirst, focusLast, onEscape]);
|
|
149
401
|
return {
|
|
150
402
|
listRef,
|
|
151
403
|
handleKeyDown,
|
|
404
|
+
handleFocus,
|
|
152
405
|
focusItem,
|
|
153
406
|
focusFirst,
|
|
154
407
|
focusLast
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface UseLongPressOptions {
|
|
2
|
+
/** Fired with the touch start point once the press is held for `delayMs`. */
|
|
3
|
+
onLongPress: (point: {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
}) => void;
|
|
7
|
+
/** When true, touch handlers are inert. */
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
/** Hold duration before the press fires, in ms. Defaults to 500. */
|
|
10
|
+
delayMs?: number;
|
|
11
|
+
/** Movement past this distance (px, either axis) cancels the press. Defaults to 10. */
|
|
12
|
+
moveCancelPx?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface UseLongPressHandlers {
|
|
15
|
+
onTouchStart: (e: React.TouchEvent) => void;
|
|
16
|
+
onTouchMove: (e: React.TouchEvent) => void;
|
|
17
|
+
onTouchEnd: () => void;
|
|
18
|
+
onTouchCancel: () => void;
|
|
19
|
+
}
|
|
20
|
+
export declare function useLongPress(options: UseLongPressOptions): UseLongPressHandlers;
|
|
21
|
+
//# sourceMappingURL=useLongPress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useLongPress.d.ts","sourceRoot":"","sources":["../../src/hooks/useLongPress.ts"],"names":[],"mappings":"AA4BA,MAAM,WAAW,mBAAmB;IAClC,6EAA6E;IAC7E,WAAW,EAAE,CAAC,KAAK,EAAE;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAC,KAAK,IAAI,CAAC;IACrD,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uFAAuF;IACvF,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IAC5C,WAAW,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IAC3C,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,wBAAgB,YAAY,CAC1B,OAAO,EAAE,mBAAmB,GAC3B,oBAAoB,CAsEtB"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file useLongPress.ts
|
|
7
|
+
* @input Long-press options: onLongPress callback, disabled, delayMs, moveCancelPx
|
|
8
|
+
* @output Touch handlers to spread onto an element
|
|
9
|
+
* @position Core hook for touch long-press invocation; used by ContextMenu
|
|
10
|
+
*
|
|
11
|
+
* SYNC: When modified, update the hooks barrel /packages/core/src/hooks/index.ts
|
|
12
|
+
*
|
|
13
|
+
* Detects a single-finger long-press: fires `onLongPress` with the touch
|
|
14
|
+
* start point after `delayMs`. Cancels if the finger moves past
|
|
15
|
+
* `moveCancelPx` (treated as a scroll/drag), lifts, or the touch is
|
|
16
|
+
* cancelled. The pending timer is also cleared on unmount.
|
|
17
|
+
*
|
|
18
|
+
* Motivation: iOS Safari never synthesizes a `contextmenu` event on
|
|
19
|
+
* long-press, so long-press is the only touch affordance for opening
|
|
20
|
+
* cursor-positioned surfaces.
|
|
21
|
+
*/
|
|
22
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
23
|
+
|
|
24
|
+
// Default long-press tuning.
|
|
25
|
+
const DEFAULT_DELAY_MS = 500;
|
|
26
|
+
const DEFAULT_MOVE_CANCEL_PX = 10;
|
|
27
|
+
export function useLongPress(options) {
|
|
28
|
+
const {
|
|
29
|
+
onLongPress,
|
|
30
|
+
disabled = false,
|
|
31
|
+
delayMs = DEFAULT_DELAY_MS,
|
|
32
|
+
moveCancelPx = DEFAULT_MOVE_CANCEL_PX
|
|
33
|
+
} = options;
|
|
34
|
+
const timerRef = useRef(null);
|
|
35
|
+
const startRef = useRef(null);
|
|
36
|
+
// Keep the latest callback without re-creating handlers on every render.
|
|
37
|
+
const onLongPressRef = useRef(onLongPress);
|
|
38
|
+
onLongPressRef.current = onLongPress;
|
|
39
|
+
const clear = useCallback(() => {
|
|
40
|
+
if (timerRef.current != null) {
|
|
41
|
+
clearTimeout(timerRef.current);
|
|
42
|
+
timerRef.current = null;
|
|
43
|
+
}
|
|
44
|
+
startRef.current = null;
|
|
45
|
+
}, []);
|
|
46
|
+
const onTouchStart = useCallback(e => {
|
|
47
|
+
if (disabled || e.touches.length !== 1) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const touch = e.touches[0];
|
|
51
|
+
// Clear any stale timer first, THEN record the start point — clearing
|
|
52
|
+
// also nulls startRef, so order matters.
|
|
53
|
+
clear();
|
|
54
|
+
startRef.current = {
|
|
55
|
+
x: touch.clientX,
|
|
56
|
+
y: touch.clientY
|
|
57
|
+
};
|
|
58
|
+
timerRef.current = setTimeout(() => {
|
|
59
|
+
const start = startRef.current;
|
|
60
|
+
if (start == null) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
onLongPressRef.current({
|
|
64
|
+
x: start.x,
|
|
65
|
+
y: start.y
|
|
66
|
+
});
|
|
67
|
+
}, delayMs);
|
|
68
|
+
}, [disabled, delayMs, clear]);
|
|
69
|
+
const onTouchMove = useCallback(e => {
|
|
70
|
+
const start = startRef.current;
|
|
71
|
+
if (start == null || e.touches.length !== 1) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const touch = e.touches[0];
|
|
75
|
+
if (Math.abs(touch.clientX - start.x) > moveCancelPx || Math.abs(touch.clientY - start.y) > moveCancelPx) {
|
|
76
|
+
// Treat as a scroll/drag, not a long-press.
|
|
77
|
+
clear();
|
|
78
|
+
}
|
|
79
|
+
}, [moveCancelPx, clear]);
|
|
80
|
+
|
|
81
|
+
// Cancel any pending long-press timer on unmount.
|
|
82
|
+
useEffect(() => clear, [clear]);
|
|
83
|
+
return {
|
|
84
|
+
onTouchStart,
|
|
85
|
+
onTouchMove,
|
|
86
|
+
onTouchEnd: clear,
|
|
87
|
+
onTouchCancel: clear
|
|
88
|
+
};
|
|
89
|
+
}
|