@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
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file useKeyboardHint.test.tsx
|
|
5
|
+
* @input Uses React Testing Library, useKeyboardHint
|
|
6
|
+
* @output Unit tests for keyboard hint rendering
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {render} from '@testing-library/react';
|
|
10
|
+
import {describe, expect, it} from 'vitest';
|
|
11
|
+
import {useKeyboardHint, type KeyboardHintOrientation} from './useKeyboardHint';
|
|
12
|
+
|
|
13
|
+
function TestHint({orientation}: {orientation?: KeyboardHintOrientation}) {
|
|
14
|
+
const {hintElement} = useKeyboardHint({orientation});
|
|
15
|
+
return <div>{hintElement}</div>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getKeyLabels(container: HTMLElement): (string | null)[] {
|
|
19
|
+
return Array.from(container.querySelectorAll('.astryx-kbd')).map(key =>
|
|
20
|
+
key.getAttribute('aria-label'),
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getKeyText(container: HTMLElement): string[] {
|
|
25
|
+
return Array.from(container.querySelectorAll('kbd')).map(
|
|
26
|
+
key => key.textContent ?? '',
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe('useKeyboardHint', () => {
|
|
31
|
+
it('renders horizontal arrow keys with Kbd', () => {
|
|
32
|
+
const {container} = render(<TestHint orientation="horizontal" />);
|
|
33
|
+
|
|
34
|
+
expect(getKeyLabels(container)).toEqual(['Left arrow', 'Right arrow']);
|
|
35
|
+
expect(getKeyText(container)).toEqual(['←', '→']);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('renders vertical arrow keys with Kbd', () => {
|
|
39
|
+
const {container} = render(<TestHint orientation="vertical" />);
|
|
40
|
+
|
|
41
|
+
expect(getKeyLabels(container)).toEqual(['Up arrow', 'Down arrow']);
|
|
42
|
+
expect(getKeyText(container)).toEqual(['↑', '↓']);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('renders all arrow keys for both-axis navigation', () => {
|
|
46
|
+
const {container} = render(<TestHint orientation="both" />);
|
|
47
|
+
|
|
48
|
+
expect(getKeyLabels(container)).toEqual([
|
|
49
|
+
'Left arrow',
|
|
50
|
+
'Right arrow',
|
|
51
|
+
'Up arrow',
|
|
52
|
+
'Down arrow',
|
|
53
|
+
]);
|
|
54
|
+
expect(getKeyText(container)).toEqual(['←', '→', '↑', '↓']);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('positions the hint farther from the anchor', () => {
|
|
58
|
+
const {container} = render(<TestHint />);
|
|
59
|
+
const hint = container.querySelector('[popover="manual"]') as HTMLElement;
|
|
60
|
+
|
|
61
|
+
expect(hint.style.marginBlockStart).toBe('var(--spacing-2)');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('keeps the hint surface padding after useLayer reset styles', () => {
|
|
65
|
+
const {container} = render(<TestHint />);
|
|
66
|
+
const hint = container.querySelector('[popover="manual"]') as HTMLElement;
|
|
67
|
+
|
|
68
|
+
expect(hint.className).toContain('useKeyboardHint__styles.hint');
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file useKeyboardHint.tsx
|
|
7
|
+
* @input Uses React, StyleX, Kbd, useLayer
|
|
8
|
+
* @output Exports useKeyboardHint hook — ephemeral arrow-key navigation hint
|
|
9
|
+
* @position Core hook; shows sighted keyboard users how to navigate composite
|
|
10
|
+
* widgets that use roving tabindex (single Tab stop, arrows inside)
|
|
11
|
+
*
|
|
12
|
+
* SYNC: When modified, update:
|
|
13
|
+
* - /packages/core/src/hooks/index.ts
|
|
14
|
+
* - /packages/core/src/hooks/useKeyboardHint.doc.mjs
|
|
15
|
+
* - /apps/storybook/stories/useKeyboardHint.stories.tsx
|
|
16
|
+
* - /packages/cli/templates/blocks/components/Hooks/useKeyboardHintHookUsage.tsx
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import React, {useCallback, useEffect, useRef, type ReactNode} from 'react';
|
|
20
|
+
import * as stylex from '@stylexjs/stylex';
|
|
21
|
+
import {
|
|
22
|
+
colorVars,
|
|
23
|
+
radiusVars,
|
|
24
|
+
shadowVars,
|
|
25
|
+
spacingVars,
|
|
26
|
+
typeScaleVars,
|
|
27
|
+
} from '../theme/tokens.stylex';
|
|
28
|
+
import {Kbd} from '../Kbd';
|
|
29
|
+
import {useLayer} from '../Layer/useLayer';
|
|
30
|
+
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Types
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
export type KeyboardHintOrientation = 'horizontal' | 'vertical' | 'both';
|
|
36
|
+
|
|
37
|
+
export interface UseKeyboardHintOptions {
|
|
38
|
+
/**
|
|
39
|
+
* Orientation of the arrow-key navigation. Controls which arrow icons are
|
|
40
|
+
* shown in the hint badge.
|
|
41
|
+
* - `'horizontal'` → ← →
|
|
42
|
+
* - `'vertical'` → ↑ ↓
|
|
43
|
+
* - `'both'` → ← → ↑ ↓
|
|
44
|
+
* @default 'horizontal'
|
|
45
|
+
*/
|
|
46
|
+
orientation?: KeyboardHintOrientation;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Milliseconds before the hint auto-dismisses after appearing.
|
|
50
|
+
* @default 3000
|
|
51
|
+
*/
|
|
52
|
+
dismissAfterMs?: number;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Whether the hint is enabled. Set to false to disable for a specific
|
|
56
|
+
* instance (e.g. when the widget is read-only or the user has dismissed
|
|
57
|
+
* globally).
|
|
58
|
+
* @default true
|
|
59
|
+
*/
|
|
60
|
+
isEnabled?: boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface UseKeyboardHintReturn {
|
|
64
|
+
/**
|
|
65
|
+
* The popover hint element to render inside your component tree (portals to
|
|
66
|
+
* top layer via `popover="manual"`). Render unconditionally — it manages its
|
|
67
|
+
* own visibility.
|
|
68
|
+
*/
|
|
69
|
+
hintElement: ReactNode;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Attach to the composite container's `onFocus`. Shows the hint on the first
|
|
73
|
+
* keyboard-focus (`:focus-visible`) entry from outside.
|
|
74
|
+
*/
|
|
75
|
+
onFocus: (e: React.FocusEvent) => void;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Attach to the composite container's `onBlur`. Hides the hint when focus
|
|
79
|
+
* leaves the composite entirely.
|
|
80
|
+
*/
|
|
81
|
+
onBlur: (e: React.FocusEvent) => void;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Attach to the composite container's `onKeyDown`. Dismisses the hint on the
|
|
85
|
+
* first arrow press (the user discovered the interaction).
|
|
86
|
+
*/
|
|
87
|
+
onKeyDown: (e: React.KeyboardEvent) => void;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// Styles
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
|
|
94
|
+
const ARROW_KEYS = new Set(['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']);
|
|
95
|
+
|
|
96
|
+
const ARROW_HINT_KEYS: Record<
|
|
97
|
+
KeyboardHintOrientation,
|
|
98
|
+
ReadonlyArray<string>
|
|
99
|
+
> = {
|
|
100
|
+
horizontal: ['left', 'right'],
|
|
101
|
+
vertical: ['up', 'down'],
|
|
102
|
+
both: ['left', 'right', 'up', 'down'],
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const styles = stylex.create({
|
|
106
|
+
hint: {
|
|
107
|
+
// Top layer + anchor positioned
|
|
108
|
+
position: 'fixed',
|
|
109
|
+
inset: 'auto',
|
|
110
|
+
margin: 0,
|
|
111
|
+
border: 'none',
|
|
112
|
+
|
|
113
|
+
// Surface
|
|
114
|
+
backgroundColor: colorVars['--color-background-popover'],
|
|
115
|
+
borderRadius: radiusVars['--radius-element'],
|
|
116
|
+
boxShadow: shadowVars['--shadow-low'],
|
|
117
|
+
paddingBlockStart: spacingVars['--spacing-1'],
|
|
118
|
+
paddingBlockEnd: spacingVars['--spacing-1'],
|
|
119
|
+
paddingInlineStart: spacingVars['--spacing-2'],
|
|
120
|
+
paddingInlineEnd: spacingVars['--spacing-2'],
|
|
121
|
+
|
|
122
|
+
// Typography
|
|
123
|
+
fontSize: typeScaleVars['--text-supporting-size'],
|
|
124
|
+
lineHeight: typeScaleVars['--text-supporting-leading'],
|
|
125
|
+
color: colorVars['--color-text-secondary'],
|
|
126
|
+
whiteSpace: 'nowrap',
|
|
127
|
+
|
|
128
|
+
// Animation
|
|
129
|
+
opacity: {
|
|
130
|
+
default: 0,
|
|
131
|
+
':popover-open': 1,
|
|
132
|
+
},
|
|
133
|
+
transitionProperty: 'opacity, display, overlay',
|
|
134
|
+
transitionDuration: '150ms',
|
|
135
|
+
transitionBehavior: 'allow-discrete',
|
|
136
|
+
|
|
137
|
+
// Don't capture pointer events (hint floats above content)
|
|
138
|
+
pointerEvents: 'none',
|
|
139
|
+
},
|
|
140
|
+
keys: {
|
|
141
|
+
display: 'inline-flex',
|
|
142
|
+
alignItems: 'center',
|
|
143
|
+
gap: spacingVars['--spacing-1'],
|
|
144
|
+
},
|
|
145
|
+
label: {
|
|
146
|
+
marginInlineStart: spacingVars['--spacing-1'],
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
// Hook
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Shows an ephemeral visual hint ("← → to navigate") anchored to the focused
|
|
156
|
+
* item when a composite widget first receives keyboard focus. Teaches sighted
|
|
157
|
+
* keyboard users that arrows navigate within the group.
|
|
158
|
+
*
|
|
159
|
+
* The hint renders in the top layer (popover="manual") and is CSS-anchor-
|
|
160
|
+
* positioned to the currently focused element, so it is never clipped by
|
|
161
|
+
* overflow containers. It auto-dismisses on first arrow press, timeout, or
|
|
162
|
+
* blur, and does not re-show for that instance.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```
|
|
166
|
+
* const hint = useKeyboardHint({orientation: 'horizontal'});
|
|
167
|
+
* <div role="toolbar" onFocus={hint.onFocus} onBlur={hint.onBlur} onKeyDown={hint.onKeyDown}>
|
|
168
|
+
* {children}
|
|
169
|
+
* {hint.hintElement}
|
|
170
|
+
* </div>
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
export function useKeyboardHint(
|
|
174
|
+
options: UseKeyboardHintOptions = {},
|
|
175
|
+
): UseKeyboardHintReturn {
|
|
176
|
+
const {
|
|
177
|
+
orientation = 'horizontal',
|
|
178
|
+
dismissAfterMs = 3000,
|
|
179
|
+
isEnabled = true,
|
|
180
|
+
} = options;
|
|
181
|
+
|
|
182
|
+
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
183
|
+
const dismissedRef = useRef(false);
|
|
184
|
+
const isVisibleRef = useRef(false);
|
|
185
|
+
const layerAnchorRef = useRef<(el: HTMLElement | null) => void>(() => {});
|
|
186
|
+
|
|
187
|
+
const clearDismissTimeout = useCallback(() => {
|
|
188
|
+
if (timeoutRef.current) {
|
|
189
|
+
clearTimeout(timeoutRef.current);
|
|
190
|
+
timeoutRef.current = null;
|
|
191
|
+
}
|
|
192
|
+
}, []);
|
|
193
|
+
|
|
194
|
+
const handleLayerShow = useCallback(() => {
|
|
195
|
+
isVisibleRef.current = true;
|
|
196
|
+
}, []);
|
|
197
|
+
|
|
198
|
+
const handleLayerHide = useCallback(() => {
|
|
199
|
+
isVisibleRef.current = false;
|
|
200
|
+
clearDismissTimeout();
|
|
201
|
+
layerAnchorRef.current(null);
|
|
202
|
+
}, [clearDismissTimeout]);
|
|
203
|
+
|
|
204
|
+
const layer = useLayer({
|
|
205
|
+
mode: 'context',
|
|
206
|
+
onShow: handleLayerShow,
|
|
207
|
+
onHide: handleLayerHide,
|
|
208
|
+
});
|
|
209
|
+
layerAnchorRef.current = layer.ref;
|
|
210
|
+
|
|
211
|
+
// Hide + mark dismissed (won't re-show for this instance)
|
|
212
|
+
const dismiss = useCallback(() => {
|
|
213
|
+
dismissedRef.current = true;
|
|
214
|
+
clearDismissTimeout();
|
|
215
|
+
layer.hide();
|
|
216
|
+
isVisibleRef.current = false;
|
|
217
|
+
layerAnchorRef.current(null);
|
|
218
|
+
}, [clearDismissTimeout, layer]);
|
|
219
|
+
|
|
220
|
+
// Show the layer anchored to the focused element
|
|
221
|
+
const show = useCallback(
|
|
222
|
+
(anchor: HTMLElement) => {
|
|
223
|
+
if (dismissedRef.current || !isEnabled) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
layerAnchorRef.current(anchor);
|
|
228
|
+
layer.show();
|
|
229
|
+
|
|
230
|
+
clearDismissTimeout();
|
|
231
|
+
timeoutRef.current = setTimeout(() => {
|
|
232
|
+
dismiss();
|
|
233
|
+
}, dismissAfterMs);
|
|
234
|
+
},
|
|
235
|
+
[clearDismissTimeout, dismiss, dismissAfterMs, isEnabled, layer],
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
// Cleanup on unmount
|
|
239
|
+
useEffect(
|
|
240
|
+
() => () => {
|
|
241
|
+
clearDismissTimeout();
|
|
242
|
+
layerAnchorRef.current(null);
|
|
243
|
+
},
|
|
244
|
+
[clearDismissTimeout],
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
// --- Handlers ---
|
|
248
|
+
|
|
249
|
+
const onFocus = useCallback(
|
|
250
|
+
(e: React.FocusEvent) => {
|
|
251
|
+
if (dismissedRef.current || !isEnabled) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
// Only show on keyboard focus (focus-visible)
|
|
255
|
+
const target = e.target as HTMLElement;
|
|
256
|
+
if (!target.matches(':focus-visible')) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
// Only show when focus enters from outside the container
|
|
260
|
+
const container = e.currentTarget as HTMLElement;
|
|
261
|
+
if (
|
|
262
|
+
e.relatedTarget instanceof Node &&
|
|
263
|
+
container.contains(e.relatedTarget)
|
|
264
|
+
) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
show(target);
|
|
268
|
+
},
|
|
269
|
+
[show, isEnabled],
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
const onBlur = useCallback(
|
|
273
|
+
(e: React.FocusEvent) => {
|
|
274
|
+
if (!isVisibleRef.current) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
const container = e.currentTarget as HTMLElement;
|
|
278
|
+
// Only dismiss when focus leaves the container entirely
|
|
279
|
+
if (
|
|
280
|
+
e.relatedTarget instanceof Node &&
|
|
281
|
+
container.contains(e.relatedTarget)
|
|
282
|
+
) {
|
|
283
|
+
// Focus moved within — re-anchor to the new target
|
|
284
|
+
if (!dismissedRef.current && e.relatedTarget instanceof HTMLElement) {
|
|
285
|
+
layerAnchorRef.current(e.relatedTarget);
|
|
286
|
+
}
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
dismiss();
|
|
290
|
+
},
|
|
291
|
+
[dismiss],
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
const onKeyDown = useCallback(
|
|
295
|
+
(e: React.KeyboardEvent) => {
|
|
296
|
+
if (!isVisibleRef.current) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
if (ARROW_KEYS.has(e.key)) {
|
|
300
|
+
dismiss();
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
[dismiss],
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
// --- Render the hint element ---
|
|
307
|
+
|
|
308
|
+
const arrowContent = (
|
|
309
|
+
<span {...stylex.props(styles.keys)}>
|
|
310
|
+
{ARROW_HINT_KEYS[orientation].map(key => (
|
|
311
|
+
<Kbd key={key} keys={key} />
|
|
312
|
+
))}
|
|
313
|
+
</span>
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
const hintElement = layer.render(
|
|
317
|
+
<span aria-hidden="true">
|
|
318
|
+
{arrowContent}
|
|
319
|
+
<span {...stylex.props(styles.label)}>to navigate</span>
|
|
320
|
+
</span>,
|
|
321
|
+
{
|
|
322
|
+
placement: 'below',
|
|
323
|
+
alignment: 'start',
|
|
324
|
+
xstyle: styles.hint,
|
|
325
|
+
style: {
|
|
326
|
+
marginBlockStart: spacingVars['--spacing-2'],
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
return {hintElement, onFocus, onBlur, onKeyDown};
|
|
332
|
+
}
|
|
@@ -34,11 +34,39 @@ export const docs = {
|
|
|
34
34
|
},
|
|
35
35
|
{
|
|
36
36
|
name: 'options.orientation',
|
|
37
|
-
type: "'horizontal' | 'vertical'",
|
|
38
|
-
description: "
|
|
37
|
+
type: "'horizontal' | 'vertical' | 'both'",
|
|
38
|
+
description: "Navigation orientation. 'horizontal' uses ArrowLeft/ArrowRight, 'vertical' uses ArrowUp/ArrowDown, 'both' accepts all four arrows.",
|
|
39
39
|
default: "'vertical'",
|
|
40
40
|
required: false,
|
|
41
41
|
},
|
|
42
|
+
{
|
|
43
|
+
name: 'options.hasHomeEnd',
|
|
44
|
+
type: 'boolean',
|
|
45
|
+
description: 'Whether Home/End jump to the first/last enabled item.',
|
|
46
|
+
default: 'true',
|
|
47
|
+
required: false,
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'options.isRtl',
|
|
51
|
+
type: 'boolean',
|
|
52
|
+
description: 'Whether the list is in a right-to-left context. When true, ArrowLeft/ArrowRight are swapped for horizontal navigation so it follows visual direction.',
|
|
53
|
+
default: 'false',
|
|
54
|
+
required: false,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: 'options.hasRovingTabIndex',
|
|
58
|
+
type: 'boolean',
|
|
59
|
+
description: 'Opt into roving-tabindex ownership: the hook stamps a single tab stop (one item tabindex="0", the rest -1), repairs it as items mount/unmount or toggle disabled, and moves it with arrow navigation. When false, the hook only moves focus and never touches tabindex.',
|
|
60
|
+
default: 'false',
|
|
61
|
+
required: false,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'options.hasCaretGuard',
|
|
65
|
+
type: 'boolean',
|
|
66
|
+
description: "When true, arrow keys are not stolen from a nested text input/textarea whose caret is not at the boundary in the direction of travel (or that has a selection), and are never stolen from a nested contenteditable (rich-text editor / chat composer). Preserves inline text editing within the list.",
|
|
67
|
+
default: 'false',
|
|
68
|
+
required: false,
|
|
69
|
+
},
|
|
42
70
|
],
|
|
43
71
|
returns: [
|
|
44
72
|
{
|
|
@@ -51,6 +79,11 @@ export const docs = {
|
|
|
51
79
|
type: '(e: React.KeyboardEvent) => void',
|
|
52
80
|
description: 'Key down handler to attach to the list container.',
|
|
53
81
|
},
|
|
82
|
+
{
|
|
83
|
+
name: 'handleFocus',
|
|
84
|
+
type: '(e: React.FocusEvent) => void',
|
|
85
|
+
description: 'Focus handler for the container. Keeps the roving tab stop in sync when hasRovingTabIndex is enabled; a no-op otherwise, so it is always safe to attach.',
|
|
86
|
+
},
|
|
54
87
|
{
|
|
55
88
|
name: 'focusItem',
|
|
56
89
|
type: '(index: number) => void',
|
|
@@ -58,21 +91,22 @@ export const docs = {
|
|
|
58
91
|
},
|
|
59
92
|
{
|
|
60
93
|
name: 'focusFirst',
|
|
61
|
-
type: '() =>
|
|
62
|
-
description: 'Focus the first
|
|
94
|
+
type: '() => boolean',
|
|
95
|
+
description: 'Focus the first enabled item. Returns true when an item was focused.',
|
|
63
96
|
},
|
|
64
97
|
{
|
|
65
98
|
name: 'focusLast',
|
|
66
|
-
type: '() =>
|
|
67
|
-
description: 'Focus the last
|
|
99
|
+
type: '() => boolean',
|
|
100
|
+
description: 'Focus the last enabled item. Returns true when an item was focused.',
|
|
68
101
|
},
|
|
69
102
|
],
|
|
70
103
|
usage: {
|
|
71
104
|
description:
|
|
72
|
-
'Manages keyboard navigation within a linear list following WAI-ARIA menu/listbox/toolbar patterns. Supports arrow key navigation (vertical or
|
|
105
|
+
'Manages keyboard navigation within a linear list following WAI-ARIA menu/listbox/toolbar patterns. Supports arrow key navigation (vertical, horizontal, or both), Home/End for boundaries, optional wrap-around, RTL, and Escape to close. Opt into hasRovingTabIndex for composite widgets (toolbars, segmented controls, tab strips) that own a single tab stop. Suitable for dropdown menus, toolbars, and any 1D focusable list.',
|
|
73
106
|
bestPractices: [
|
|
74
107
|
{ guidance: true, description: "Set orientation to 'horizontal' for toolbars and tab bars, 'vertical' for dropdown menus." },
|
|
75
108
|
{ guidance: true, description: 'Provide an onEscape callback for menus/dropdowns to return focus to the trigger.' },
|
|
109
|
+
{ guidance: true, description: 'Enable hasRovingTabIndex (and hasCaretGuard when the widget can contain text inputs) for toolbar-style composites that should be a single tab stop.' },
|
|
76
110
|
{ guidance: false, description: 'Use for 2D grid navigation; prefer useGridFocus for grids and calendars.' },
|
|
77
111
|
],
|
|
78
112
|
},
|
|
@@ -91,14 +125,19 @@ export const docsDense = {
|
|
|
91
125
|
'options.itemSelector': 'selector for focusable items in list.',
|
|
92
126
|
'options.wrap': 'whether arrow navigation wraps around at ends.',
|
|
93
127
|
'options.onEscape': 'callback when Escape key pressed (e.g. close menu).',
|
|
94
|
-
'options.orientation': "navigation orientation. 'horizontal' uses ArrowLeft/ArrowRight, 'vertical' uses ArrowUp/ArrowDown.",
|
|
128
|
+
'options.orientation': "navigation orientation. 'horizontal' uses ArrowLeft/ArrowRight, 'vertical' uses ArrowUp/ArrowDown, 'both' accepts all four arrows.",
|
|
129
|
+
'options.hasHomeEnd': 'whether Home/End jump to first/last enabled item.',
|
|
130
|
+
'options.isRtl': 'when true, ArrowLeft/ArrowRight swap for horizontal nav (RTL).',
|
|
131
|
+
'options.hasRovingTabIndex': 'opt into roving-tabindex ownership: hook stamps + repairs a single tab stop across items.',
|
|
132
|
+
'options.hasCaretGuard': "when true, don't steal arrow keys from a nested text input/textarea mid-line or from a contenteditable.",
|
|
95
133
|
},
|
|
96
134
|
returnDescriptions: {
|
|
97
135
|
listRef: 'ref to attach to list container element.',
|
|
98
136
|
handleKeyDown: 'key down handler for list container.',
|
|
137
|
+
handleFocus: 'focus handler; keeps roving tab stop in sync when hasRovingTabIndex is on (else no-op).',
|
|
99
138
|
focusItem: 'focus specific item by index (clamped to valid range).',
|
|
100
|
-
focusFirst: 'focus first
|
|
101
|
-
focusLast: 'focus last
|
|
139
|
+
focusFirst: 'focus first enabled item; returns true when focused.',
|
|
140
|
+
focusLast: 'focus last enabled item; returns true when focused.',
|
|
102
141
|
},
|
|
103
142
|
usage: {
|
|
104
143
|
description:
|
|
@@ -106,6 +145,7 @@ export const docsDense = {
|
|
|
106
145
|
bestPractices: [
|
|
107
146
|
{ guidance: true, description: "Set orientation to 'horizontal' for toolbars + tab bars, 'vertical' for dropdown menus." },
|
|
108
147
|
{ guidance: true, description: 'Provide onEscape callback for menus/dropdowns to return focus to trigger.' },
|
|
148
|
+
{ guidance: true, description: 'Enable hasRovingTabIndex (+ hasCaretGuard when widget can contain text inputs) for toolbar-style composites that should be a single tab stop.' },
|
|
109
149
|
{ guidance: false, description: 'Use for 2D grid navigation; prefer useGridFocus for grids + calendars.' },
|
|
110
150
|
],
|
|
111
151
|
},
|