@allxsmith/bestax-bulma 5.55.0 → 5.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bestax.css +1 -1
- package/dist/bestax.css.map +1 -1
- package/dist/esm/columns/Column.js +1 -0
- package/dist/esm/columns/Column.js.map +1 -1
- package/dist/esm/columns/Columns.js +1 -0
- package/dist/esm/columns/Columns.js.map +1 -1
- package/dist/esm/components/Avatar.js +1 -0
- package/dist/esm/components/Avatar.js.map +1 -1
- package/dist/esm/components/Avatars.js +1 -0
- package/dist/esm/components/Avatars.js.map +1 -1
- package/dist/esm/components/Badge.js +1 -0
- package/dist/esm/components/Badge.js.map +1 -1
- package/dist/esm/components/Breadcrumb.js +1 -0
- package/dist/esm/components/Breadcrumb.js.map +1 -1
- package/dist/esm/components/Card.js +1 -0
- package/dist/esm/components/Card.js.map +1 -1
- package/dist/esm/components/Carousel.js +1 -0
- package/dist/esm/components/Carousel.js.map +1 -1
- package/dist/esm/components/Collapse.js +1 -0
- package/dist/esm/components/Collapse.js.map +1 -1
- package/dist/esm/components/Collapses.js +1 -0
- package/dist/esm/components/Collapses.js.map +1 -1
- package/dist/esm/components/Command.js +1 -0
- package/dist/esm/components/Command.js.map +1 -1
- package/dist/esm/components/ContextMenu.js +1 -0
- package/dist/esm/components/ContextMenu.js.map +1 -1
- package/dist/esm/components/DataTable.js +1 -0
- package/dist/esm/components/DataTable.js.map +1 -1
- package/dist/esm/components/Dialog.js +1 -0
- package/dist/esm/components/Dialog.js.map +1 -1
- package/dist/esm/components/Dropdown.js +1 -0
- package/dist/esm/components/Dropdown.js.map +1 -1
- package/dist/esm/components/EmptyState.js +1 -0
- package/dist/esm/components/EmptyState.js.map +1 -1
- package/dist/esm/components/HoverCard.js +1 -0
- package/dist/esm/components/HoverCard.js.map +1 -1
- package/dist/esm/components/Loading.js +1 -0
- package/dist/esm/components/Loading.js.map +1 -1
- package/dist/esm/components/Menu.js +1 -0
- package/dist/esm/components/Menu.js.map +1 -1
- package/dist/esm/components/Message.js +1 -0
- package/dist/esm/components/Message.js.map +1 -1
- package/dist/esm/components/Modal.js +1 -0
- package/dist/esm/components/Modal.js.map +1 -1
- package/dist/esm/components/Navbar.js +1 -0
- package/dist/esm/components/Navbar.js.map +1 -1
- package/dist/esm/components/Pagination.js +1 -0
- package/dist/esm/components/Pagination.js.map +1 -1
- package/dist/esm/components/Panel.js +1 -0
- package/dist/esm/components/Panel.js.map +1 -1
- package/dist/esm/components/Popover.js +1 -0
- package/dist/esm/components/Popover.js.map +1 -1
- package/dist/esm/components/Reveal.js +1 -0
- package/dist/esm/components/Reveal.js.map +1 -1
- package/dist/esm/components/ScrollArea.js +1 -0
- package/dist/esm/components/ScrollArea.js.map +1 -1
- package/dist/esm/components/SegmentedControl.js +1 -0
- package/dist/esm/components/SegmentedControl.js.map +1 -1
- package/dist/esm/components/Sidebar.js +1 -0
- package/dist/esm/components/Sidebar.js.map +1 -1
- package/dist/esm/components/Steps.js +1 -0
- package/dist/esm/components/Steps.js.map +1 -1
- package/dist/esm/components/Tabs.js +1 -0
- package/dist/esm/components/Tabs.js.map +1 -1
- package/dist/esm/components/Timeline.js +1 -0
- package/dist/esm/components/Timeline.js.map +1 -1
- package/dist/esm/components/Toast.js +1 -0
- package/dist/esm/components/Toast.js.map +1 -1
- package/dist/esm/components/Tooltip.js +1 -0
- package/dist/esm/components/Tooltip.js.map +1 -1
- package/dist/esm/components/TreeView.js +1 -0
- package/dist/esm/components/TreeView.js.map +1 -1
- package/dist/esm/components/collapsesContext.js +1 -0
- package/dist/esm/components/collapsesContext.js.map +1 -1
- package/dist/esm/components/useDataTable.js +28 -0
- package/dist/esm/components/useDataTable.js.map +1 -1
- package/dist/esm/elements/Block.js +1 -0
- package/dist/esm/elements/Block.js.map +1 -1
- package/dist/esm/elements/Box.js +1 -0
- package/dist/esm/elements/Box.js.map +1 -1
- package/dist/esm/elements/Button.js +1 -0
- package/dist/esm/elements/Button.js.map +1 -1
- package/dist/esm/elements/Buttons.js +1 -0
- package/dist/esm/elements/Buttons.js.map +1 -1
- package/dist/esm/elements/Code.js +1 -0
- package/dist/esm/elements/Code.js.map +1 -1
- package/dist/esm/elements/ColorSwatch.js +1 -0
- package/dist/esm/elements/ColorSwatch.js.map +1 -1
- package/dist/esm/elements/Content.js +1 -0
- package/dist/esm/elements/Content.js.map +1 -1
- package/dist/esm/elements/Delete.js +1 -0
- package/dist/esm/elements/Delete.js.map +1 -1
- package/dist/esm/elements/DescriptionList.js +1 -0
- package/dist/esm/elements/DescriptionList.js.map +1 -1
- package/dist/esm/elements/Divider.js +1 -0
- package/dist/esm/elements/Divider.js.map +1 -1
- package/dist/esm/elements/Emphasis.js +1 -0
- package/dist/esm/elements/Emphasis.js.map +1 -1
- package/dist/esm/elements/Figure.js +1 -0
- package/dist/esm/elements/Figure.js.map +1 -1
- package/dist/esm/elements/Icon.js +1 -0
- package/dist/esm/elements/Icon.js.map +1 -1
- package/dist/esm/elements/IconText.js +1 -0
- package/dist/esm/elements/IconText.js.map +1 -1
- package/dist/esm/elements/Image.js +1 -0
- package/dist/esm/elements/Image.js.map +1 -1
- package/dist/esm/elements/Kbd.js +1 -0
- package/dist/esm/elements/Kbd.js.map +1 -1
- package/dist/esm/elements/Link.js +1 -0
- package/dist/esm/elements/Link.js.map +1 -1
- package/dist/esm/elements/LinkButton.js +1 -0
- package/dist/esm/elements/LinkButton.js.map +1 -1
- package/dist/esm/elements/ListItem.js +1 -0
- package/dist/esm/elements/ListItem.js.map +1 -1
- package/dist/esm/elements/Loader.js +1 -0
- package/dist/esm/elements/Loader.js.map +1 -1
- package/dist/esm/elements/Notification.js +1 -0
- package/dist/esm/elements/Notification.js.map +1 -1
- package/dist/esm/elements/OrderedList.js +1 -0
- package/dist/esm/elements/OrderedList.js.map +1 -1
- package/dist/esm/elements/Paragraph.js +1 -0
- package/dist/esm/elements/Paragraph.js.map +1 -1
- package/dist/esm/elements/Pre.js +1 -0
- package/dist/esm/elements/Pre.js.map +1 -1
- package/dist/esm/elements/Progress.js +1 -0
- package/dist/esm/elements/Progress.js.map +1 -1
- package/dist/esm/elements/RingProgress.js +1 -0
- package/dist/esm/elements/RingProgress.js.map +1 -1
- package/dist/esm/elements/Skeleton.js +1 -0
- package/dist/esm/elements/Skeleton.js.map +1 -1
- package/dist/esm/elements/Span.js +1 -0
- package/dist/esm/elements/Span.js.map +1 -1
- package/dist/esm/elements/Strong.js +1 -0
- package/dist/esm/elements/Strong.js.map +1 -1
- package/dist/esm/elements/SubTitle.js +1 -0
- package/dist/esm/elements/SubTitle.js.map +1 -1
- package/dist/esm/elements/Table.js +1 -0
- package/dist/esm/elements/Table.js.map +1 -1
- package/dist/esm/elements/Tag.js +1 -0
- package/dist/esm/elements/Tag.js.map +1 -1
- package/dist/esm/elements/Tags.js +1 -0
- package/dist/esm/elements/Tags.js.map +1 -1
- package/dist/esm/elements/Tbody.js +1 -0
- package/dist/esm/elements/Tbody.js.map +1 -1
- package/dist/esm/elements/Td.js +1 -0
- package/dist/esm/elements/Td.js.map +1 -1
- package/dist/esm/elements/Tfoot.js +1 -0
- package/dist/esm/elements/Tfoot.js.map +1 -1
- package/dist/esm/elements/Th.js +1 -0
- package/dist/esm/elements/Th.js.map +1 -1
- package/dist/esm/elements/Thead.js +1 -0
- package/dist/esm/elements/Thead.js.map +1 -1
- package/dist/esm/elements/Title.js +1 -0
- package/dist/esm/elements/Title.js.map +1 -1
- package/dist/esm/elements/ToggleButton.js +1 -0
- package/dist/esm/elements/ToggleButton.js.map +1 -1
- package/dist/esm/elements/Tr.js +1 -0
- package/dist/esm/elements/Tr.js.map +1 -1
- package/dist/esm/elements/UnorderedList.js +1 -0
- package/dist/esm/elements/UnorderedList.js.map +1 -1
- package/dist/esm/elements/iconProps.js +1 -0
- package/dist/esm/elements/iconProps.js.map +1 -1
- package/dist/esm/form/Autocomplete.js +1 -0
- package/dist/esm/form/Autocomplete.js.map +1 -1
- package/dist/esm/form/Checkbox.js +1 -0
- package/dist/esm/form/Checkbox.js.map +1 -1
- package/dist/esm/form/CheckboxCard.js +1 -0
- package/dist/esm/form/CheckboxCard.js.map +1 -1
- package/dist/esm/form/Checkboxes.js +1 -0
- package/dist/esm/form/Checkboxes.js.map +1 -1
- package/dist/esm/form/ColorInput.js +69 -0
- package/dist/esm/form/ColorInput.js.map +1 -0
- package/dist/esm/form/ColorInputBase.js +199 -0
- package/dist/esm/form/ColorInputBase.js.map +1 -0
- package/dist/esm/form/ColorPicker.js +5 -20
- package/dist/esm/form/ColorPicker.js.map +1 -1
- package/dist/esm/form/Combobox.js +1 -0
- package/dist/esm/form/Combobox.js.map +1 -1
- package/dist/esm/form/Control.js +1 -0
- package/dist/esm/form/Control.js.map +1 -1
- package/dist/esm/form/DateInput.js +1 -0
- package/dist/esm/form/DateInput.js.map +1 -1
- package/dist/esm/form/DateInputBase.js +1 -0
- package/dist/esm/form/DateInputBase.js.map +1 -1
- package/dist/esm/form/DateRangeInput.js +1 -0
- package/dist/esm/form/DateRangeInput.js.map +1 -1
- package/dist/esm/form/DateRangeInputBase.js +1 -0
- package/dist/esm/form/DateRangeInputBase.js.map +1 -1
- package/dist/esm/form/DateTimeInput.js +1 -0
- package/dist/esm/form/DateTimeInput.js.map +1 -1
- package/dist/esm/form/DateTimeInputBase.js +1 -0
- package/dist/esm/form/DateTimeInputBase.js.map +1 -1
- package/dist/esm/form/Dropzone.js +1 -0
- package/dist/esm/form/Dropzone.js.map +1 -1
- package/dist/esm/form/Field.js +1 -0
- package/dist/esm/form/Field.js.map +1 -1
- package/dist/esm/form/Fieldset.js +1 -0
- package/dist/esm/form/Fieldset.js.map +1 -1
- package/dist/esm/form/File.js +1 -0
- package/dist/esm/form/File.js.map +1 -1
- package/dist/esm/form/FormContext.js +1 -0
- package/dist/esm/form/FormContext.js.map +1 -1
- package/dist/esm/form/Input.js +1 -0
- package/dist/esm/form/Input.js.map +1 -1
- package/dist/esm/form/InputBase.js +1 -0
- package/dist/esm/form/InputBase.js.map +1 -1
- package/dist/esm/form/Numberinput.js +1 -0
- package/dist/esm/form/Numberinput.js.map +1 -1
- package/dist/esm/form/PinInput.js +1 -0
- package/dist/esm/form/PinInput.js.map +1 -1
- package/dist/esm/form/Radio.js +1 -0
- package/dist/esm/form/Radio.js.map +1 -1
- package/dist/esm/form/RadioCard.js +1 -0
- package/dist/esm/form/RadioCard.js.map +1 -1
- package/dist/esm/form/Radios.js +1 -0
- package/dist/esm/form/Radios.js.map +1 -1
- package/dist/esm/form/Rate.js +1 -0
- package/dist/esm/form/Rate.js.map +1 -1
- package/dist/esm/form/Select.js +1 -0
- package/dist/esm/form/Select.js.map +1 -1
- package/dist/esm/form/SelectBase.js +1 -0
- package/dist/esm/form/SelectBase.js.map +1 -1
- package/dist/esm/form/Slider.js +1 -0
- package/dist/esm/form/Slider.js.map +1 -1
- package/dist/esm/form/Switch.js +1 -0
- package/dist/esm/form/Switch.js.map +1 -1
- package/dist/esm/form/Taginput.js +1 -0
- package/dist/esm/form/Taginput.js.map +1 -1
- package/dist/esm/form/TextArea.js +1 -0
- package/dist/esm/form/TextArea.js.map +1 -1
- package/dist/esm/form/TextAreaBase.js +1 -0
- package/dist/esm/form/TextAreaBase.js.map +1 -1
- package/dist/esm/form/TimeInput.js +1 -0
- package/dist/esm/form/TimeInput.js.map +1 -1
- package/dist/esm/form/TimeInputBase.js +1 -0
- package/dist/esm/form/TimeInputBase.js.map +1 -1
- package/dist/esm/form/_colorInternals/EyeDropperButton.js +41 -0
- package/dist/esm/form/_colorInternals/EyeDropperButton.js.map +1 -0
- package/dist/esm/form/_colorInternals/pickerState.js +5 -4
- package/dist/esm/form/_colorInternals/pickerState.js.map +1 -1
- package/dist/esm/form/_pickerInternals/Calendar.js +1 -0
- package/dist/esm/form/_pickerInternals/Calendar.js.map +1 -1
- package/dist/esm/form/_pickerInternals/PickerPopover.js +1 -0
- package/dist/esm/form/_pickerInternals/PickerPopover.js.map +1 -1
- package/dist/esm/form/_pickerInternals/TimeWheels.js +1 -0
- package/dist/esm/form/_pickerInternals/TimeWheels.js.map +1 -1
- package/dist/esm/form/_pickerInternals/useLocaleDefaults.js +1 -0
- package/dist/esm/form/_pickerInternals/useLocaleDefaults.js.map +1 -1
- package/dist/esm/form/_pickerInternals/useNativeMobilePicker.js +1 -0
- package/dist/esm/form/_pickerInternals/useNativeMobilePicker.js.map +1 -1
- package/dist/esm/form/_pickerInternals/useSegmentedEntry.js +1 -0
- package/dist/esm/form/_pickerInternals/useSegmentedEntry.js.map +1 -1
- package/dist/esm/form/controlLoading.js +1 -0
- package/dist/esm/form/controlLoading.js.map +1 -1
- package/dist/esm/form/groupFieldset.js +1 -0
- package/dist/esm/form/groupFieldset.js.map +1 -1
- package/dist/esm/form/useAutoLabelId.js +1 -0
- package/dist/esm/form/useAutoLabelId.js.map +1 -1
- package/dist/esm/grid/Cell.js +1 -0
- package/dist/esm/grid/Cell.js.map +1 -1
- package/dist/esm/grid/Grid.js +1 -0
- package/dist/esm/grid/Grid.js.map +1 -1
- package/dist/esm/helpers/ClientOnly.js +1 -0
- package/dist/esm/helpers/ClientOnly.js.map +1 -1
- package/dist/esm/helpers/Config.js +1 -0
- package/dist/esm/helpers/Config.js.map +1 -1
- package/dist/esm/helpers/LocaleProvider.js +1 -0
- package/dist/esm/helpers/LocaleProvider.js.map +1 -1
- package/dist/esm/helpers/Theme.js +1 -0
- package/dist/esm/helpers/Theme.js.map +1 -1
- package/dist/esm/helpers/flattenChildren.js +1 -0
- package/dist/esm/helpers/flattenChildren.js.map +1 -1
- package/dist/esm/helpers/inertProps.js +1 -0
- package/dist/esm/helpers/inertProps.js.map +1 -1
- package/dist/esm/helpers/localeFormat.js +1 -0
- package/dist/esm/helpers/localeFormat.js.map +1 -1
- package/dist/esm/helpers/overlayStack.js +1 -0
- package/dist/esm/helpers/overlayStack.js.map +1 -1
- package/dist/esm/helpers/portal.js +1 -0
- package/dist/esm/helpers/portal.js.map +1 -1
- package/dist/esm/helpers/scrollLock.js +1 -0
- package/dist/esm/helpers/scrollLock.js.map +1 -1
- package/dist/esm/helpers/statusRegion.js +1 -0
- package/dist/esm/helpers/statusRegion.js.map +1 -1
- package/dist/esm/helpers/typeahead.js +1 -0
- package/dist/esm/helpers/typeahead.js.map +1 -1
- package/dist/esm/helpers/useAnchoredPosition.js +1 -0
- package/dist/esm/helpers/useAnchoredPosition.js.map +1 -1
- package/dist/esm/helpers/useBulmaClasses.js +1 -0
- package/dist/esm/helpers/useBulmaClasses.js.map +1 -1
- package/dist/esm/helpers/useClientLayoutEffect.js +1 -0
- package/dist/esm/helpers/useClientLayoutEffect.js.map +1 -1
- package/dist/esm/helpers/useColorClasses.js +1 -0
- package/dist/esm/helpers/useColorClasses.js.map +1 -1
- package/dist/esm/helpers/useComboboxEngine.js +1 -0
- package/dist/esm/helpers/useComboboxEngine.js.map +1 -1
- package/dist/esm/helpers/useContextMenuTrigger.js +1 -0
- package/dist/esm/helpers/useContextMenuTrigger.js.map +1 -1
- package/dist/esm/helpers/useFlexboxClasses.js +1 -0
- package/dist/esm/helpers/useFlexboxClasses.js.map +1 -1
- package/dist/esm/helpers/useFocusTrap.js +1 -0
- package/dist/esm/helpers/useFocusTrap.js.map +1 -1
- package/dist/esm/helpers/useHoverOpen.js +1 -0
- package/dist/esm/helpers/useHoverOpen.js.map +1 -1
- package/dist/esm/helpers/useInertSiblings.js +1 -0
- package/dist/esm/helpers/useInertSiblings.js.map +1 -1
- package/dist/esm/helpers/useIsHydrated.js +1 -0
- package/dist/esm/helpers/useIsHydrated.js.map +1 -1
- package/dist/esm/helpers/useLatestRef.js +1 -0
- package/dist/esm/helpers/useLatestRef.js.map +1 -1
- package/dist/esm/helpers/useMenuKeys.js +1 -0
- package/dist/esm/helpers/useMenuKeys.js.map +1 -1
- package/dist/esm/helpers/useOtherClasses.js +1 -0
- package/dist/esm/helpers/useOtherClasses.js.map +1 -1
- package/dist/esm/helpers/useSpacingClasses.js +1 -0
- package/dist/esm/helpers/useSpacingClasses.js.map +1 -1
- package/dist/esm/helpers/useTreeView.js +1 -0
- package/dist/esm/helpers/useTreeView.js.map +1 -1
- package/dist/esm/helpers/useTypographyClasses.js +1 -0
- package/dist/esm/helpers/useTypographyClasses.js.map +1 -1
- package/dist/esm/helpers/useVisibilityClasses.js +1 -0
- package/dist/esm/helpers/useVisibilityClasses.js.map +1 -1
- package/dist/esm/layout/Container.js +1 -0
- package/dist/esm/layout/Container.js.map +1 -1
- package/dist/esm/layout/Footer.js +1 -0
- package/dist/esm/layout/Footer.js.map +1 -1
- package/dist/esm/layout/Hero.js +1 -0
- package/dist/esm/layout/Hero.js.map +1 -1
- package/dist/esm/layout/Level.js +1 -0
- package/dist/esm/layout/Level.js.map +1 -1
- package/dist/esm/layout/Media.js +1 -0
- package/dist/esm/layout/Media.js.map +1 -1
- package/dist/esm/layout/Section.js +1 -0
- package/dist/esm/layout/Section.js.map +1 -1
- package/dist/esm/layout/Splitter.js +1 -0
- package/dist/esm/layout/Splitter.js.map +1 -1
- package/dist/extras.css +1 -1
- package/dist/extras.css.map +1 -1
- package/dist/index.cjs +300 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +2 -0
- package/dist/index.esm.js.map +1 -1
- package/dist/types/components/DataTable.d.ts +1 -1
- package/dist/types/components/useDataTable.d.ts +36 -0
- package/dist/types/form/ColorInput.d.ts +76 -0
- package/dist/types/form/ColorInputBase.d.ts +108 -0
- package/dist/types/form/ColorPicker.d.ts +5 -0
- package/dist/types/form/_colorInternals/EyeDropperButton.d.ts +8 -0
- package/dist/types/form/_colorInternals/pickerState.d.ts +7 -1
- package/dist/types/index.d.ts +4 -0
- package/dist/types-cjs/components/DataTable.d.ts +1 -1
- package/dist/types-cjs/components/useDataTable.d.ts +36 -0
- package/dist/types-cjs/form/ColorInput.d.ts +76 -0
- package/dist/types-cjs/form/ColorInputBase.d.ts +108 -0
- package/dist/types-cjs/form/ColorPicker.d.ts +5 -0
- package/dist/types-cjs/form/_colorInternals/EyeDropperButton.d.ts +8 -0
- package/dist/types-cjs/form/_colorInternals/pickerState.d.ts +7 -1
- package/dist/types-cjs/index.d.ts +4 -0
- package/dist/versions/bestax-no-dark-mode.css +1 -1
- package/dist/versions/bestax-no-dark-mode.css.map +1 -1
- package/dist/versions/bestax-no-helpers-prefixed.css +1 -1
- package/dist/versions/bestax-no-helpers-prefixed.css.map +1 -1
- package/dist/versions/bestax-no-helpers.css +1 -1
- package/dist/versions/bestax-no-helpers.css.map +1 -1
- package/dist/versions/bestax-prefixed.css +1 -1
- package/dist/versions/bestax-prefixed.css.map +1 -1
- package/package.json +2 -1
- package/src/scss/form/_colorinput.scss +109 -0
- package/src/scss/form/_index.scss +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFlexboxClasses.js","sources":["../../../src/helpers/useFlexboxClasses.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { classNames } from './classNames';\nimport { useConfig } from './Config';\nimport {\n createBulmaClassHelpers,\n validFlexDirections,\n validFlexWraps,\n validJustifyContents,\n validAlignContents,\n validAlignItems,\n validAlignSelfs,\n validFlexGrowShrink,\n BulmaDisplayProps,\n} from './bulmaClassHelpers';\n\n/**\n * Props for applying Bulma flexbox helper classes. The display props are read\n * only to gate the flex container helpers; this hook never emits display\n * classes itself.\n */\nexport interface BulmaFlexboxProps extends BulmaDisplayProps {\n /** Flex direction (e.g., 'row', 'column'). */\n flexDirection?: (typeof validFlexDirections)[number];\n /** Flex wrap (e.g., 'wrap', 'nowrap'). */\n flexWrap?: (typeof validFlexWraps)[number];\n /** Justify content (e.g., 'center', 'space-between'). */\n justifyContent?: (typeof validJustifyContents)[number];\n /** Align content (e.g., 'center', 'stretch'). */\n alignContent?: (typeof validAlignContents)[number];\n /** Align items (e.g., 'center', 'flex-start'). */\n alignItems?: (typeof validAlignItems)[number];\n /** Align self (e.g., 'auto', 'center'). */\n alignSelf?: (typeof validAlignSelfs)[number];\n /** Flex grow value (e.g., '0', '1', '2', '3', '4', '5'). */\n flexGrow?: (typeof validFlexGrowShrink)[number];\n /** Flex shrink value (e.g., '0', '1', '2', '3', '4', '5'). */\n flexShrink?: (typeof validFlexGrowShrink)[number];\n}\n\n/**\n * A hook that generates Bulma flexbox helper classes. Flex container helpers\n * (direction, wrap, justify/align content, align items) are only emitted when\n * one of the display props indicates a flex display; flex item helpers\n * (align self, grow, shrink) are always emitted.\n *\n * Note: this hook reads the display props purely as a gate and never emits\n * display classes — `is-flex` and friends come from useVisibilityClasses (or\n * the useBulmaClasses aggregate).\n *\n * @function useFlexboxClasses\n * @param props - Flexbox-related Bulma helper props.\n * @returns A space-separated string of flexbox helper classes.\n * @example\n * const flexboxClasses = useFlexboxClasses({\n * display: 'flex', // gates the container helpers, but emits no 'is-flex'\n * justifyContent: 'center',\n * });\n * // flexboxClasses: 'is-justify-content-center'\n * // ('is-flex' itself comes from useVisibilityClasses/useBulmaClasses)\n */\nexport const useFlexboxClasses = (props: BulmaFlexboxProps): string => {\n const { classPrefix } = useConfig();\n\n const {\n flexDirection,\n flexWrap,\n justifyContent,\n alignContent,\n alignItems,\n alignSelf,\n flexGrow,\n flexShrink,\n display,\n displayMobile,\n displayTablet,\n displayTabletOnly,\n displayTouch,\n displayDesktop,\n displayDesktopOnly,\n displayWidescreen,\n displayWidescreenOnly,\n displayFullhd,\n } = props;\n\n return useMemo(() => {\n const { classes, addClassNoViewport } =\n createBulmaClassHelpers(classPrefix);\n\n // Flexbox\n const hasFlexDisplay =\n display === 'flex' ||\n display === 'inline-flex' ||\n displayMobile === 'flex' ||\n displayMobile === 'inline-flex' ||\n displayTablet === 'flex' ||\n displayTablet === 'inline-flex' ||\n displayTabletOnly === 'flex' ||\n displayTabletOnly === 'inline-flex' ||\n displayTouch === 'flex' ||\n displayTouch === 'inline-flex' ||\n displayDesktop === 'flex' ||\n displayDesktop === 'inline-flex' ||\n displayDesktopOnly === 'flex' ||\n displayDesktopOnly === 'inline-flex' ||\n displayWidescreen === 'flex' ||\n displayWidescreen === 'inline-flex' ||\n displayWidescreenOnly === 'flex' ||\n displayWidescreenOnly === 'inline-flex' ||\n displayFullhd === 'flex' ||\n displayFullhd === 'inline-flex';\n\n if (hasFlexDisplay) {\n // Flexbox container properties do not support viewport modifiers in Bulma\n addClassNoViewport(\n 'is-flex-direction',\n flexDirection,\n validFlexDirections\n );\n addClassNoViewport('is-flex-wrap', flexWrap, validFlexWraps);\n addClassNoViewport(\n 'is-justify-content',\n justifyContent,\n validJustifyContents\n );\n addClassNoViewport('is-align-content', alignContent, validAlignContents);\n addClassNoViewport('is-align-items', alignItems, validAlignItems);\n }\n\n // Flex item properties (can be applied to any element that is a flex item)\n // These don't require the element itself to have display: flex\n addClassNoViewport('is-align-self', alignSelf, validAlignSelfs);\n addClassNoViewport('is-flex-grow', flexGrow, validFlexGrowShrink);\n addClassNoViewport('is-flex-shrink', flexShrink, validFlexGrowShrink);\n\n return classNames(classes);\n }, [\n classPrefix,\n flexDirection,\n flexWrap,\n justifyContent,\n alignContent,\n alignItems,\n alignSelf,\n flexGrow,\n flexShrink,\n display,\n displayMobile,\n displayTablet,\n displayTabletOnly,\n displayTouch,\n displayDesktop,\n displayDesktopOnly,\n displayWidescreen,\n displayWidescreenOnly,\n displayFullhd,\n ]);\n};\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useFlexboxClasses.js","sources":["../../../src/helpers/useFlexboxClasses.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { classNames } from './classNames';\nimport { useConfig } from './Config';\nimport {\n createBulmaClassHelpers,\n validFlexDirections,\n validFlexWraps,\n validJustifyContents,\n validAlignContents,\n validAlignItems,\n validAlignSelfs,\n validFlexGrowShrink,\n BulmaDisplayProps,\n} from './bulmaClassHelpers';\n\n/**\n * Props for applying Bulma flexbox helper classes. The display props are read\n * only to gate the flex container helpers; this hook never emits display\n * classes itself.\n */\nexport interface BulmaFlexboxProps extends BulmaDisplayProps {\n /** Flex direction (e.g., 'row', 'column'). */\n flexDirection?: (typeof validFlexDirections)[number];\n /** Flex wrap (e.g., 'wrap', 'nowrap'). */\n flexWrap?: (typeof validFlexWraps)[number];\n /** Justify content (e.g., 'center', 'space-between'). */\n justifyContent?: (typeof validJustifyContents)[number];\n /** Align content (e.g., 'center', 'stretch'). */\n alignContent?: (typeof validAlignContents)[number];\n /** Align items (e.g., 'center', 'flex-start'). */\n alignItems?: (typeof validAlignItems)[number];\n /** Align self (e.g., 'auto', 'center'). */\n alignSelf?: (typeof validAlignSelfs)[number];\n /** Flex grow value (e.g., '0', '1', '2', '3', '4', '5'). */\n flexGrow?: (typeof validFlexGrowShrink)[number];\n /** Flex shrink value (e.g., '0', '1', '2', '3', '4', '5'). */\n flexShrink?: (typeof validFlexGrowShrink)[number];\n}\n\n/**\n * A hook that generates Bulma flexbox helper classes. Flex container helpers\n * (direction, wrap, justify/align content, align items) are only emitted when\n * one of the display props indicates a flex display; flex item helpers\n * (align self, grow, shrink) are always emitted.\n *\n * Note: this hook reads the display props purely as a gate and never emits\n * display classes — `is-flex` and friends come from useVisibilityClasses (or\n * the useBulmaClasses aggregate).\n *\n * @function useFlexboxClasses\n * @param props - Flexbox-related Bulma helper props.\n * @returns A space-separated string of flexbox helper classes.\n * @example\n * const flexboxClasses = useFlexboxClasses({\n * display: 'flex', // gates the container helpers, but emits no 'is-flex'\n * justifyContent: 'center',\n * });\n * // flexboxClasses: 'is-justify-content-center'\n * // ('is-flex' itself comes from useVisibilityClasses/useBulmaClasses)\n */\nexport const useFlexboxClasses = (props: BulmaFlexboxProps): string => {\n const { classPrefix } = useConfig();\n\n const {\n flexDirection,\n flexWrap,\n justifyContent,\n alignContent,\n alignItems,\n alignSelf,\n flexGrow,\n flexShrink,\n display,\n displayMobile,\n displayTablet,\n displayTabletOnly,\n displayTouch,\n displayDesktop,\n displayDesktopOnly,\n displayWidescreen,\n displayWidescreenOnly,\n displayFullhd,\n } = props;\n\n return useMemo(() => {\n const { classes, addClassNoViewport } =\n createBulmaClassHelpers(classPrefix);\n\n // Flexbox\n const hasFlexDisplay =\n display === 'flex' ||\n display === 'inline-flex' ||\n displayMobile === 'flex' ||\n displayMobile === 'inline-flex' ||\n displayTablet === 'flex' ||\n displayTablet === 'inline-flex' ||\n displayTabletOnly === 'flex' ||\n displayTabletOnly === 'inline-flex' ||\n displayTouch === 'flex' ||\n displayTouch === 'inline-flex' ||\n displayDesktop === 'flex' ||\n displayDesktop === 'inline-flex' ||\n displayDesktopOnly === 'flex' ||\n displayDesktopOnly === 'inline-flex' ||\n displayWidescreen === 'flex' ||\n displayWidescreen === 'inline-flex' ||\n displayWidescreenOnly === 'flex' ||\n displayWidescreenOnly === 'inline-flex' ||\n displayFullhd === 'flex' ||\n displayFullhd === 'inline-flex';\n\n if (hasFlexDisplay) {\n // Flexbox container properties do not support viewport modifiers in Bulma\n addClassNoViewport(\n 'is-flex-direction',\n flexDirection,\n validFlexDirections\n );\n addClassNoViewport('is-flex-wrap', flexWrap, validFlexWraps);\n addClassNoViewport(\n 'is-justify-content',\n justifyContent,\n validJustifyContents\n );\n addClassNoViewport('is-align-content', alignContent, validAlignContents);\n addClassNoViewport('is-align-items', alignItems, validAlignItems);\n }\n\n // Flex item properties (can be applied to any element that is a flex item)\n // These don't require the element itself to have display: flex\n addClassNoViewport('is-align-self', alignSelf, validAlignSelfs);\n addClassNoViewport('is-flex-grow', flexGrow, validFlexGrowShrink);\n addClassNoViewport('is-flex-shrink', flexShrink, validFlexGrowShrink);\n\n return classNames(classes);\n }, [\n classPrefix,\n flexDirection,\n flexWrap,\n justifyContent,\n alignContent,\n alignItems,\n alignSelf,\n flexGrow,\n flexShrink,\n display,\n displayMobile,\n displayTablet,\n displayTabletOnly,\n displayTouch,\n displayDesktop,\n displayDesktopOnly,\n displayWidescreen,\n displayWidescreenOnly,\n displayFullhd,\n ]);\n};\n"],"names":[],"mappings":";;;;;;;AAuCA;;;;;;;;;;;;;;;;;;;;AAoBG;AACI,MAAM,iBAAiB,GAAG,CAAC,KAAwB,KAAY;AACpE,IAAA,MAAM,EAAE,WAAW,EAAE,GAAG,SAAS,EAAE;AAEnC,IAAA,MAAM,EACJ,aAAa,EACb,QAAQ,EACR,cAAc,EACd,YAAY,EACZ,UAAU,EACV,SAAS,EACT,QAAQ,EACR,UAAU,EACV,OAAO,EACP,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,qBAAqB,EACrB,aAAa,GACd,GAAG,KAAK;IAET,OAAO,OAAO,CAAC,MAAK;QAClB,MAAM,EAAE,OAAO,EAAE,kBAAkB,EAAE,GACnC,uBAAuB,CAAC,WAAW,CAAC;;AAGtC,QAAA,MAAM,cAAc,GAClB,OAAO,KAAK,MAAM;AAClB,YAAA,OAAO,KAAK,aAAa;AACzB,YAAA,aAAa,KAAK,MAAM;AACxB,YAAA,aAAa,KAAK,aAAa;AAC/B,YAAA,aAAa,KAAK,MAAM;AACxB,YAAA,aAAa,KAAK,aAAa;AAC/B,YAAA,iBAAiB,KAAK,MAAM;AAC5B,YAAA,iBAAiB,KAAK,aAAa;AACnC,YAAA,YAAY,KAAK,MAAM;AACvB,YAAA,YAAY,KAAK,aAAa;AAC9B,YAAA,cAAc,KAAK,MAAM;AACzB,YAAA,cAAc,KAAK,aAAa;AAChC,YAAA,kBAAkB,KAAK,MAAM;AAC7B,YAAA,kBAAkB,KAAK,aAAa;AACpC,YAAA,iBAAiB,KAAK,MAAM;AAC5B,YAAA,iBAAiB,KAAK,aAAa;AACnC,YAAA,qBAAqB,KAAK,MAAM;AAChC,YAAA,qBAAqB,KAAK,aAAa;AACvC,YAAA,aAAa,KAAK,MAAM;YACxB,aAAa,KAAK,aAAa;QAEjC,IAAI,cAAc,EAAE;;AAElB,YAAA,kBAAkB,CAChB,mBAAmB,EACnB,aAAa,EACb,mBAAmB,CACpB;AACD,YAAA,kBAAkB,CAAC,cAAc,EAAE,QAAQ,EAAE,cAAc,CAAC;AAC5D,YAAA,kBAAkB,CAChB,oBAAoB,EACpB,cAAc,EACd,oBAAoB,CACrB;AACD,YAAA,kBAAkB,CAAC,kBAAkB,EAAE,YAAY,EAAE,kBAAkB,CAAC;AACxE,YAAA,kBAAkB,CAAC,gBAAgB,EAAE,UAAU,EAAE,eAAe,CAAC;QACnE;;;AAIA,QAAA,kBAAkB,CAAC,eAAe,EAAE,SAAS,EAAE,eAAe,CAAC;AAC/D,QAAA,kBAAkB,CAAC,cAAc,EAAE,QAAQ,EAAE,mBAAmB,CAAC;AACjE,QAAA,kBAAkB,CAAC,gBAAgB,EAAE,UAAU,EAAE,mBAAmB,CAAC;AAErE,QAAA,OAAO,UAAU,CAAC,OAAO,CAAC;AAC5B,IAAA,CAAC,EAAE;QACD,WAAW;QACX,aAAa;QACb,QAAQ;QACR,cAAc;QACd,YAAY;QACZ,UAAU;QACV,SAAS;QACT,QAAQ;QACR,UAAU;QACV,OAAO;QACP,aAAa;QACb,aAAa;QACb,iBAAiB;QACjB,YAAY;QACZ,cAAc;QACd,kBAAkB;QAClB,iBAAiB;QACjB,qBAAqB;QACrB,aAAa;AACd,KAAA,CAAC;AACJ;;"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
|
+
'use client';
|
|
2
3
|
import { useRef, useState, useEffect } from 'react';
|
|
3
4
|
import { useIsHydrated } from './useIsHydrated.js';
|
|
4
5
|
import { getDeepestActiveElement } from './shadowDom.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFocusTrap.js","sources":["../../../src/helpers/useFocusTrap.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport type { RefObject } from 'react';\nimport { useIsHydrated } from './useIsHydrated';\nimport { getDeepestActiveElement } from './shadowDom';\n\n/**\n * Whether `el` is editable while its parent is not, which makes it an editing\n * host: the one tab stop an editable region gets. `contenteditable=\"false\"`\n * turns editing off, and a missing or unknown value inherits the parent's.\n */\nfunction isEditingHost(el: Element): boolean {\n const editable = (node: Element): boolean | null => {\n const value = node.getAttribute('contenteditable')?.toLowerCase();\n if (value === undefined) return null;\n if (value === '' || value === 'true' || value === 'plaintext-only') {\n return true;\n }\n return value === 'false' ? false : null;\n };\n if (editable(el) !== true) return false;\n for (let node = el.parentElement; node; node = node.parentElement) {\n const state = editable(node);\n if (state !== null) return !state;\n }\n return true;\n}\n\n/** Whether `el` is the summary of its `<details>`: the first one, not a stray. */\nconst isDetailsSummary = (el: Element): boolean =>\n el.parentElement?.localName === 'details' &&\n Array.from(el.parentElement.children).find(\n child => child.localName === 'summary'\n ) === el;\n\n/**\n * Whether the browser puts `el` in the tab order, before anything that hides\n * or disables it. The `tabIndex` property can't answer this on its own: it\n * can read `-1` for an editable region or an `<embed>` that Tab still visits,\n * and `0` for a `<video>` without controls that it doesn't. So a valid\n * `tabindex` attribute decides, and otherwise the element's kind does.\n * Internal like `focusTabStop`: `src/index.ts` names what it takes from here.\n */\nexport function isTabbableKind(el: Element): boolean {\n const tabindex = el.getAttribute('tabindex');\n if (tabindex !== null && /^\\s*[+-]?\\d/.test(tabindex)) {\n return (el as HTMLElement).tabIndex >= 0;\n }\n switch (el.localName) {\n case 'a':\n case 'area':\n return el.hasAttribute('href');\n case 'input':\n return (el as HTMLInputElement).type !== 'hidden';\n case 'button':\n case 'select':\n case 'textarea':\n case 'iframe':\n case 'embed':\n case 'object':\n return true;\n case 'audio':\n case 'video':\n return el.hasAttribute('controls');\n case 'summary':\n return isDetailsSummary(el);\n default:\n return isEditingHost(el);\n }\n}\n\n/**\n * The node's parent in the flattened tree, the one the browser renders and\n * tabs through: the slot it is assigned to, else its parent, else the host of\n * the shadow root it tops, and nothing above the document. A closed shadow\n * root hides its slots (`assignedSlot` reads `null` under one), so a child of\n * its host goes straight to the host, whether a slot takes it or not.\n */\nconst flatParent = (node: Node): Node | null =>\n (node as Element).assignedSlot ??\n node.parentElement ??\n (node.parentNode as ShadowRoot | null)?.host ??\n null;\n\n/**\n * The element's children in the flattened tree, in the order they render: a\n * host's shadow root content, a slot's assigned elements (or its fallback\n * content when nothing is assigned), anything else's own children. A host\n * whose shadow root is closed has no root to read, so its own children stand\n * in, including any that no slot takes and that aren't rendered.\n */\nfunction flatChildren(el: Element): Element[] {\n if (el.shadowRoot) return Array.from(el.shadowRoot.children);\n if (el.localName === 'slot') {\n const assigned = (el as HTMLSlotElement).assignedNodes();\n if (assigned.length > 0) {\n return assigned.filter(\n (node): node is Element => node.nodeType === Node.ELEMENT_NODE\n );\n }\n }\n return Array.from(el.children);\n}\n\nconst isShown = (el: Element): boolean =>\n !el.hasAttribute('inert') && getComputedStyle(el).display !== 'none';\n\nconst isVisible = (el: Element): boolean => {\n const { visibility } = getComputedStyle(el);\n return visibility !== 'hidden' && visibility !== 'collapse';\n};\n\n/**\n * Whether nothing from `el` up to `top` hides it: `inert`, `display: none`\n * (not inherited, so each element is checked) or a closed `<details>` it\n * isn't the summary of. `top` itself isn't checked, and `null` walks to the\n * top of the page.\n */\nfunction isShownUpTo(el: Element, top: Node | null): boolean {\n let below: Element | null = null;\n for (\n let node: Element | null = el;\n node && node !== top;\n node = flatParent(node) as Element | null\n ) {\n if (!isShown(node)) return false;\n if (\n below &&\n node.localName === 'details' &&\n !node.hasAttribute('open') &&\n !isDetailsSummary(below)\n ) {\n return false;\n }\n below = node;\n }\n return true;\n}\n\n/**\n * The images that use the image map `area` belongs to. An image names its\n * map with `usemap=\"#…\"`, matching the map's `name` or `id`.\n */\nfunction imagesUsingMapOf(area: Element): Element[] {\n const map = area.closest('map');\n if (!map) return [];\n const names = [map.getAttribute('name'), map.id].filter(name => name);\n return Array.from(\n (area.getRootNode() as ParentNode).querySelectorAll('img[usemap]')\n ).filter(img => {\n const ref = img.getAttribute('usemap') as string;\n return ref.startsWith('#') && names.includes(ref.slice(1));\n });\n}\n\n/**\n * Whether an image map area is shown. An area has no box of its own: the\n * HTML rendering rules give it `display: none`, though a browser may not\n * report that. So it counts where an image using its map is shown, as long\n * as nothing makes it inert.\n */\nfunction isAreaShown(area: Element, container: Element): boolean {\n for (\n let node: Element | null = area;\n node && node !== container;\n node = flatParent(node) as Element | null\n ) {\n if (node.hasAttribute('inert')) return false;\n }\n return imagesUsingMapOf(area).some(\n img => isShownUpTo(img, null) && isVisible(img)\n );\n}\n\n/**\n * Whether Tab can land on `el`, one of the kinds it visits. A disabled control\n * (including one inside a disabled `<fieldset>`), an inert subtree and\n * anything not rendered are skipped. `visibility` is inherited, so the\n * element's own computed value covers its ancestors.\n */\nfunction isRenderedAndEnabled(el: Element, container: Element): boolean {\n if (el.matches(':disabled')) return false;\n if (el.localName === 'area') return isAreaShown(el, container);\n return isShownUpTo(el, container) && isVisible(el);\n}\n\n/** A radio button with a name, which shares one tab stop with its group. */\nconst isGroupedRadio = (el: Element): el is HTMLInputElement =>\n el.localName === 'input' &&\n (el as HTMLInputElement).type === 'radio' &&\n (el as HTMLInputElement).name !== '';\n\n/** Whether two radio buttons are in one group: same name, form owner and tree. */\nconst sameGroup = (a: HTMLInputElement, b: Element): b is HTMLInputElement =>\n isGroupedRadio(b) &&\n b.name === a.name &&\n b.form === a.form &&\n b.getRootNode() === a.getRootNode();\n\n/**\n * The checked button of `radio`'s group. The group isn't bounded by the trap,\n * so this looks through the radio's whole tree.\n */\nconst checkedInGroup = (radio: HTMLInputElement): Element | undefined =>\n Array.from(\n (radio.getRootNode() as ParentNode).querySelectorAll('input:checked')\n ).find(el => sameGroup(radio, el));\n\n/** The flattened-tree ancestors of `node` below the container, outermost first, then `node`. */\nfunction flatChain(node: Node, container: Node): Node[] {\n const chain: Node[] = [];\n for (let n: Node | null = node; n && n !== container; n = flatParent(n)) {\n chain.unshift(n);\n }\n return chain;\n}\n\n/**\n * Whether `node` is inside the container in the flattened tree: in it, in a\n * shadow root it holds, or slotted into it. A child of a closed-root host\n * counts as inside wherever the host is, as its slot can't be seen.\n */\nfunction inside(container: Node, node: Node): boolean {\n for (let n: Node | null = node; n; n = flatParent(n)) {\n if (n === container) return true;\n }\n return false;\n}\n\n/**\n * `true` when Tab reaches `b` after `a`, which is their order in the\n * flattened tree: a shadow host comes before its shadow root's content, and\n * slotted content comes where its slot is. A closed root's slots can't be\n * seen, so its host's children keep their place in the light DOM. Two\n * flattened-tree siblings are always in one DOM tree, so their document\n * position decides between them.\n */\nfunction follows(container: Element, a: Node, b: Node): boolean {\n const chainA = flatChain(a, container);\n const chainB = flatChain(b, container);\n for (let i = 0; i < Math.max(chainA.length, chainB.length); i++) {\n const [x, y] = [chainA[i], chainB[i]];\n if (x === y) continue;\n if (!x) return true;\n if (!y) return false;\n return Boolean(\n x.compareDocumentPosition(y) & Node.DOCUMENT_POSITION_FOLLOWING\n );\n }\n return false;\n}\n\n/**\n * Every element of a tabbable kind under `el` in the flattened tree, depth\n * first, which is the order Tab visits them. Under a host whose shadow root\n * is closed, that takes in the host's own children instead, rendered or not.\n */\nfunction collectCandidates(\n el: Element,\n out: HTMLElement[] = []\n): HTMLElement[] {\n for (const child of flatChildren(el)) {\n if (isTabbableKind(child)) out.push(child as HTMLElement);\n collectCandidates(child, out);\n }\n return out;\n}\n\ninterface TabStops {\n first: HTMLElement;\n last: HTMLElement;\n /** The stop that stands for `node`: its group's stop for a radio button. */\n stopFor(node: Element): Element;\n /** The stops from one end inward, the last first when `fromLast`. */\n from(fromLast: boolean): Generator<HTMLElement>;\n}\n\n/**\n * The first and last tab stops inside `container`, or `null` when there are\n * none. Only the ends matter for wrapping, so the style checks run from each\n * end until they find a stop rather than over every candidate.\n *\n * A radio group is one stop, the way the browser treats it: its checked\n * button, or its first when none is checked. Counting every button would make\n * the group's last one an end, and Tab from the checked one would leave. The\n * group goes by name, form owner and tree, not by the trap, so a checked\n * button outside the container that Tab can land on leaves the group no stop\n * inside. One that Tab can't land on leaves the group as if none were checked.\n */\nfunction findTabStops(container: HTMLElement): TabStops | null {\n const candidates = collectCandidates(container);\n const checked = new Map<Element, boolean>();\n const usable = (el: Element): boolean => {\n let ok = checked.get(el);\n if (ok === undefined) {\n ok = isRenderedAndEnabled(el, container);\n checked.set(el, ok);\n }\n return ok;\n };\n // Resolved once per group, filed under each of its buttons, since finding\n // the checked one scans the radio's whole tree.\n const groupStops = new Map<Element, HTMLElement | undefined>();\n const groupStop = (radio: HTMLInputElement): HTMLElement | undefined => {\n if (groupStops.has(radio)) return groupStops.get(radio);\n const group = candidates.filter(el => sameGroup(radio, el));\n const on = checkedInGroup(radio);\n const stop =\n on && isTabbableKind(on) && usable(on)\n ? group.find(el => el === on)\n : group.find(usable);\n for (const el of [radio, ...group]) groupStops.set(el, stop);\n return stop;\n };\n const isStop = (el: HTMLElement): boolean =>\n usable(el) && (!isGroupedRadio(el) || groupStop(el) === el);\n // Checked only as they're reached, and the checks are kept, so finding\n // both ends costs what it did when only the ends were looked for.\n function* from(fromLast: boolean): Generator<HTMLElement> {\n for (let i = 0; i < candidates.length; i++) {\n const el = candidates[fromLast ? candidates.length - 1 - i : i];\n if (isStop(el)) yield el;\n }\n }\n\n const first = from(false).next().value;\n if (!first) return null;\n return {\n first,\n last: from(true).next().value as HTMLElement,\n stopFor: node => (isGroupedRadio(node) && groupStop(node)) || node,\n from,\n };\n}\n\n/**\n * The element that has focus in the container's own document (an iframe's,\n * when it renders into one), inside open shadow roots too. Focus inside a\n * closed one reads as its host, which the comparisons above place like any\n * other element. Browsers fall back to <body> when nothing has\n * focus. jsdom, where consumers run their tests, reports nothing once focus\n * was inside a shadow root that was removed, so this falls back for it.\n */\nconst focusedElement = (doc: Document): Element =>\n getDeepestActiveElement(doc) ?? doc.body;\n\n/**\n * Moves focus to the first tab stop inside `container`, or the last when\n * `fromLast`, and to the container when there is none. Every move a trap\n * makes goes through here: into the container as it opens, to the other end\n * on a wrap, and back in after a Tab the browser carried out.\n *\n * A stop that doesn't take focus is passed over for the next one in. An\n * `<object>` or `<embed>` with nothing to show is one: it counts as a stop by\n * its kind, but browsers refuse to focus it, and which ones they refuse\n * depends on the browser and on what has loaded, so only trying tells. A\n * stop took focus when focus moved, wherever it went (a stop can hand focus\n * on), or when focus was on it already. `src/index.ts` names its exports from\n * this module, so this one stays internal.\n */\nexport function focusTabStop(container: HTMLElement, fromLast = false): void {\n const doc = container.ownerDocument;\n for (const stop of findTabStops(container)?.from(fromLast) ?? []) {\n const before = focusedElement(doc);\n stop.focus();\n const now = focusedElement(doc);\n if (now !== before || inside(stop, now)) return;\n }\n container.focus();\n}\n\n/**\n * Handles a Tab for a trap on `container`. When the browser has no tab stop\n * left in that direction inside the container, focus goes to the stop at the\n * other end and the key is marked used, and with no stops at all the\n * container takes focus. Focus outside the container goes in at that end\n * too. Returns whether it moved focus; otherwise the browser does.\n */\nfunction wrapTab(e: KeyboardEvent, container: HTMLElement): boolean {\n const stops = findTabStops(container);\n if (!stops) {\n e.preventDefault();\n container.focus();\n return true;\n }\n const focused = focusedElement(container.ownerDocument);\n const current = stops.stopFor(focused);\n // Wrap whenever the browser has no stop left in that direction inside the\n // container. Comparing positions rather than testing `=== first` also\n // covers focus resting on the container or on an element outside the tab\n // order, which the browser would otherwise walk straight out of.\n if (\n !inside(container, focused) ||\n (e.shiftKey\n ? !follows(container, stops.first, current)\n : !follows(container, current, stops.last))\n ) {\n e.preventDefault();\n focusTabStop(container, e.shiftKey);\n return true;\n }\n return false;\n}\n\n/**\n * Holds Tab inside the element `container` returns until the returned\n * function is called. Tab and Shift+Tab heard on `keyTarget` wrap at the\n * ends, unless something already marked the key used, and if a Tab the trap\n * let through still takes focus out, it sends focus back to the other end.\n *\n * `container` is read each time a key or a focus change arrives, so an\n * overlay whose nodes are remounted while it's open, as moving to another\n * portal target does, stays trapped without attaching again. `holds` is asked\n * on every Tab, and the trap leaves the key alone while it says no. Internal\n * like `focusTabStop`. It's for an overlay that holds Tab from a listener of\n * its own on `window` and asks the overlay stack first, so it stands aside\n * while another overlay is open over it, which a trap listening on the\n * container can't do.\n */\nexport function trapTab(\n container: () => HTMLElement | null,\n keyTarget: HTMLElement | Window,\n holds: () => boolean = () => true\n): () => void {\n // The document focus moves in: the window's own, or the element's.\n const doc =\n 'document' in keyTarget ? keyTarget.document : keyTarget.ownerDocument;\n // A window and an element type their listeners differently, so the trap\n // listens through the base type both share.\n const keys: EventTarget = keyTarget;\n // The end focus goes back to if a Tab the trap let the browser handle\n // takes it out (the last for Shift+Tab), held until that Tab has finished\n // moving focus, and `null` otherwise.\n let wrapFromLast: boolean | null = null;\n let wrapTimer: ReturnType<typeof setTimeout> | undefined;\n\n // `keyTarget` is any target, so the listener takes the base event type.\n const handleKeyDown = (event: Event) => {\n const e = event as KeyboardEvent;\n const box = container();\n // A trap nested inside this one has already wrapped focus.\n if (e.key !== 'Tab' || e.defaultPrevented || !box || !holds()) return;\n if (wrapTab(e, box)) return;\n wrapFromLast = e.shiftKey;\n clearTimeout(wrapTimer);\n wrapTimer = setTimeout(() => {\n wrapFromLast = null;\n });\n };\n\n // The browser moves focus as soon as the keydown is over, so focus that\n // lands outside while `wrapFromLast` is set got there by that Tab.\n const handleFocusIn = (e: FocusEvent) => {\n const box = container();\n if (wrapFromLast === null || !box) return;\n if (inside(box, e.composedPath()[0] as Node)) return;\n const fromLast = wrapFromLast;\n wrapFromLast = null;\n focusTabStop(box, fromLast);\n };\n\n keys.addEventListener('keydown', handleKeyDown);\n doc.addEventListener('focusin', handleFocusIn, true);\n return () => {\n keys.removeEventListener('keydown', handleKeyDown);\n doc.removeEventListener('focusin', handleFocusIn, true);\n clearTimeout(wrapTimer);\n };\n}\n\n/**\n * Options for `useFocusTrap`.\n */\nexport interface UseFocusTrapOptions {\n /**\n * Whether the trap is on. Turning it on moves focus into the container;\n * turning it off (or unmounting) releases Tab and restores focus.\n * @defaultValue true\n */\n active?: boolean;\n /**\n * The element inside the container to focus when the trap turns on. It has\n * to be one that can take focus, so a heading needs `tabIndex={-1}`. When\n * focusing it doesn't put focus inside the container (the ref is empty, or\n * its element is outside, disabled, hidden or can't take focus), focus goes\n * to the first tab stop inside the container that takes focus, and to the\n * container itself when none does.\n */\n initialFocusRef?: RefObject<HTMLElement | null>;\n /**\n * Where focus goes when the trap turns off. `true` returns it to the element\n * that had focus when the trap turned on; a ref sends it to that element\n * instead; `false` leaves it alone. Focus is only moved while the trap still\n * holds it, so a click that already put focus somewhere else outside the\n * container keeps it there.\n *\n * `true` reads focus after the container's content has mounted, so content\n * that focuses itself as it mounts (an `autoFocus` input) hides the element\n * that opened it. And some browsers don't focus a button when it is\n * clicked. For a panel opened from a button, pass the button's ref.\n * @defaultValue true\n */\n restoreFocus?: boolean | RefObject<HTMLElement | null>;\n}\n\n/**\n * Keeps keyboard focus inside a container while it is active: focus moves in\n * when the trap turns on, Tab and Shift+Tab wrap between the first and last\n * tab stops, and focus goes back where it came from when the trap turns off.\n *\n * Tab stops are what the browser's Tab visits, in document order: links,\n * form controls, frames, media with controls, a `<details>` summary, editable\n * regions, anything with `tabIndex={0}`, and the content of open shadow\n * roots. Hidden, disabled, inert and `tabIndex={-1}` elements are skipped,\n * and a radio group counts once. Give the container `tabIndex={-1}` so it can\n * hold focus itself when nothing inside can. The trap listens on the\n * container, so a trap nested inside another handles Tab first and the outer\n * one stands aside.\n *\n * The trap is about Tab. If a Tab it let through still takes focus out (a\n * browser can leave out a stop the page has, such as links when its settings\n * say to), it sends focus back to the other end. A pointer or a script moving\n * focus out is left alone, because pulling focus back from a click is modal\n * behaviour, and a modal also has to block the page behind it, which `Modal`\n * does. Content the container renders through a portal lives elsewhere in\n * the DOM, so it is outside the trap: render nested overlays inside the\n * container, or give them a trap of their own. To trap content a `Portal`\n * renders, pass a ref to the element inside the `Portal` as `containerRef`.\n *\n * The trap looks at `containerRef` again each time the component calling it\n * renders. A container that mounts after the trap turns on, such as a panel\n * waiting for a `Portal` target held in state, has it attach once that\n * component renders with the container in the DOM. When the container leaves\n * or is replaced, the trap lets go of it as it would turning off, handing\n * focus back, and attaches to the next one.\n * A child that mounts the container in a render of its own (from its own\n * state, or as a `Suspense` boundary resolves) goes unseen until the\n * component calling the hook renders again. The trap waits for hydration, so\n * that component renders again when the page hydrates, and portaled content,\n * which first appears then, is found.\n *\n * @function useFocusTrap\n * @param containerRef - Ref to the element focus stays inside.\n * @param options - When the trap is on, and where focus starts and ends.\n *\n * @example\n * const panelRef = useRef<HTMLDivElement>(null);\n * useFocusTrap(panelRef, { active: isOpen });\n * return isOpen ? (\n * <div ref={panelRef} role=\"dialog\" aria-label=\"Filters\" tabIndex={-1}>\n * …\n * </div>\n * ) : null;\n */\nexport function useFocusTrap(\n containerRef: RefObject<HTMLElement | null>,\n options: UseFocusTrapOptions = {}\n): void {\n const { active = true, initialFocusRef, restoreFocus = true } = options;\n // Portaled content has no server-rendered counterpart, so a portaled\n // container only exists from the commit after hydration.\n const hydrated = useIsHydrated();\n // The container the trap is attached to, or `null` while it has none.\n const heldRef = useRef<HTMLElement | null>(null);\n // Bumped to run the trap again when the container in the ref changes.\n const [refChanges, setRefChanges] = useState(0);\n\n useEffect(() => {\n heldRef.current = null;\n if (!active || !hydrated) return undefined;\n const container = containerRef.current;\n if (!container) return undefined;\n heldRef.current = container;\n const doc = container.ownerDocument;\n\n const opener = focusedElement(doc) as HTMLElement;\n\n // `initialFocusRef` counts only when focusing it puts focus inside. Focus\n // put outside would stay out, as Tab there never reaches the container's\n // listener, and focusing something that can't take focus (a heading\n // without `tabIndex`, a disabled control) leaves focus where it was.\n const initial = initialFocusRef?.current;\n const inTrap = initial && inside(container, initial) ? initial : null;\n inTrap?.focus();\n if (!inTrap || !inside(container, focusedElement(doc))) {\n focusTabStop(container);\n }\n\n const releaseTab = trapTab(() => container, container);\n return () => {\n releaseTab();\n let target = restoreFocus ? opener : null;\n // `null` is an object too, and a caller without types can pass it.\n if (restoreFocus && typeof restoreFocus === 'object') {\n // Read now rather than when the trap turned on: a trigger that\n // re-mounted in between has a new node.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n target = restoreFocus.current;\n }\n // Hand focus back only while the trap still holds it: inside the\n // container, or dropped to <body> because the container left the page.\n const now = focusedElement(doc);\n if (target && (now === doc.body || inside(container, now))) {\n target.focus();\n }\n };\n }, [\n active,\n hydrated,\n containerRef,\n initialFocusRef,\n restoreFocus,\n refChanges,\n ]);\n\n // Nothing says when a ref is set, so after every render, a ref that no\n // longer matches what the trap holds (a container that mounted, left or was\n // replaced) runs it again. Effects run in order, so whenever the effect\n // above ran in this commit, it read the ref just before this does, and a\n // mismatch means the ref changed in a render it didn't run for. The render\n // a bump asks for runs it, so the two match and the trap settles. An empty\n // ref matches a trap holding nothing, so a container that never mounts\n // costs no render.\n // eslint-disable-next-line react-hooks/exhaustive-deps -- after every render on purpose\n useEffect(() => {\n if (active && hydrated && containerRef.current !== heldRef.current) {\n setRefChanges(n => n + 1);\n }\n });\n}\n"],"names":["isShown"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useFocusTrap.js","sources":["../../../src/helpers/useFocusTrap.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport type { RefObject } from 'react';\nimport { useIsHydrated } from './useIsHydrated';\nimport { getDeepestActiveElement } from './shadowDom';\n\n/**\n * Whether `el` is editable while its parent is not, which makes it an editing\n * host: the one tab stop an editable region gets. `contenteditable=\"false\"`\n * turns editing off, and a missing or unknown value inherits the parent's.\n */\nfunction isEditingHost(el: Element): boolean {\n const editable = (node: Element): boolean | null => {\n const value = node.getAttribute('contenteditable')?.toLowerCase();\n if (value === undefined) return null;\n if (value === '' || value === 'true' || value === 'plaintext-only') {\n return true;\n }\n return value === 'false' ? false : null;\n };\n if (editable(el) !== true) return false;\n for (let node = el.parentElement; node; node = node.parentElement) {\n const state = editable(node);\n if (state !== null) return !state;\n }\n return true;\n}\n\n/** Whether `el` is the summary of its `<details>`: the first one, not a stray. */\nconst isDetailsSummary = (el: Element): boolean =>\n el.parentElement?.localName === 'details' &&\n Array.from(el.parentElement.children).find(\n child => child.localName === 'summary'\n ) === el;\n\n/**\n * Whether the browser puts `el` in the tab order, before anything that hides\n * or disables it. The `tabIndex` property can't answer this on its own: it\n * can read `-1` for an editable region or an `<embed>` that Tab still visits,\n * and `0` for a `<video>` without controls that it doesn't. So a valid\n * `tabindex` attribute decides, and otherwise the element's kind does.\n * Internal like `focusTabStop`: `src/index.ts` names what it takes from here.\n */\nexport function isTabbableKind(el: Element): boolean {\n const tabindex = el.getAttribute('tabindex');\n if (tabindex !== null && /^\\s*[+-]?\\d/.test(tabindex)) {\n return (el as HTMLElement).tabIndex >= 0;\n }\n switch (el.localName) {\n case 'a':\n case 'area':\n return el.hasAttribute('href');\n case 'input':\n return (el as HTMLInputElement).type !== 'hidden';\n case 'button':\n case 'select':\n case 'textarea':\n case 'iframe':\n case 'embed':\n case 'object':\n return true;\n case 'audio':\n case 'video':\n return el.hasAttribute('controls');\n case 'summary':\n return isDetailsSummary(el);\n default:\n return isEditingHost(el);\n }\n}\n\n/**\n * The node's parent in the flattened tree, the one the browser renders and\n * tabs through: the slot it is assigned to, else its parent, else the host of\n * the shadow root it tops, and nothing above the document. A closed shadow\n * root hides its slots (`assignedSlot` reads `null` under one), so a child of\n * its host goes straight to the host, whether a slot takes it or not.\n */\nconst flatParent = (node: Node): Node | null =>\n (node as Element).assignedSlot ??\n node.parentElement ??\n (node.parentNode as ShadowRoot | null)?.host ??\n null;\n\n/**\n * The element's children in the flattened tree, in the order they render: a\n * host's shadow root content, a slot's assigned elements (or its fallback\n * content when nothing is assigned), anything else's own children. A host\n * whose shadow root is closed has no root to read, so its own children stand\n * in, including any that no slot takes and that aren't rendered.\n */\nfunction flatChildren(el: Element): Element[] {\n if (el.shadowRoot) return Array.from(el.shadowRoot.children);\n if (el.localName === 'slot') {\n const assigned = (el as HTMLSlotElement).assignedNodes();\n if (assigned.length > 0) {\n return assigned.filter(\n (node): node is Element => node.nodeType === Node.ELEMENT_NODE\n );\n }\n }\n return Array.from(el.children);\n}\n\nconst isShown = (el: Element): boolean =>\n !el.hasAttribute('inert') && getComputedStyle(el).display !== 'none';\n\nconst isVisible = (el: Element): boolean => {\n const { visibility } = getComputedStyle(el);\n return visibility !== 'hidden' && visibility !== 'collapse';\n};\n\n/**\n * Whether nothing from `el` up to `top` hides it: `inert`, `display: none`\n * (not inherited, so each element is checked) or a closed `<details>` it\n * isn't the summary of. `top` itself isn't checked, and `null` walks to the\n * top of the page.\n */\nfunction isShownUpTo(el: Element, top: Node | null): boolean {\n let below: Element | null = null;\n for (\n let node: Element | null = el;\n node && node !== top;\n node = flatParent(node) as Element | null\n ) {\n if (!isShown(node)) return false;\n if (\n below &&\n node.localName === 'details' &&\n !node.hasAttribute('open') &&\n !isDetailsSummary(below)\n ) {\n return false;\n }\n below = node;\n }\n return true;\n}\n\n/**\n * The images that use the image map `area` belongs to. An image names its\n * map with `usemap=\"#…\"`, matching the map's `name` or `id`.\n */\nfunction imagesUsingMapOf(area: Element): Element[] {\n const map = area.closest('map');\n if (!map) return [];\n const names = [map.getAttribute('name'), map.id].filter(name => name);\n return Array.from(\n (area.getRootNode() as ParentNode).querySelectorAll('img[usemap]')\n ).filter(img => {\n const ref = img.getAttribute('usemap') as string;\n return ref.startsWith('#') && names.includes(ref.slice(1));\n });\n}\n\n/**\n * Whether an image map area is shown. An area has no box of its own: the\n * HTML rendering rules give it `display: none`, though a browser may not\n * report that. So it counts where an image using its map is shown, as long\n * as nothing makes it inert.\n */\nfunction isAreaShown(area: Element, container: Element): boolean {\n for (\n let node: Element | null = area;\n node && node !== container;\n node = flatParent(node) as Element | null\n ) {\n if (node.hasAttribute('inert')) return false;\n }\n return imagesUsingMapOf(area).some(\n img => isShownUpTo(img, null) && isVisible(img)\n );\n}\n\n/**\n * Whether Tab can land on `el`, one of the kinds it visits. A disabled control\n * (including one inside a disabled `<fieldset>`), an inert subtree and\n * anything not rendered are skipped. `visibility` is inherited, so the\n * element's own computed value covers its ancestors.\n */\nfunction isRenderedAndEnabled(el: Element, container: Element): boolean {\n if (el.matches(':disabled')) return false;\n if (el.localName === 'area') return isAreaShown(el, container);\n return isShownUpTo(el, container) && isVisible(el);\n}\n\n/** A radio button with a name, which shares one tab stop with its group. */\nconst isGroupedRadio = (el: Element): el is HTMLInputElement =>\n el.localName === 'input' &&\n (el as HTMLInputElement).type === 'radio' &&\n (el as HTMLInputElement).name !== '';\n\n/** Whether two radio buttons are in one group: same name, form owner and tree. */\nconst sameGroup = (a: HTMLInputElement, b: Element): b is HTMLInputElement =>\n isGroupedRadio(b) &&\n b.name === a.name &&\n b.form === a.form &&\n b.getRootNode() === a.getRootNode();\n\n/**\n * The checked button of `radio`'s group. The group isn't bounded by the trap,\n * so this looks through the radio's whole tree.\n */\nconst checkedInGroup = (radio: HTMLInputElement): Element | undefined =>\n Array.from(\n (radio.getRootNode() as ParentNode).querySelectorAll('input:checked')\n ).find(el => sameGroup(radio, el));\n\n/** The flattened-tree ancestors of `node` below the container, outermost first, then `node`. */\nfunction flatChain(node: Node, container: Node): Node[] {\n const chain: Node[] = [];\n for (let n: Node | null = node; n && n !== container; n = flatParent(n)) {\n chain.unshift(n);\n }\n return chain;\n}\n\n/**\n * Whether `node` is inside the container in the flattened tree: in it, in a\n * shadow root it holds, or slotted into it. A child of a closed-root host\n * counts as inside wherever the host is, as its slot can't be seen.\n */\nfunction inside(container: Node, node: Node): boolean {\n for (let n: Node | null = node; n; n = flatParent(n)) {\n if (n === container) return true;\n }\n return false;\n}\n\n/**\n * `true` when Tab reaches `b` after `a`, which is their order in the\n * flattened tree: a shadow host comes before its shadow root's content, and\n * slotted content comes where its slot is. A closed root's slots can't be\n * seen, so its host's children keep their place in the light DOM. Two\n * flattened-tree siblings are always in one DOM tree, so their document\n * position decides between them.\n */\nfunction follows(container: Element, a: Node, b: Node): boolean {\n const chainA = flatChain(a, container);\n const chainB = flatChain(b, container);\n for (let i = 0; i < Math.max(chainA.length, chainB.length); i++) {\n const [x, y] = [chainA[i], chainB[i]];\n if (x === y) continue;\n if (!x) return true;\n if (!y) return false;\n return Boolean(\n x.compareDocumentPosition(y) & Node.DOCUMENT_POSITION_FOLLOWING\n );\n }\n return false;\n}\n\n/**\n * Every element of a tabbable kind under `el` in the flattened tree, depth\n * first, which is the order Tab visits them. Under a host whose shadow root\n * is closed, that takes in the host's own children instead, rendered or not.\n */\nfunction collectCandidates(\n el: Element,\n out: HTMLElement[] = []\n): HTMLElement[] {\n for (const child of flatChildren(el)) {\n if (isTabbableKind(child)) out.push(child as HTMLElement);\n collectCandidates(child, out);\n }\n return out;\n}\n\ninterface TabStops {\n first: HTMLElement;\n last: HTMLElement;\n /** The stop that stands for `node`: its group's stop for a radio button. */\n stopFor(node: Element): Element;\n /** The stops from one end inward, the last first when `fromLast`. */\n from(fromLast: boolean): Generator<HTMLElement>;\n}\n\n/**\n * The first and last tab stops inside `container`, or `null` when there are\n * none. Only the ends matter for wrapping, so the style checks run from each\n * end until they find a stop rather than over every candidate.\n *\n * A radio group is one stop, the way the browser treats it: its checked\n * button, or its first when none is checked. Counting every button would make\n * the group's last one an end, and Tab from the checked one would leave. The\n * group goes by name, form owner and tree, not by the trap, so a checked\n * button outside the container that Tab can land on leaves the group no stop\n * inside. One that Tab can't land on leaves the group as if none were checked.\n */\nfunction findTabStops(container: HTMLElement): TabStops | null {\n const candidates = collectCandidates(container);\n const checked = new Map<Element, boolean>();\n const usable = (el: Element): boolean => {\n let ok = checked.get(el);\n if (ok === undefined) {\n ok = isRenderedAndEnabled(el, container);\n checked.set(el, ok);\n }\n return ok;\n };\n // Resolved once per group, filed under each of its buttons, since finding\n // the checked one scans the radio's whole tree.\n const groupStops = new Map<Element, HTMLElement | undefined>();\n const groupStop = (radio: HTMLInputElement): HTMLElement | undefined => {\n if (groupStops.has(radio)) return groupStops.get(radio);\n const group = candidates.filter(el => sameGroup(radio, el));\n const on = checkedInGroup(radio);\n const stop =\n on && isTabbableKind(on) && usable(on)\n ? group.find(el => el === on)\n : group.find(usable);\n for (const el of [radio, ...group]) groupStops.set(el, stop);\n return stop;\n };\n const isStop = (el: HTMLElement): boolean =>\n usable(el) && (!isGroupedRadio(el) || groupStop(el) === el);\n // Checked only as they're reached, and the checks are kept, so finding\n // both ends costs what it did when only the ends were looked for.\n function* from(fromLast: boolean): Generator<HTMLElement> {\n for (let i = 0; i < candidates.length; i++) {\n const el = candidates[fromLast ? candidates.length - 1 - i : i];\n if (isStop(el)) yield el;\n }\n }\n\n const first = from(false).next().value;\n if (!first) return null;\n return {\n first,\n last: from(true).next().value as HTMLElement,\n stopFor: node => (isGroupedRadio(node) && groupStop(node)) || node,\n from,\n };\n}\n\n/**\n * The element that has focus in the container's own document (an iframe's,\n * when it renders into one), inside open shadow roots too. Focus inside a\n * closed one reads as its host, which the comparisons above place like any\n * other element. Browsers fall back to <body> when nothing has\n * focus. jsdom, where consumers run their tests, reports nothing once focus\n * was inside a shadow root that was removed, so this falls back for it.\n */\nconst focusedElement = (doc: Document): Element =>\n getDeepestActiveElement(doc) ?? doc.body;\n\n/**\n * Moves focus to the first tab stop inside `container`, or the last when\n * `fromLast`, and to the container when there is none. Every move a trap\n * makes goes through here: into the container as it opens, to the other end\n * on a wrap, and back in after a Tab the browser carried out.\n *\n * A stop that doesn't take focus is passed over for the next one in. An\n * `<object>` or `<embed>` with nothing to show is one: it counts as a stop by\n * its kind, but browsers refuse to focus it, and which ones they refuse\n * depends on the browser and on what has loaded, so only trying tells. A\n * stop took focus when focus moved, wherever it went (a stop can hand focus\n * on), or when focus was on it already. `src/index.ts` names its exports from\n * this module, so this one stays internal.\n */\nexport function focusTabStop(container: HTMLElement, fromLast = false): void {\n const doc = container.ownerDocument;\n for (const stop of findTabStops(container)?.from(fromLast) ?? []) {\n const before = focusedElement(doc);\n stop.focus();\n const now = focusedElement(doc);\n if (now !== before || inside(stop, now)) return;\n }\n container.focus();\n}\n\n/**\n * Handles a Tab for a trap on `container`. When the browser has no tab stop\n * left in that direction inside the container, focus goes to the stop at the\n * other end and the key is marked used, and with no stops at all the\n * container takes focus. Focus outside the container goes in at that end\n * too. Returns whether it moved focus; otherwise the browser does.\n */\nfunction wrapTab(e: KeyboardEvent, container: HTMLElement): boolean {\n const stops = findTabStops(container);\n if (!stops) {\n e.preventDefault();\n container.focus();\n return true;\n }\n const focused = focusedElement(container.ownerDocument);\n const current = stops.stopFor(focused);\n // Wrap whenever the browser has no stop left in that direction inside the\n // container. Comparing positions rather than testing `=== first` also\n // covers focus resting on the container or on an element outside the tab\n // order, which the browser would otherwise walk straight out of.\n if (\n !inside(container, focused) ||\n (e.shiftKey\n ? !follows(container, stops.first, current)\n : !follows(container, current, stops.last))\n ) {\n e.preventDefault();\n focusTabStop(container, e.shiftKey);\n return true;\n }\n return false;\n}\n\n/**\n * Holds Tab inside the element `container` returns until the returned\n * function is called. Tab and Shift+Tab heard on `keyTarget` wrap at the\n * ends, unless something already marked the key used, and if a Tab the trap\n * let through still takes focus out, it sends focus back to the other end.\n *\n * `container` is read each time a key or a focus change arrives, so an\n * overlay whose nodes are remounted while it's open, as moving to another\n * portal target does, stays trapped without attaching again. `holds` is asked\n * on every Tab, and the trap leaves the key alone while it says no. Internal\n * like `focusTabStop`. It's for an overlay that holds Tab from a listener of\n * its own on `window` and asks the overlay stack first, so it stands aside\n * while another overlay is open over it, which a trap listening on the\n * container can't do.\n */\nexport function trapTab(\n container: () => HTMLElement | null,\n keyTarget: HTMLElement | Window,\n holds: () => boolean = () => true\n): () => void {\n // The document focus moves in: the window's own, or the element's.\n const doc =\n 'document' in keyTarget ? keyTarget.document : keyTarget.ownerDocument;\n // A window and an element type their listeners differently, so the trap\n // listens through the base type both share.\n const keys: EventTarget = keyTarget;\n // The end focus goes back to if a Tab the trap let the browser handle\n // takes it out (the last for Shift+Tab), held until that Tab has finished\n // moving focus, and `null` otherwise.\n let wrapFromLast: boolean | null = null;\n let wrapTimer: ReturnType<typeof setTimeout> | undefined;\n\n // `keyTarget` is any target, so the listener takes the base event type.\n const handleKeyDown = (event: Event) => {\n const e = event as KeyboardEvent;\n const box = container();\n // A trap nested inside this one has already wrapped focus.\n if (e.key !== 'Tab' || e.defaultPrevented || !box || !holds()) return;\n if (wrapTab(e, box)) return;\n wrapFromLast = e.shiftKey;\n clearTimeout(wrapTimer);\n wrapTimer = setTimeout(() => {\n wrapFromLast = null;\n });\n };\n\n // The browser moves focus as soon as the keydown is over, so focus that\n // lands outside while `wrapFromLast` is set got there by that Tab.\n const handleFocusIn = (e: FocusEvent) => {\n const box = container();\n if (wrapFromLast === null || !box) return;\n if (inside(box, e.composedPath()[0] as Node)) return;\n const fromLast = wrapFromLast;\n wrapFromLast = null;\n focusTabStop(box, fromLast);\n };\n\n keys.addEventListener('keydown', handleKeyDown);\n doc.addEventListener('focusin', handleFocusIn, true);\n return () => {\n keys.removeEventListener('keydown', handleKeyDown);\n doc.removeEventListener('focusin', handleFocusIn, true);\n clearTimeout(wrapTimer);\n };\n}\n\n/**\n * Options for `useFocusTrap`.\n */\nexport interface UseFocusTrapOptions {\n /**\n * Whether the trap is on. Turning it on moves focus into the container;\n * turning it off (or unmounting) releases Tab and restores focus.\n * @defaultValue true\n */\n active?: boolean;\n /**\n * The element inside the container to focus when the trap turns on. It has\n * to be one that can take focus, so a heading needs `tabIndex={-1}`. When\n * focusing it doesn't put focus inside the container (the ref is empty, or\n * its element is outside, disabled, hidden or can't take focus), focus goes\n * to the first tab stop inside the container that takes focus, and to the\n * container itself when none does.\n */\n initialFocusRef?: RefObject<HTMLElement | null>;\n /**\n * Where focus goes when the trap turns off. `true` returns it to the element\n * that had focus when the trap turned on; a ref sends it to that element\n * instead; `false` leaves it alone. Focus is only moved while the trap still\n * holds it, so a click that already put focus somewhere else outside the\n * container keeps it there.\n *\n * `true` reads focus after the container's content has mounted, so content\n * that focuses itself as it mounts (an `autoFocus` input) hides the element\n * that opened it. And some browsers don't focus a button when it is\n * clicked. For a panel opened from a button, pass the button's ref.\n * @defaultValue true\n */\n restoreFocus?: boolean | RefObject<HTMLElement | null>;\n}\n\n/**\n * Keeps keyboard focus inside a container while it is active: focus moves in\n * when the trap turns on, Tab and Shift+Tab wrap between the first and last\n * tab stops, and focus goes back where it came from when the trap turns off.\n *\n * Tab stops are what the browser's Tab visits, in document order: links,\n * form controls, frames, media with controls, a `<details>` summary, editable\n * regions, anything with `tabIndex={0}`, and the content of open shadow\n * roots. Hidden, disabled, inert and `tabIndex={-1}` elements are skipped,\n * and a radio group counts once. Give the container `tabIndex={-1}` so it can\n * hold focus itself when nothing inside can. The trap listens on the\n * container, so a trap nested inside another handles Tab first and the outer\n * one stands aside.\n *\n * The trap is about Tab. If a Tab it let through still takes focus out (a\n * browser can leave out a stop the page has, such as links when its settings\n * say to), it sends focus back to the other end. A pointer or a script moving\n * focus out is left alone, because pulling focus back from a click is modal\n * behaviour, and a modal also has to block the page behind it, which `Modal`\n * does. Content the container renders through a portal lives elsewhere in\n * the DOM, so it is outside the trap: render nested overlays inside the\n * container, or give them a trap of their own. To trap content a `Portal`\n * renders, pass a ref to the element inside the `Portal` as `containerRef`.\n *\n * The trap looks at `containerRef` again each time the component calling it\n * renders. A container that mounts after the trap turns on, such as a panel\n * waiting for a `Portal` target held in state, has it attach once that\n * component renders with the container in the DOM. When the container leaves\n * or is replaced, the trap lets go of it as it would turning off, handing\n * focus back, and attaches to the next one.\n * A child that mounts the container in a render of its own (from its own\n * state, or as a `Suspense` boundary resolves) goes unseen until the\n * component calling the hook renders again. The trap waits for hydration, so\n * that component renders again when the page hydrates, and portaled content,\n * which first appears then, is found.\n *\n * @function useFocusTrap\n * @param containerRef - Ref to the element focus stays inside.\n * @param options - When the trap is on, and where focus starts and ends.\n *\n * @example\n * const panelRef = useRef<HTMLDivElement>(null);\n * useFocusTrap(panelRef, { active: isOpen });\n * return isOpen ? (\n * <div ref={panelRef} role=\"dialog\" aria-label=\"Filters\" tabIndex={-1}>\n * …\n * </div>\n * ) : null;\n */\nexport function useFocusTrap(\n containerRef: RefObject<HTMLElement | null>,\n options: UseFocusTrapOptions = {}\n): void {\n const { active = true, initialFocusRef, restoreFocus = true } = options;\n // Portaled content has no server-rendered counterpart, so a portaled\n // container only exists from the commit after hydration.\n const hydrated = useIsHydrated();\n // The container the trap is attached to, or `null` while it has none.\n const heldRef = useRef<HTMLElement | null>(null);\n // Bumped to run the trap again when the container in the ref changes.\n const [refChanges, setRefChanges] = useState(0);\n\n useEffect(() => {\n heldRef.current = null;\n if (!active || !hydrated) return undefined;\n const container = containerRef.current;\n if (!container) return undefined;\n heldRef.current = container;\n const doc = container.ownerDocument;\n\n const opener = focusedElement(doc) as HTMLElement;\n\n // `initialFocusRef` counts only when focusing it puts focus inside. Focus\n // put outside would stay out, as Tab there never reaches the container's\n // listener, and focusing something that can't take focus (a heading\n // without `tabIndex`, a disabled control) leaves focus where it was.\n const initial = initialFocusRef?.current;\n const inTrap = initial && inside(container, initial) ? initial : null;\n inTrap?.focus();\n if (!inTrap || !inside(container, focusedElement(doc))) {\n focusTabStop(container);\n }\n\n const releaseTab = trapTab(() => container, container);\n return () => {\n releaseTab();\n let target = restoreFocus ? opener : null;\n // `null` is an object too, and a caller without types can pass it.\n if (restoreFocus && typeof restoreFocus === 'object') {\n // Read now rather than when the trap turned on: a trigger that\n // re-mounted in between has a new node.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n target = restoreFocus.current;\n }\n // Hand focus back only while the trap still holds it: inside the\n // container, or dropped to <body> because the container left the page.\n const now = focusedElement(doc);\n if (target && (now === doc.body || inside(container, now))) {\n target.focus();\n }\n };\n }, [\n active,\n hydrated,\n containerRef,\n initialFocusRef,\n restoreFocus,\n refChanges,\n ]);\n\n // Nothing says when a ref is set, so after every render, a ref that no\n // longer matches what the trap holds (a container that mounted, left or was\n // replaced) runs it again. Effects run in order, so whenever the effect\n // above ran in this commit, it read the ref just before this does, and a\n // mismatch means the ref changed in a render it didn't run for. The render\n // a bump asks for runs it, so the two match and the trap settles. An empty\n // ref matches a trap holding nothing, so a container that never mounts\n // costs no render.\n // eslint-disable-next-line react-hooks/exhaustive-deps -- after every render on purpose\n useEffect(() => {\n if (active && hydrated && containerRef.current !== heldRef.current) {\n setRefChanges(n => n + 1);\n }\n });\n}\n"],"names":["isShown"],"mappings":";;;;;;AAKA;;;;AAIG;AACH,SAAS,aAAa,CAAC,EAAW,EAAA;AAChC,IAAA,MAAM,QAAQ,GAAG,CAAC,IAAa,KAAoB;QACjD,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,iBAAiB,CAAC,EAAE,WAAW,EAAE;QACjE,IAAI,KAAK,KAAK,SAAS;AAAE,YAAA,OAAO,IAAI;AACpC,QAAA,IAAI,KAAK,KAAK,EAAE,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,gBAAgB,EAAE;AAClE,YAAA,OAAO,IAAI;QACb;QACA,OAAO,KAAK,KAAK,OAAO,GAAG,KAAK,GAAG,IAAI;AACzC,IAAA,CAAC;AACD,IAAA,IAAI,QAAQ,CAAC,EAAE,CAAC,KAAK,IAAI;AAAE,QAAA,OAAO,KAAK;AACvC,IAAA,KAAK,IAAI,IAAI,GAAG,EAAE,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC,aAAa,EAAE;AACjE,QAAA,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC;QAC5B,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,CAAC,KAAK;IACnC;AACA,IAAA,OAAO,IAAI;AACb;AAEA;AACA,MAAM,gBAAgB,GAAG,CAAC,EAAW,KACnC,EAAE,CAAC,aAAa,EAAE,SAAS,KAAK,SAAS;IACzC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,IAAI,CACxC,KAAK,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,CACvC,KAAK,EAAE;AAEV;;;;;;;AAOG;AACG,SAAU,cAAc,CAAC,EAAW,EAAA;IACxC,MAAM,QAAQ,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC;IAC5C,IAAI,QAAQ,KAAK,IAAI,IAAI,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;AACrD,QAAA,OAAQ,EAAkB,CAAC,QAAQ,IAAI,CAAC;IAC1C;AACA,IAAA,QAAQ,EAAE,CAAC,SAAS;AAClB,QAAA,KAAK,GAAG;AACR,QAAA,KAAK,MAAM;AACT,YAAA,OAAO,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC;AAChC,QAAA,KAAK,OAAO;AACV,YAAA,OAAQ,EAAuB,CAAC,IAAI,KAAK,QAAQ;AACnD,QAAA,KAAK,QAAQ;AACb,QAAA,KAAK,QAAQ;AACb,QAAA,KAAK,UAAU;AACf,QAAA,KAAK,QAAQ;AACb,QAAA,KAAK,OAAO;AACZ,QAAA,KAAK,QAAQ;AACX,YAAA,OAAO,IAAI;AACb,QAAA,KAAK,OAAO;AACZ,QAAA,KAAK,OAAO;AACV,YAAA,OAAO,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC;AACpC,QAAA,KAAK,SAAS;AACZ,YAAA,OAAO,gBAAgB,CAAC,EAAE,CAAC;AAC7B,QAAA;AACE,YAAA,OAAO,aAAa,CAAC,EAAE,CAAC;;AAE9B;AAEA;;;;;;AAMG;AACH,MAAM,UAAU,GAAG,CAAC,IAAU,KAC3B,IAAgB,CAAC,YAAY;AAC9B,IAAA,IAAI,CAAC,aAAa;IACjB,IAAI,CAAC,UAAgC,EAAE,IAAI;AAC5C,IAAA,IAAI;AAEN;;;;;;AAMG;AACH,SAAS,YAAY,CAAC,EAAW,EAAA;IAC/B,IAAI,EAAE,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;AAC5D,IAAA,IAAI,EAAE,CAAC,SAAS,KAAK,MAAM,EAAE;AAC3B,QAAA,MAAM,QAAQ,GAAI,EAAsB,CAAC,aAAa,EAAE;AACxD,QAAA,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;AACvB,YAAA,OAAO,QAAQ,CAAC,MAAM,CACpB,CAAC,IAAI,KAAsB,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,YAAY,CAC/D;QACH;IACF;IACA,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC;AAChC;AAEA,MAAMA,SAAO,GAAG,CAAC,EAAW,KAC1B,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,gBAAgB,CAAC,EAAE,CAAC,CAAC,OAAO,KAAK,MAAM;AAEtE,MAAM,SAAS,GAAG,CAAC,EAAW,KAAa;IACzC,MAAM,EAAE,UAAU,EAAE,GAAG,gBAAgB,CAAC,EAAE,CAAC;AAC3C,IAAA,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,KAAK,UAAU;AAC7D,CAAC;AAED;;;;;AAKG;AACH,SAAS,WAAW,CAAC,EAAW,EAAE,GAAgB,EAAA;IAChD,IAAI,KAAK,GAAmB,IAAI;AAChC,IAAA,KACE,IAAI,IAAI,GAAmB,EAAE,EAC7B,IAAI,IAAI,IAAI,KAAK,GAAG,EACpB,IAAI,GAAG,UAAU,CAAC,IAAI,CAAmB,EACzC;AACA,QAAA,IAAI,CAACA,SAAO,CAAC,IAAI,CAAC;AAAE,YAAA,OAAO,KAAK;AAChC,QAAA,IACE,KAAK;YACL,IAAI,CAAC,SAAS,KAAK,SAAS;AAC5B,YAAA,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;AAC1B,YAAA,CAAC,gBAAgB,CAAC,KAAK,CAAC,EACxB;AACA,YAAA,OAAO,KAAK;QACd;QACA,KAAK,GAAG,IAAI;IACd;AACA,IAAA,OAAO,IAAI;AACb;AAEA;;;AAGG;AACH,SAAS,gBAAgB,CAAC,IAAa,EAAA;IACrC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;AAC/B,IAAA,IAAI,CAAC,GAAG;AAAE,QAAA,OAAO,EAAE;IACnB,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC;AACrE,IAAA,OAAO,KAAK,CAAC,IAAI,CACd,IAAI,CAAC,WAAW,EAAiB,CAAC,gBAAgB,CAAC,aAAa,CAAC,CACnE,CAAC,MAAM,CAAC,GAAG,IAAG;QACb,MAAM,GAAG,GAAG,GAAG,CAAC,YAAY,CAAC,QAAQ,CAAW;AAChD,QAAA,OAAO,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC5D,IAAA,CAAC,CAAC;AACJ;AAEA;;;;;AAKG;AACH,SAAS,WAAW,CAAC,IAAa,EAAE,SAAkB,EAAA;AACpD,IAAA,KACE,IAAI,IAAI,GAAmB,IAAI,EAC/B,IAAI,IAAI,IAAI,KAAK,SAAS,EAC1B,IAAI,GAAG,UAAU,CAAC,IAAI,CAAmB,EACzC;AACA,QAAA,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC;AAAE,YAAA,OAAO,KAAK;IAC9C;IACA,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAC,IAAI,CAChC,GAAG,IAAI,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,SAAS,CAAC,GAAG,CAAC,CAChD;AACH;AAEA;;;;;AAKG;AACH,SAAS,oBAAoB,CAAC,EAAW,EAAE,SAAkB,EAAA;AAC3D,IAAA,IAAI,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC;AAAE,QAAA,OAAO,KAAK;AACzC,IAAA,IAAI,EAAE,CAAC,SAAS,KAAK,MAAM;AAAE,QAAA,OAAO,WAAW,CAAC,EAAE,EAAE,SAAS,CAAC;IAC9D,OAAO,WAAW,CAAC,EAAE,EAAE,SAAS,CAAC,IAAI,SAAS,CAAC,EAAE,CAAC;AACpD;AAEA;AACA,MAAM,cAAc,GAAG,CAAC,EAAW,KACjC,EAAE,CAAC,SAAS,KAAK,OAAO;IACvB,EAAuB,CAAC,IAAI,KAAK,OAAO;AACxC,IAAA,EAAuB,CAAC,IAAI,KAAK,EAAE;AAEtC;AACA,MAAM,SAAS,GAAG,CAAC,CAAmB,EAAE,CAAU,KAChD,cAAc,CAAC,CAAC,CAAC;AACjB,IAAA,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI;AACjB,IAAA,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI;IACjB,CAAC,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,WAAW,EAAE;AAErC;;;AAGG;AACH,MAAM,cAAc,GAAG,CAAC,KAAuB,KAC7C,KAAK,CAAC,IAAI,CACP,KAAK,CAAC,WAAW,EAAiB,CAAC,gBAAgB,CAAC,eAAe,CAAC,CACtE,CAAC,IAAI,CAAC,EAAE,IAAI,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAEpC;AACA,SAAS,SAAS,CAAC,IAAU,EAAE,SAAe,EAAA;IAC5C,MAAM,KAAK,GAAW,EAAE;AACxB,IAAA,KAAK,IAAI,CAAC,GAAgB,IAAI,EAAE,CAAC,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,EAAE;AACvE,QAAA,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;IAClB;AACA,IAAA,OAAO,KAAK;AACd;AAEA;;;;AAIG;AACH,SAAS,MAAM,CAAC,SAAe,EAAE,IAAU,EAAA;AACzC,IAAA,KAAK,IAAI,CAAC,GAAgB,IAAI,EAAE,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,EAAE;QACpD,IAAI,CAAC,KAAK,SAAS;AAAE,YAAA,OAAO,IAAI;IAClC;AACA,IAAA,OAAO,KAAK;AACd;AAEA;;;;;;;AAOG;AACH,SAAS,OAAO,CAAC,SAAkB,EAAE,CAAO,EAAE,CAAO,EAAA;IACnD,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,EAAE,SAAS,CAAC;IACtC,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,EAAE,SAAS,CAAC;IACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE;AAC/D,QAAA,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;QACrC,IAAI,CAAC,KAAK,CAAC;YAAE;AACb,QAAA,IAAI,CAAC,CAAC;AAAE,YAAA,OAAO,IAAI;AACnB,QAAA,IAAI,CAAC,CAAC;AAAE,YAAA,OAAO,KAAK;AACpB,QAAA,OAAO,OAAO,CACZ,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,2BAA2B,CAChE;IACH;AACA,IAAA,OAAO,KAAK;AACd;AAEA;;;;AAIG;AACH,SAAS,iBAAiB,CACxB,EAAW,EACX,MAAqB,EAAE,EAAA;IAEvB,KAAK,MAAM,KAAK,IAAI,YAAY,CAAC,EAAE,CAAC,EAAE;QACpC,IAAI,cAAc,CAAC,KAAK,CAAC;AAAE,YAAA,GAAG,CAAC,IAAI,CAAC,KAAoB,CAAC;AACzD,QAAA,iBAAiB,CAAC,KAAK,EAAE,GAAG,CAAC;IAC/B;AACA,IAAA,OAAO,GAAG;AACZ;AAWA;;;;;;;;;;;AAWG;AACH,SAAS,YAAY,CAAC,SAAsB,EAAA;AAC1C,IAAA,MAAM,UAAU,GAAG,iBAAiB,CAAC,SAAS,CAAC;AAC/C,IAAA,MAAM,OAAO,GAAG,IAAI,GAAG,EAAoB;AAC3C,IAAA,MAAM,MAAM,GAAG,CAAC,EAAW,KAAa;QACtC,IAAI,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;AACxB,QAAA,IAAI,EAAE,KAAK,SAAS,EAAE;AACpB,YAAA,EAAE,GAAG,oBAAoB,CAAC,EAAE,EAAE,SAAS,CAAC;AACxC,YAAA,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC;QACrB;AACA,QAAA,OAAO,EAAE;AACX,IAAA,CAAC;;;AAGD,IAAA,MAAM,UAAU,GAAG,IAAI,GAAG,EAAoC;AAC9D,IAAA,MAAM,SAAS,GAAG,CAAC,KAAuB,KAA6B;AACrE,QAAA,IAAI,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC;AAAE,YAAA,OAAO,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC;AACvD,QAAA,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,EAAE,IAAI,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC3D,QAAA,MAAM,EAAE,GAAG,cAAc,CAAC,KAAK,CAAC;AAChC,QAAA,MAAM,IAAI,GACR,EAAE,IAAI,cAAc,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC,EAAE;AACnC,cAAE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE;AAC5B,cAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC;QACxB,KAAK,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC;AAAE,YAAA,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC;AAC5D,QAAA,OAAO,IAAI;AACb,IAAA,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,EAAe,KAC7B,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC,IAAI,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;;;AAG7D,IAAA,UAAU,IAAI,CAAC,QAAiB,EAAA;AAC9B,QAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YAC1C,MAAM,EAAE,GAAG,UAAU,CAAC,QAAQ,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC/D,IAAI,MAAM,CAAC,EAAE,CAAC;AAAE,gBAAA,MAAM,EAAE;QAC1B;IACF;IAEA,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK;AACtC,IAAA,IAAI,CAAC,KAAK;AAAE,QAAA,OAAO,IAAI;IACvB,OAAO;QACL,KAAK;QACL,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,KAAoB;AAC5C,QAAA,OAAO,EAAE,IAAI,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,IAAI;QAClE,IAAI;KACL;AACH;AAEA;;;;;;;AAOG;AACH,MAAM,cAAc,GAAG,CAAC,GAAa,KACnC,uBAAuB,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,IAAI;AAE1C;;;;;;;;;;;;;AAaG;SACa,YAAY,CAAC,SAAsB,EAAE,QAAQ,GAAG,KAAK,EAAA;AACnE,IAAA,MAAM,GAAG,GAAG,SAAS,CAAC,aAAa;AACnC,IAAA,KAAK,MAAM,IAAI,IAAI,YAAY,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE;AAChE,QAAA,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC;QAClC,IAAI,CAAC,KAAK,EAAE;AACZ,QAAA,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC;QAC/B,IAAI,GAAG,KAAK,MAAM,IAAI,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC;YAAE;IAC3C;IACA,SAAS,CAAC,KAAK,EAAE;AACnB;AAEA;;;;;;AAMG;AACH,SAAS,OAAO,CAAC,CAAgB,EAAE,SAAsB,EAAA;AACvD,IAAA,MAAM,KAAK,GAAG,YAAY,CAAC,SAAS,CAAC;IACrC,IAAI,CAAC,KAAK,EAAE;QACV,CAAC,CAAC,cAAc,EAAE;QAClB,SAAS,CAAC,KAAK,EAAE;AACjB,QAAA,OAAO,IAAI;IACb;IACA,MAAM,OAAO,GAAG,cAAc,CAAC,SAAS,CAAC,aAAa,CAAC;IACvD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;;;;;AAKtC,IAAA,IACE,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC;SAC1B,CAAC,CAAC;cACC,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,KAAK,EAAE,OAAO;AAC1C,cAAE,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,EAC7C;QACA,CAAC,CAAC,cAAc,EAAE;AAClB,QAAA,YAAY,CAAC,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC;AACnC,QAAA,OAAO,IAAI;IACb;AACA,IAAA,OAAO,KAAK;AACd;AAEA;;;;;;;;;;;;;;AAcG;AACG,SAAU,OAAO,CACrB,SAAmC,EACnC,SAA+B,EAC/B,KAAA,GAAuB,MAAM,IAAI,EAAA;;AAGjC,IAAA,MAAM,GAAG,GACP,UAAU,IAAI,SAAS,GAAG,SAAS,CAAC,QAAQ,GAAG,SAAS,CAAC,aAAa;;;IAGxE,MAAM,IAAI,GAAgB,SAAS;;;;IAInC,IAAI,YAAY,GAAmB,IAAI;AACvC,IAAA,IAAI,SAAoD;;AAGxD,IAAA,MAAM,aAAa,GAAG,CAAC,KAAY,KAAI;QACrC,MAAM,CAAC,GAAG,KAAsB;AAChC,QAAA,MAAM,GAAG,GAAG,SAAS,EAAE;;AAEvB,QAAA,IAAI,CAAC,CAAC,GAAG,KAAK,KAAK,IAAI,CAAC,CAAC,gBAAgB,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE;YAAE;AAC/D,QAAA,IAAI,OAAO,CAAC,CAAC,EAAE,GAAG,CAAC;YAAE;AACrB,QAAA,YAAY,GAAG,CAAC,CAAC,QAAQ;QACzB,YAAY,CAAC,SAAS,CAAC;AACvB,QAAA,SAAS,GAAG,UAAU,CAAC,MAAK;YAC1B,YAAY,GAAG,IAAI;AACrB,QAAA,CAAC,CAAC;AACJ,IAAA,CAAC;;;AAID,IAAA,MAAM,aAAa,GAAG,CAAC,CAAa,KAAI;AACtC,QAAA,MAAM,GAAG,GAAG,SAAS,EAAE;AACvB,QAAA,IAAI,YAAY,KAAK,IAAI,IAAI,CAAC,GAAG;YAAE;QACnC,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAS,CAAC;YAAE;QAC9C,MAAM,QAAQ,GAAG,YAAY;QAC7B,YAAY,GAAG,IAAI;AACnB,QAAA,YAAY,CAAC,GAAG,EAAE,QAAQ,CAAC;AAC7B,IAAA,CAAC;AAED,IAAA,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE,aAAa,CAAC;IAC/C,GAAG,CAAC,gBAAgB,CAAC,SAAS,EAAE,aAAa,EAAE,IAAI,CAAC;AACpD,IAAA,OAAO,MAAK;AACV,QAAA,IAAI,CAAC,mBAAmB,CAAC,SAAS,EAAE,aAAa,CAAC;QAClD,GAAG,CAAC,mBAAmB,CAAC,SAAS,EAAE,aAAa,EAAE,IAAI,CAAC;QACvD,YAAY,CAAC,SAAS,CAAC;AACzB,IAAA,CAAC;AACH;AAqCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDG;SACa,YAAY,CAC1B,YAA2C,EAC3C,UAA+B,EAAE,EAAA;AAEjC,IAAA,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,eAAe,EAAE,YAAY,GAAG,IAAI,EAAE,GAAG,OAAO;;;AAGvE,IAAA,MAAM,QAAQ,GAAG,aAAa,EAAE;;AAEhC,IAAA,MAAM,OAAO,GAAG,MAAM,CAAqB,IAAI,CAAC;;IAEhD,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;IAE/C,SAAS,CAAC,MAAK;AACb,QAAA,OAAO,CAAC,OAAO,GAAG,IAAI;AACtB,QAAA,IAAI,CAAC,MAAM,IAAI,CAAC,QAAQ;AAAE,YAAA,OAAO,SAAS;AAC1C,QAAA,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO;AACtC,QAAA,IAAI,CAAC,SAAS;AAAE,YAAA,OAAO,SAAS;AAChC,QAAA,OAAO,CAAC,OAAO,GAAG,SAAS;AAC3B,QAAA,MAAM,GAAG,GAAG,SAAS,CAAC,aAAa;AAEnC,QAAA,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAgB;;;;;AAMjD,QAAA,MAAM,OAAO,GAAG,eAAe,EAAE,OAAO;AACxC,QAAA,MAAM,MAAM,GAAG,OAAO,IAAI,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,OAAO,GAAG,IAAI;QACrE,MAAM,EAAE,KAAK,EAAE;AACf,QAAA,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,EAAE;YACtD,YAAY,CAAC,SAAS,CAAC;QACzB;QAEA,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,SAAS,EAAE,SAAS,CAAC;AACtD,QAAA,OAAO,MAAK;AACV,YAAA,UAAU,EAAE;YACZ,IAAI,MAAM,GAAG,YAAY,GAAG,MAAM,GAAG,IAAI;;AAEzC,YAAA,IAAI,YAAY,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE;;;;AAIpD,gBAAA,MAAM,GAAG,YAAY,CAAC,OAAO;YAC/B;;;AAGA,YAAA,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC;AAC/B,YAAA,IAAI,MAAM,KAAK,GAAG,KAAK,GAAG,CAAC,IAAI,IAAI,MAAM,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,EAAE;gBAC1D,MAAM,CAAC,KAAK,EAAE;YAChB;AACF,QAAA,CAAC;AACH,IAAA,CAAC,EAAE;QACD,MAAM;QACN,QAAQ;QACR,YAAY;QACZ,eAAe;QACf,YAAY;QACZ,UAAU;AACX,KAAA,CAAC;;;;;;;;;;IAWF,SAAS,CAAC,MAAK;AACb,QAAA,IAAI,MAAM,IAAI,QAAQ,IAAI,YAAY,CAAC,OAAO,KAAK,OAAO,CAAC,OAAO,EAAE;YAClE,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC3B;AACF,IAAA,CAAC,CAAC;AACJ;;"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
|
+
'use client';
|
|
2
3
|
import { useRef, useCallback, useMemo } from 'react';
|
|
3
4
|
import { useClientLayoutEffect } from './useClientLayoutEffect.js';
|
|
4
5
|
import { isEventInside, getDeepestActiveElement } from './shadowDom.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useHoverOpen.js","sources":["../../../src/helpers/useHoverOpen.ts"],"sourcesContent":["// INTERNAL: deliberately not exported from src/index.ts.\nimport { useCallback, useMemo, useRef } from 'react';\nimport type { FocusEvent, PointerEvent, RefObject } from 'react';\nimport { useClientLayoutEffect } from './useClientLayoutEffect';\nimport { getDeepestActiveElement, isEventInside } from './shadowDom';\n\n/**\n * Options for `useHoverOpen`.\n */\nexport interface UseHoverOpenOptions {\n /** Whether the floating element is open now, controlled or not. */\n open: boolean;\n /**\n * Asks for the floating element to open or close: when a delay runs out,\n * and at once on Escape. It isn't asked for the state it is already in,\n * except that an Escape between asking to open and the open showing takes\n * the ask back by asking to close.\n */\n onOpenChange: (open: boolean) => void;\n /** How long hover or keyboard focus waits before asking to open, in milliseconds. */\n openDelay: number;\n /**\n * How long it waits once the pointer and focus have both left before\n * asking to close, in milliseconds. Coming back in that time keeps it open,\n * which is how the pointer crosses a gap to the floating element.\n */\n closeDelay: number;\n /**\n * The element that opens it, which has to take focus itself. The Escape\n * listener goes on its document, an iframe's when it renders into one, and\n * focus left in the floating element comes back here when the floating\n * element closes.\n */\n triggerRef: RefObject<HTMLElement | null>;\n /** The floating element, while it is open. */\n floatingRef: RefObject<HTMLElement | null>;\n /**\n * Whether a tap on the trigger opens it at once, with the next tap closing\n * it. Without it, a touch pointer never opens it. While a tap holds it\n * open, no hover timer closes it, and a press outside the trigger and the\n * floating element does, as Escape does. The trigger's own tap action\n * still runs.\n * @defaultValue false\n */\n openOnTap?: boolean;\n /**\n * Whether an Escape that closes it stops there when focus is on the\n * trigger, in the floating element or nowhere, so a `Popover` or `Modal`\n * around it keeps its own. Off, Escape still closes it and always goes on\n * to whatever has focus, as suits content that only adds to the trigger.\n * @defaultValue true\n */\n consumeEscape?: boolean;\n}\n\n/**\n * Pointer and focus handlers for the element that holds the trigger. The\n * floating element has to be inside it in React's tree, in place or through\n * a portal, since React's events reach it from there either way, and it\n * should hold nothing else: focus inside it that isn't on the trigger counts\n * as focus in the floating element.\n */\nexport interface HoverOpenHandlers {\n onPointerEnter: (event: PointerEvent<HTMLElement>) => void;\n onPointerOver: (event: PointerEvent<HTMLElement>) => void;\n onPointerLeave: (event: PointerEvent<HTMLElement>) => void;\n onFocus: (event: FocusEvent<HTMLElement>) => void;\n onBlur: (event: FocusEvent<HTMLElement>) => void;\n onPointerDown: (event: PointerEvent<HTMLElement>) => void;\n onPointerUp: (event: PointerEvent<HTMLElement>) => void;\n}\n\n/**\n * Runs the caller's own handler on the element that holds the trigger, then\n * the hook's, so neither replaces the other.\n */\nexport function chain<E>(\n own: ((event: E) => void) | undefined,\n ours: (event: E) => void\n): (event: E) => void {\n return event => {\n own?.(event);\n ours(event);\n };\n}\n\n/**\n * Whether focus that just arrived on `el` is keyboard focus, as the browser\n * decides it for `:focus-visible`: focus from a key or from the browser's\n * own controls is, focus a press brings isn't, and focus a script moves\n * follows whichever came last. A browser that can't match the selector\n * throws, and then every focus counts, so keyboard users can still open it.\n */\nfunction isKeyboardFocus(el: Element): boolean {\n try {\n return el.matches(':focus-visible');\n } catch {\n return true;\n }\n}\n\n/** Whether `node` is `container` or inside it, when there are both. */\nconst within = (container: Node | null, node: Node | null): boolean =>\n Boolean(container && node && container.contains(node));\n\n/** A hook that wants Escape: its floating element is open, or an open is on its way. */\ninterface EscapeEntry {\n /** When its floating element opened, counted across the page, or 0 while it is closed. */\n openedAt: number;\n trigger: () => HTMLElement | null;\n floating: () => HTMLElement | null;\n /** Takes back an open that is on its way: waiting on its delay, or asked for and not showing yet. */\n cancel: () => void;\n /** Asks to close. */\n close: () => void;\n /** Whether an Escape that closes it can stop there. */\n consumes: () => boolean;\n}\n\n/**\n * Per document, the hooks that want Escape and the one listener they share\n * while there are any, so a card inside another card doesn't add a second\n * listener that closes both on one keypress.\n */\nconst escapeListeners = new Map<\n Document,\n { entries: Set<EscapeEntry>; stop: () => void }\n>();\n\n/** Counts openings across the page, so the most recent one is known. */\nlet openings = 0;\n\n/**\n * Adds `entry` to the hooks that want Escape on `doc`, starting the shared\n * listener when it is the first.\n *\n * Escape takes back every open that is on its way, so nothing appears right\n * after it, and closes one open floating element, chosen by these rules in\n * turn:\n *\n * 1. One that focus is in, on its trigger or inside it, when there is one.\n * 2. The innermost: one whose floating element holds no other's trigger.\n * That follows nesting whether either one is portaled, since an inner\n * trigger sits in the outer floating element either way.\n * 3. The one that opened last.\n *\n * The keypress belongs to whatever has focus. When that is one of the open\n * floating elements or their triggers, or nothing, the listener stops it in\n * the capture phase, before anything around (a Popover, a Modal) sees it,\n * unless the one it closes was set not to consume Escape.\n * When focus is anywhere else, such as a combobox or a dialog of its own,\n * the floating element still closes, since hover put it there without\n * focus, and the key goes on to that owner.\n *\n * @returns What to call when the hook stops wanting Escape.\n */\nfunction wantEscape(doc: Document, entry: EscapeEntry): () => void {\n let listener = escapeListeners.get(doc);\n if (!listener) {\n const entries = new Set<EscapeEntry>();\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key !== 'Escape') return;\n const open: EscapeEntry[] = [];\n // A copy, since taking an open back can take its hook out of the set.\n for (const each of [...entries]) {\n if (each.openedAt) open.push(each);\n else each.cancel();\n }\n const focused = getDeepestActiveElement(doc);\n const holding = open.filter(\n each =>\n within(each.trigger(), focused) || within(each.floating(), focused)\n );\n const pool = holding.length > 0 ? holding : open;\n let top: EscapeEntry | undefined;\n for (const each of pool) {\n const holdsAnother = pool.some(\n other => other !== each && within(each.floating(), other.trigger())\n );\n if (!holdsAnother && (!top || each.openedAt > top.openedAt)) {\n top = each;\n }\n }\n if (!top) return;\n if (\n top.consumes() &&\n (holding.length > 0 || !focused || focused === doc.body)\n ) {\n event.preventDefault();\n event.stopPropagation();\n }\n top.close();\n };\n doc.addEventListener('keydown', handleKeyDown, true);\n listener = {\n entries,\n stop: () => {\n doc.removeEventListener('keydown', handleKeyDown, true);\n escapeListeners.delete(doc);\n },\n };\n escapeListeners.set(doc, listener);\n }\n const shared = listener;\n shared.entries.add(entry);\n return () => {\n shared.entries.delete(entry);\n if (shared.entries.size === 0) shared.stop();\n };\n}\n\ninterface HoverOpenState {\n /** The pointer, other than a touch, is over the trigger or the floating element. */\n pointerIn: boolean;\n /** Focus holds it open: any focus in the floating element, or keyboard focus on the trigger. */\n focusIn: boolean;\n /** Focus, however it got there, is in the floating element. */\n focusInFloating: boolean;\n /** Whether the pointer or focus was holding it open when last settled. */\n held: boolean;\n /** An open is waiting on its delay. */\n opening: boolean;\n /**\n * An open was asked for and isn't showing yet: no commit has answered it,\n * and the event that asked isn't over.\n */\n asked: boolean;\n /** Focus is on its way back to the trigger, which isn't focus arriving. */\n restoring: boolean;\n timer?: ReturnType<typeof setTimeout>;\n /** Ends an ask to open that no commit answered, once the event that made it is over. */\n askTimer?: ReturnType<typeof setTimeout>;\n /** Takes this hook out of the ones that want Escape, while it is in. */\n releaseEscape?: () => void;\n /**\n * The document of the tap that opened it, or asked to: a press outside in\n * that document closes it while it is open.\n */\n tappedIn?: Document;\n /** The last press React brought to the holder: on the trigger or in the floating element's tree. */\n pressedInside?: Event;\n /** Takes the listener for a press outside off its document, while it is on. */\n stopPress?: () => void;\n}\n\n/**\n * Decides when a floating element that opens on hover or keyboard focus asks\n * to open and to close. It renders nothing, and leaves roles, ARIA and\n * placement to the component that calls it.\n *\n * - The pointer entering, or keyboard focus arriving on the trigger, asks to\n * open after `openDelay`. A touch pointer doesn't count. Neither does focus\n * that a press brings to the trigger: it counts when the browser shows it\n * as `:focus-visible`. The pointer arriving from outside a shadow root the\n * trigger renders into counts too, though React sends no enter for it.\n * - Focus in the floating element holds it open however it got there,\n * including focus in content it portals elsewhere, such as a nested card.\n * Whether a click focuses what it lands on is up to the browser: Safari\n * and Firefox on macOS don't focus a button on click, so there a clicked\n * button doesn't hold it once the pointer leaves.\n * - Once the pointer and focus have both left, it asks to close after\n * `closeDelay`, and either coming back before then keeps it open. Nothing\n * else closes it on a timer.\n * - Escape closes it at once, wherever focus is, and stops there when focus\n * is in it, on its trigger or nowhere, so a `Popover` or `Modal` around it\n * keeps its own. With focus somewhere else, or with `consumeEscape` off,\n * the key goes on after closing it. Inside another one, the inner one\n * closes first. Escape before it\n * opens takes the opening back and goes on its way. One key listener per\n * document serves every hook on it, there only while one of them is open\n * or on its way to open.\n * - With `openOnTap`, a tap on the trigger opens it at once and the next tap\n * on the trigger closes it. While a tap holds it open, a press outside the\n * trigger and the floating element closes it, from a listener on the\n * document that is there only then. Taps never count as hover, and mouse\n * and pen work the same with it or without.\n * - Once it closes, for any reason, whatever was holding it open has to\n * leave and come back to open it again.\n * - When the floating element closes with focus inside it, focus goes back\n * to the trigger, unless the same update put it somewhere else. Handing it\n * back doesn't count as focus arriving.\n *\n * Nothing it scheduled runs after the component unmounts.\n *\n * @function useHoverOpen\n * @param options - The open state, how to ask for another, the delays, and the two elements.\n * @returns Handlers for the element that holds the trigger.\n */\nexport function useHoverOpen(options: UseHoverOpenOptions): HoverOpenHandlers {\n const latestRef = useRef(options);\n const stateRef = useRef<HoverOpenState>({\n pointerIn: false,\n focusIn: false,\n focusInFloating: false,\n held: false,\n opening: false,\n asked: false,\n restoring: false,\n });\n\n // In the hooks that want Escape from the moment an open is on its way until\n // the floating element closes, and out otherwise. Stamped with the order it\n // opened in.\n const listenForEscape = useCallback(function listen() {\n const s = stateRef.current;\n const entry = entryRef.current;\n const { open, triggerRef } = latestRef.current;\n if (!open) entry.openedAt = 0;\n else if (!entry.openedAt) {\n openings += 1;\n entry.openedAt = openings;\n }\n const wanted = open || s.opening || s.asked;\n if (wanted === Boolean(s.releaseEscape)) return;\n if (s.releaseEscape) {\n s.releaseEscape();\n s.releaseEscape = undefined;\n return;\n }\n const doc = triggerRef.current?.ownerDocument ?? document;\n s.releaseEscape = wantEscape(doc, entry);\n }, []);\n\n const entryRef = useRef<EscapeEntry>({\n openedAt: 0,\n trigger: () => latestRef.current.triggerRef.current,\n floating: () => latestRef.current.floatingRef.current,\n // Only ever called while an open is on its way, so it is one or the\n // other: still waiting on its delay, or asked for already.\n cancel: () => {\n const s = stateRef.current;\n clearTimeout(s.timer);\n // An ask already made is taken back by asking the other way.\n if (s.asked) latestRef.current.onOpenChange(false);\n s.opening = false;\n s.asked = false;\n listenForEscape();\n },\n close: () => {\n clearTimeout(stateRef.current.timer);\n latestRef.current.onOpenChange(false);\n },\n consumes: () => latestRef.current.consumeEscape !== false,\n });\n\n // Asks to open or to close. Escape can take an ask to open back until it is\n // answered: by the commit that follows it, or, when the caller keeps it\n // closed without rendering at all, once the event that asked is over, by\n // which time React has rendered any answer the caller set.\n const ask = useCallback(\n (next: boolean) => {\n const s = stateRef.current;\n s.asked = next;\n latestRef.current.onOpenChange(next);\n listenForEscape();\n if (!next) return;\n clearTimeout(s.askTimer);\n s.askTimer = setTimeout(() => {\n // Still closed: the caller turned it down.\n if (latestRef.current.open) return;\n s.asked = false;\n s.tappedIn = undefined;\n listenForEscape();\n });\n },\n [listenForEscape]\n );\n\n // Asks for what the pointer and focus call for, after its delay, when that\n // changed since the last time and isn't the state already.\n const settle = useCallback(() => {\n const s = stateRef.current;\n const held = s.pointerIn || s.focusIn;\n if (held !== s.held) {\n s.held = held;\n clearTimeout(s.timer);\n const { open, openDelay, closeDelay } = latestRef.current;\n s.opening = held && !open;\n if (held !== open) {\n s.timer = setTimeout(\n () => {\n s.opening = false;\n if (latestRef.current.open !== held) ask(held);\n else listenForEscape();\n },\n held ? openDelay : closeDelay\n );\n }\n }\n listenForEscape();\n }, [listenForEscape, ask]);\n\n // While a tap holds the floating element open, a press outside the trigger\n // and the floating element closes it. Set up again each time it opens or\n // closes.\n const listenForPress = useCallback(() => {\n const s = stateRef.current;\n s.stopPress?.();\n s.stopPress = undefined;\n const doc = latestRef.current.open ? s.tappedIn : undefined;\n if (!doc) return;\n const handlePointerDown = (event: Event) => {\n // Inside is a press React brought to the holder, which covers content\n // the floating element portals elsewhere, or one whose path runs\n // through the trigger or the floating element, which covers a press\n // that something inside stopped before React got it to the holder.\n const { triggerRef, floatingRef } = latestRef.current;\n if (\n event === s.pressedInside ||\n isEventInside(event, triggerRef.current) ||\n isEventInside(event, floatingRef.current)\n ) {\n return;\n }\n latestRef.current.onOpenChange(false);\n };\n doc.addEventListener('pointerdown', handlePointerDown);\n s.stopPress = () =>\n doc.removeEventListener('pointerdown', handlePointerDown);\n }, []);\n\n useClientLayoutEffect(() => {\n latestRef.current = options;\n // The commit after an ask to open answers it, open or still closed. A\n // tap that asked and still finds it closed was turned down.\n const s = stateRef.current;\n if (s.asked) {\n s.asked = false;\n if (!options.open) s.tappedIn = undefined;\n listenForEscape();\n }\n });\n\n useClientLayoutEffect(() => {\n const s = stateRef.current;\n return () => {\n clearTimeout(s.timer);\n clearTimeout(s.askTimer);\n s.opening = false;\n s.asked = false;\n s.releaseEscape?.();\n s.releaseEscape = undefined;\n s.stopPress?.();\n s.stopPress = undefined;\n };\n }, []);\n\n const { open } = options;\n // Before paint, so a pointer or a key arriving next finds this settled.\n useClientLayoutEffect(() => {\n const s = stateRef.current;\n if (!open) {\n // The floating element left from under the pointer, if it was there,\n // and no leave event says so. Focus resting on the trigger has done\n // its job too, so neither opens it again until it comes back.\n s.pointerIn = false;\n s.focusIn = false;\n s.tappedIn = undefined;\n if (s.focusInFloating) {\n s.focusInFloating = false;\n const trigger = latestRef.current.triggerRef.current;\n // Focus that went down with the floating element sits on <body>.\n // Focus somewhere else was put there by the same update, so it stays.\n if (\n trigger &&\n trigger.ownerDocument.activeElement === trigger.ownerDocument.body\n ) {\n s.restoring = true;\n trigger.focus();\n s.restoring = false;\n }\n }\n }\n settle();\n listenForPress();\n }, [open, settle, listenForPress]);\n\n return useMemo<HoverOpenHandlers>(() => {\n const inTrigger = (node: EventTarget | null) =>\n within(latestRef.current.triggerRef.current, node as Node | null);\n return {\n onPointerEnter: event => {\n if (event.pointerType === 'touch') return;\n stateRef.current.pointerIn = true;\n settle();\n },\n onPointerOver: event => {\n // React makes the enter out of the pointerout on the element the\n // pointer left, and skips this pointerover when that element is its\n // own. Leaving page content for a trigger inside a shadow root, that\n // pointerout names only the shadow host, so no enter reaches here.\n // A pointerover from outside the holder and the floating element is\n // the pointer arriving, then. One from inside them is the pointer\n // moving about within, which doesn't bring back a closed one.\n const s = stateRef.current;\n if (s.pointerIn || event.pointerType === 'touch') return;\n const from = event.relatedTarget as Node | null;\n if (\n within(event.currentTarget, from) ||\n within(latestRef.current.floatingRef.current, from)\n ) {\n return;\n }\n s.pointerIn = true;\n settle();\n },\n onPointerLeave: event => {\n // A touch pointer never set pointerIn, so its leave has nothing to\n // clear, and on a device with a mouse too it would close a card the\n // mouse is still holding open.\n if (event.pointerType === 'touch') return;\n stateRef.current.pointerIn = false;\n settle();\n },\n onFocus: event => {\n const s = stateRef.current;\n // React brings focus here from the trigger and from the floating\n // element's whole tree, portals included, so focus not in the\n // trigger is in the floating element, wherever its DOM is.\n s.focusInFloating = !inTrigger(event.target);\n if (s.restoring) return;\n // Any focus in the floating element holds it. On the trigger, only\n // keyboard focus does, so a click there opens nothing.\n s.focusIn = s.focusInFloating || isKeyboardFocus(event.target);\n settle();\n },\n onBlur: event => {\n const s = stateRef.current;\n const next = event.relatedTarget as Node | null;\n // Where focus goes next, as far as the DOM can say. Focus going into\n // content the floating element portals elsewhere reads as leaving,\n // and the focus event that follows straight after brings it back.\n s.focusInFloating = within(latestRef.current.floatingRef.current, next);\n // Moving between the trigger and the floating element isn't leaving.\n if (s.focusInFloating || within(event.currentTarget, next)) return;\n s.focusIn = false;\n settle();\n },\n onPointerDown: event => {\n stateRef.current.pressedInside = event.nativeEvent;\n },\n onPointerUp: event => {\n const now = latestRef.current;\n // With openOnTap, a tap on the trigger opens it at once, and the\n // next one closes it. A tap anywhere else does nothing here.\n if (\n !now.openOnTap ||\n event.pointerType !== 'touch' ||\n !inTrigger(event.target)\n ) {\n return;\n }\n stateRef.current.tappedIn = now.open\n ? undefined\n : (event.target as Element).ownerDocument;\n ask(!now.open);\n },\n };\n }, [settle, ask]);\n}\n"],"names":["chain"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useHoverOpen.js","sources":["../../../src/helpers/useHoverOpen.ts"],"sourcesContent":["// INTERNAL: deliberately not exported from src/index.ts.\nimport { useCallback, useMemo, useRef } from 'react';\nimport type { FocusEvent, PointerEvent, RefObject } from 'react';\nimport { useClientLayoutEffect } from './useClientLayoutEffect';\nimport { getDeepestActiveElement, isEventInside } from './shadowDom';\n\n/**\n * Options for `useHoverOpen`.\n */\nexport interface UseHoverOpenOptions {\n /** Whether the floating element is open now, controlled or not. */\n open: boolean;\n /**\n * Asks for the floating element to open or close: when a delay runs out,\n * and at once on Escape. It isn't asked for the state it is already in,\n * except that an Escape between asking to open and the open showing takes\n * the ask back by asking to close.\n */\n onOpenChange: (open: boolean) => void;\n /** How long hover or keyboard focus waits before asking to open, in milliseconds. */\n openDelay: number;\n /**\n * How long it waits once the pointer and focus have both left before\n * asking to close, in milliseconds. Coming back in that time keeps it open,\n * which is how the pointer crosses a gap to the floating element.\n */\n closeDelay: number;\n /**\n * The element that opens it, which has to take focus itself. The Escape\n * listener goes on its document, an iframe's when it renders into one, and\n * focus left in the floating element comes back here when the floating\n * element closes.\n */\n triggerRef: RefObject<HTMLElement | null>;\n /** The floating element, while it is open. */\n floatingRef: RefObject<HTMLElement | null>;\n /**\n * Whether a tap on the trigger opens it at once, with the next tap closing\n * it. Without it, a touch pointer never opens it. While a tap holds it\n * open, no hover timer closes it, and a press outside the trigger and the\n * floating element does, as Escape does. The trigger's own tap action\n * still runs.\n * @defaultValue false\n */\n openOnTap?: boolean;\n /**\n * Whether an Escape that closes it stops there when focus is on the\n * trigger, in the floating element or nowhere, so a `Popover` or `Modal`\n * around it keeps its own. Off, Escape still closes it and always goes on\n * to whatever has focus, as suits content that only adds to the trigger.\n * @defaultValue true\n */\n consumeEscape?: boolean;\n}\n\n/**\n * Pointer and focus handlers for the element that holds the trigger. The\n * floating element has to be inside it in React's tree, in place or through\n * a portal, since React's events reach it from there either way, and it\n * should hold nothing else: focus inside it that isn't on the trigger counts\n * as focus in the floating element.\n */\nexport interface HoverOpenHandlers {\n onPointerEnter: (event: PointerEvent<HTMLElement>) => void;\n onPointerOver: (event: PointerEvent<HTMLElement>) => void;\n onPointerLeave: (event: PointerEvent<HTMLElement>) => void;\n onFocus: (event: FocusEvent<HTMLElement>) => void;\n onBlur: (event: FocusEvent<HTMLElement>) => void;\n onPointerDown: (event: PointerEvent<HTMLElement>) => void;\n onPointerUp: (event: PointerEvent<HTMLElement>) => void;\n}\n\n/**\n * Runs the caller's own handler on the element that holds the trigger, then\n * the hook's, so neither replaces the other.\n */\nexport function chain<E>(\n own: ((event: E) => void) | undefined,\n ours: (event: E) => void\n): (event: E) => void {\n return event => {\n own?.(event);\n ours(event);\n };\n}\n\n/**\n * Whether focus that just arrived on `el` is keyboard focus, as the browser\n * decides it for `:focus-visible`: focus from a key or from the browser's\n * own controls is, focus a press brings isn't, and focus a script moves\n * follows whichever came last. A browser that can't match the selector\n * throws, and then every focus counts, so keyboard users can still open it.\n */\nfunction isKeyboardFocus(el: Element): boolean {\n try {\n return el.matches(':focus-visible');\n } catch {\n return true;\n }\n}\n\n/** Whether `node` is `container` or inside it, when there are both. */\nconst within = (container: Node | null, node: Node | null): boolean =>\n Boolean(container && node && container.contains(node));\n\n/** A hook that wants Escape: its floating element is open, or an open is on its way. */\ninterface EscapeEntry {\n /** When its floating element opened, counted across the page, or 0 while it is closed. */\n openedAt: number;\n trigger: () => HTMLElement | null;\n floating: () => HTMLElement | null;\n /** Takes back an open that is on its way: waiting on its delay, or asked for and not showing yet. */\n cancel: () => void;\n /** Asks to close. */\n close: () => void;\n /** Whether an Escape that closes it can stop there. */\n consumes: () => boolean;\n}\n\n/**\n * Per document, the hooks that want Escape and the one listener they share\n * while there are any, so a card inside another card doesn't add a second\n * listener that closes both on one keypress.\n */\nconst escapeListeners = new Map<\n Document,\n { entries: Set<EscapeEntry>; stop: () => void }\n>();\n\n/** Counts openings across the page, so the most recent one is known. */\nlet openings = 0;\n\n/**\n * Adds `entry` to the hooks that want Escape on `doc`, starting the shared\n * listener when it is the first.\n *\n * Escape takes back every open that is on its way, so nothing appears right\n * after it, and closes one open floating element, chosen by these rules in\n * turn:\n *\n * 1. One that focus is in, on its trigger or inside it, when there is one.\n * 2. The innermost: one whose floating element holds no other's trigger.\n * That follows nesting whether either one is portaled, since an inner\n * trigger sits in the outer floating element either way.\n * 3. The one that opened last.\n *\n * The keypress belongs to whatever has focus. When that is one of the open\n * floating elements or their triggers, or nothing, the listener stops it in\n * the capture phase, before anything around (a Popover, a Modal) sees it,\n * unless the one it closes was set not to consume Escape.\n * When focus is anywhere else, such as a combobox or a dialog of its own,\n * the floating element still closes, since hover put it there without\n * focus, and the key goes on to that owner.\n *\n * @returns What to call when the hook stops wanting Escape.\n */\nfunction wantEscape(doc: Document, entry: EscapeEntry): () => void {\n let listener = escapeListeners.get(doc);\n if (!listener) {\n const entries = new Set<EscapeEntry>();\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key !== 'Escape') return;\n const open: EscapeEntry[] = [];\n // A copy, since taking an open back can take its hook out of the set.\n for (const each of [...entries]) {\n if (each.openedAt) open.push(each);\n else each.cancel();\n }\n const focused = getDeepestActiveElement(doc);\n const holding = open.filter(\n each =>\n within(each.trigger(), focused) || within(each.floating(), focused)\n );\n const pool = holding.length > 0 ? holding : open;\n let top: EscapeEntry | undefined;\n for (const each of pool) {\n const holdsAnother = pool.some(\n other => other !== each && within(each.floating(), other.trigger())\n );\n if (!holdsAnother && (!top || each.openedAt > top.openedAt)) {\n top = each;\n }\n }\n if (!top) return;\n if (\n top.consumes() &&\n (holding.length > 0 || !focused || focused === doc.body)\n ) {\n event.preventDefault();\n event.stopPropagation();\n }\n top.close();\n };\n doc.addEventListener('keydown', handleKeyDown, true);\n listener = {\n entries,\n stop: () => {\n doc.removeEventListener('keydown', handleKeyDown, true);\n escapeListeners.delete(doc);\n },\n };\n escapeListeners.set(doc, listener);\n }\n const shared = listener;\n shared.entries.add(entry);\n return () => {\n shared.entries.delete(entry);\n if (shared.entries.size === 0) shared.stop();\n };\n}\n\ninterface HoverOpenState {\n /** The pointer, other than a touch, is over the trigger or the floating element. */\n pointerIn: boolean;\n /** Focus holds it open: any focus in the floating element, or keyboard focus on the trigger. */\n focusIn: boolean;\n /** Focus, however it got there, is in the floating element. */\n focusInFloating: boolean;\n /** Whether the pointer or focus was holding it open when last settled. */\n held: boolean;\n /** An open is waiting on its delay. */\n opening: boolean;\n /**\n * An open was asked for and isn't showing yet: no commit has answered it,\n * and the event that asked isn't over.\n */\n asked: boolean;\n /** Focus is on its way back to the trigger, which isn't focus arriving. */\n restoring: boolean;\n timer?: ReturnType<typeof setTimeout>;\n /** Ends an ask to open that no commit answered, once the event that made it is over. */\n askTimer?: ReturnType<typeof setTimeout>;\n /** Takes this hook out of the ones that want Escape, while it is in. */\n releaseEscape?: () => void;\n /**\n * The document of the tap that opened it, or asked to: a press outside in\n * that document closes it while it is open.\n */\n tappedIn?: Document;\n /** The last press React brought to the holder: on the trigger or in the floating element's tree. */\n pressedInside?: Event;\n /** Takes the listener for a press outside off its document, while it is on. */\n stopPress?: () => void;\n}\n\n/**\n * Decides when a floating element that opens on hover or keyboard focus asks\n * to open and to close. It renders nothing, and leaves roles, ARIA and\n * placement to the component that calls it.\n *\n * - The pointer entering, or keyboard focus arriving on the trigger, asks to\n * open after `openDelay`. A touch pointer doesn't count. Neither does focus\n * that a press brings to the trigger: it counts when the browser shows it\n * as `:focus-visible`. The pointer arriving from outside a shadow root the\n * trigger renders into counts too, though React sends no enter for it.\n * - Focus in the floating element holds it open however it got there,\n * including focus in content it portals elsewhere, such as a nested card.\n * Whether a click focuses what it lands on is up to the browser: Safari\n * and Firefox on macOS don't focus a button on click, so there a clicked\n * button doesn't hold it once the pointer leaves.\n * - Once the pointer and focus have both left, it asks to close after\n * `closeDelay`, and either coming back before then keeps it open. Nothing\n * else closes it on a timer.\n * - Escape closes it at once, wherever focus is, and stops there when focus\n * is in it, on its trigger or nowhere, so a `Popover` or `Modal` around it\n * keeps its own. With focus somewhere else, or with `consumeEscape` off,\n * the key goes on after closing it. Inside another one, the inner one\n * closes first. Escape before it\n * opens takes the opening back and goes on its way. One key listener per\n * document serves every hook on it, there only while one of them is open\n * or on its way to open.\n * - With `openOnTap`, a tap on the trigger opens it at once and the next tap\n * on the trigger closes it. While a tap holds it open, a press outside the\n * trigger and the floating element closes it, from a listener on the\n * document that is there only then. Taps never count as hover, and mouse\n * and pen work the same with it or without.\n * - Once it closes, for any reason, whatever was holding it open has to\n * leave and come back to open it again.\n * - When the floating element closes with focus inside it, focus goes back\n * to the trigger, unless the same update put it somewhere else. Handing it\n * back doesn't count as focus arriving.\n *\n * Nothing it scheduled runs after the component unmounts.\n *\n * @function useHoverOpen\n * @param options - The open state, how to ask for another, the delays, and the two elements.\n * @returns Handlers for the element that holds the trigger.\n */\nexport function useHoverOpen(options: UseHoverOpenOptions): HoverOpenHandlers {\n const latestRef = useRef(options);\n const stateRef = useRef<HoverOpenState>({\n pointerIn: false,\n focusIn: false,\n focusInFloating: false,\n held: false,\n opening: false,\n asked: false,\n restoring: false,\n });\n\n // In the hooks that want Escape from the moment an open is on its way until\n // the floating element closes, and out otherwise. Stamped with the order it\n // opened in.\n const listenForEscape = useCallback(function listen() {\n const s = stateRef.current;\n const entry = entryRef.current;\n const { open, triggerRef } = latestRef.current;\n if (!open) entry.openedAt = 0;\n else if (!entry.openedAt) {\n openings += 1;\n entry.openedAt = openings;\n }\n const wanted = open || s.opening || s.asked;\n if (wanted === Boolean(s.releaseEscape)) return;\n if (s.releaseEscape) {\n s.releaseEscape();\n s.releaseEscape = undefined;\n return;\n }\n const doc = triggerRef.current?.ownerDocument ?? document;\n s.releaseEscape = wantEscape(doc, entry);\n }, []);\n\n const entryRef = useRef<EscapeEntry>({\n openedAt: 0,\n trigger: () => latestRef.current.triggerRef.current,\n floating: () => latestRef.current.floatingRef.current,\n // Only ever called while an open is on its way, so it is one or the\n // other: still waiting on its delay, or asked for already.\n cancel: () => {\n const s = stateRef.current;\n clearTimeout(s.timer);\n // An ask already made is taken back by asking the other way.\n if (s.asked) latestRef.current.onOpenChange(false);\n s.opening = false;\n s.asked = false;\n listenForEscape();\n },\n close: () => {\n clearTimeout(stateRef.current.timer);\n latestRef.current.onOpenChange(false);\n },\n consumes: () => latestRef.current.consumeEscape !== false,\n });\n\n // Asks to open or to close. Escape can take an ask to open back until it is\n // answered: by the commit that follows it, or, when the caller keeps it\n // closed without rendering at all, once the event that asked is over, by\n // which time React has rendered any answer the caller set.\n const ask = useCallback(\n (next: boolean) => {\n const s = stateRef.current;\n s.asked = next;\n latestRef.current.onOpenChange(next);\n listenForEscape();\n if (!next) return;\n clearTimeout(s.askTimer);\n s.askTimer = setTimeout(() => {\n // Still closed: the caller turned it down.\n if (latestRef.current.open) return;\n s.asked = false;\n s.tappedIn = undefined;\n listenForEscape();\n });\n },\n [listenForEscape]\n );\n\n // Asks for what the pointer and focus call for, after its delay, when that\n // changed since the last time and isn't the state already.\n const settle = useCallback(() => {\n const s = stateRef.current;\n const held = s.pointerIn || s.focusIn;\n if (held !== s.held) {\n s.held = held;\n clearTimeout(s.timer);\n const { open, openDelay, closeDelay } = latestRef.current;\n s.opening = held && !open;\n if (held !== open) {\n s.timer = setTimeout(\n () => {\n s.opening = false;\n if (latestRef.current.open !== held) ask(held);\n else listenForEscape();\n },\n held ? openDelay : closeDelay\n );\n }\n }\n listenForEscape();\n }, [listenForEscape, ask]);\n\n // While a tap holds the floating element open, a press outside the trigger\n // and the floating element closes it. Set up again each time it opens or\n // closes.\n const listenForPress = useCallback(() => {\n const s = stateRef.current;\n s.stopPress?.();\n s.stopPress = undefined;\n const doc = latestRef.current.open ? s.tappedIn : undefined;\n if (!doc) return;\n const handlePointerDown = (event: Event) => {\n // Inside is a press React brought to the holder, which covers content\n // the floating element portals elsewhere, or one whose path runs\n // through the trigger or the floating element, which covers a press\n // that something inside stopped before React got it to the holder.\n const { triggerRef, floatingRef } = latestRef.current;\n if (\n event === s.pressedInside ||\n isEventInside(event, triggerRef.current) ||\n isEventInside(event, floatingRef.current)\n ) {\n return;\n }\n latestRef.current.onOpenChange(false);\n };\n doc.addEventListener('pointerdown', handlePointerDown);\n s.stopPress = () =>\n doc.removeEventListener('pointerdown', handlePointerDown);\n }, []);\n\n useClientLayoutEffect(() => {\n latestRef.current = options;\n // The commit after an ask to open answers it, open or still closed. A\n // tap that asked and still finds it closed was turned down.\n const s = stateRef.current;\n if (s.asked) {\n s.asked = false;\n if (!options.open) s.tappedIn = undefined;\n listenForEscape();\n }\n });\n\n useClientLayoutEffect(() => {\n const s = stateRef.current;\n return () => {\n clearTimeout(s.timer);\n clearTimeout(s.askTimer);\n s.opening = false;\n s.asked = false;\n s.releaseEscape?.();\n s.releaseEscape = undefined;\n s.stopPress?.();\n s.stopPress = undefined;\n };\n }, []);\n\n const { open } = options;\n // Before paint, so a pointer or a key arriving next finds this settled.\n useClientLayoutEffect(() => {\n const s = stateRef.current;\n if (!open) {\n // The floating element left from under the pointer, if it was there,\n // and no leave event says so. Focus resting on the trigger has done\n // its job too, so neither opens it again until it comes back.\n s.pointerIn = false;\n s.focusIn = false;\n s.tappedIn = undefined;\n if (s.focusInFloating) {\n s.focusInFloating = false;\n const trigger = latestRef.current.triggerRef.current;\n // Focus that went down with the floating element sits on <body>.\n // Focus somewhere else was put there by the same update, so it stays.\n if (\n trigger &&\n trigger.ownerDocument.activeElement === trigger.ownerDocument.body\n ) {\n s.restoring = true;\n trigger.focus();\n s.restoring = false;\n }\n }\n }\n settle();\n listenForPress();\n }, [open, settle, listenForPress]);\n\n return useMemo<HoverOpenHandlers>(() => {\n const inTrigger = (node: EventTarget | null) =>\n within(latestRef.current.triggerRef.current, node as Node | null);\n return {\n onPointerEnter: event => {\n if (event.pointerType === 'touch') return;\n stateRef.current.pointerIn = true;\n settle();\n },\n onPointerOver: event => {\n // React makes the enter out of the pointerout on the element the\n // pointer left, and skips this pointerover when that element is its\n // own. Leaving page content for a trigger inside a shadow root, that\n // pointerout names only the shadow host, so no enter reaches here.\n // A pointerover from outside the holder and the floating element is\n // the pointer arriving, then. One from inside them is the pointer\n // moving about within, which doesn't bring back a closed one.\n const s = stateRef.current;\n if (s.pointerIn || event.pointerType === 'touch') return;\n const from = event.relatedTarget as Node | null;\n if (\n within(event.currentTarget, from) ||\n within(latestRef.current.floatingRef.current, from)\n ) {\n return;\n }\n s.pointerIn = true;\n settle();\n },\n onPointerLeave: event => {\n // A touch pointer never set pointerIn, so its leave has nothing to\n // clear, and on a device with a mouse too it would close a card the\n // mouse is still holding open.\n if (event.pointerType === 'touch') return;\n stateRef.current.pointerIn = false;\n settle();\n },\n onFocus: event => {\n const s = stateRef.current;\n // React brings focus here from the trigger and from the floating\n // element's whole tree, portals included, so focus not in the\n // trigger is in the floating element, wherever its DOM is.\n s.focusInFloating = !inTrigger(event.target);\n if (s.restoring) return;\n // Any focus in the floating element holds it. On the trigger, only\n // keyboard focus does, so a click there opens nothing.\n s.focusIn = s.focusInFloating || isKeyboardFocus(event.target);\n settle();\n },\n onBlur: event => {\n const s = stateRef.current;\n const next = event.relatedTarget as Node | null;\n // Where focus goes next, as far as the DOM can say. Focus going into\n // content the floating element portals elsewhere reads as leaving,\n // and the focus event that follows straight after brings it back.\n s.focusInFloating = within(latestRef.current.floatingRef.current, next);\n // Moving between the trigger and the floating element isn't leaving.\n if (s.focusInFloating || within(event.currentTarget, next)) return;\n s.focusIn = false;\n settle();\n },\n onPointerDown: event => {\n stateRef.current.pressedInside = event.nativeEvent;\n },\n onPointerUp: event => {\n const now = latestRef.current;\n // With openOnTap, a tap on the trigger opens it at once, and the\n // next one closes it. A tap anywhere else does nothing here.\n if (\n !now.openOnTap ||\n event.pointerType !== 'touch' ||\n !inTrigger(event.target)\n ) {\n return;\n }\n stateRef.current.tappedIn = now.open\n ? undefined\n : (event.target as Element).ownerDocument;\n ask(!now.open);\n },\n };\n }, [settle, ask]);\n}\n"],"names":["chain"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useInertSiblings.js","sources":["../../../src/helpers/useInertSiblings.ts"],"sourcesContent":["// INTERNAL: deliberately not exported from src/index.ts.\nimport { useEffect } from 'react';\nimport type { RefObject } from 'react';\nimport { getActiveElementInTree, getDeepestActiveElement } from './shadowDom';\n\n/**\n * How many callers hold each element inert. Shared across callers, the way\n * the scroll lock's count is, so two that cover the same element don't take\n * `inert` off it while the other still needs it. An element that was inert\n * before any caller came isn't counted, so it's left as it was.\n */\nconst holds = new Map<Element, number>();\n\n/**\n * Makes `el` inert for one more caller. Returns `false`, touching nothing,\n * when it was inert already and no caller made it so.\n */\nfunction hold(el: Element): boolean {\n const count = holds.get(el) ?? 0;\n if (count === 0 && el.hasAttribute('inert')) return false;\n holds.set(el, count + 1);\n el.setAttribute('inert', '');\n return true;\n}\n\n/** Lets go of one caller's hold, and takes `inert` off with the last one. */\nfunction release(el: Element): void {\n const count = (holds.get(el) as number) - 1;\n if (count > 0) {\n holds.set(el, count);\n return;\n }\n holds.delete(el);\n el.removeAttribute('inert');\n}\n\n/** The element's siblings, in a shadow root as anywhere else. */\nfunction siblingsOf(el: Element): Element[] {\n const siblings: Element[] = [];\n for (let s = el.previousElementSibling; s; s = s.previousElementSibling) {\n siblings.push(s);\n }\n for (let s = el.nextElementSibling; s; s = s.nextElementSibling) {\n siblings.push(s);\n }\n return siblings;\n}\n\n/**\n * The element above `el` in the page: its parent, or the host of the shadow\n * root it sits at the top of. Nothing above the top of a detached tree.\n */\nconst above = (el: Element): Element | undefined =>\n el.parentElement ?? (el.getRootNode() as Partial<ShadowRoot>).host;\n\n/**\n * Makes the elements beside `ref`'s element inert while `active` is true,\n * and takes it off again when `active` turns false or the caller unmounts.\n * With `wholePage`, it does the same beside each element above it up to\n * `<body>`, so everything on the page but `ref`'s element is inert, the way a\n * modal layer leaves it. The element itself and its own content are never\n * touched.\n *\n * It sets the `inert` attribute through the DOM in an effect, so it works the\n * same on React 18 and 19 and a server render has nothing to show for it. An\n * element that was inert already is left alone, and keeps its `inert` once\n * this lets go. It makes inert what's there when it turns on: an element that\n * mounts beside it afterwards isn't. It follows the DOM, so content a web\n * component renders around a slot this sits in, and text with no element of\n * its own, stays as it is.\n *\n * Focus on an element that turns inert is lost: the browser moves it to\n * `<body>`. So when focus was inside what this made inert, and is still on\n * `<body>` when this lets go, it goes back to the element that had it. Focus\n * this never took isn't handed anywhere, and focus the user or the app has\n * put somewhere else meanwhile stays there.\n *\n * @function useInertSiblings\n * @param ref - Ref to the element that stays reachable.\n * @param active - Whether what's beside it is inert.\n * @param wholePage - Whether that goes for the whole page rather than only\n * the element's siblings.\n */\nexport function useInertSiblings(\n ref: RefObject<Element | null>,\n active: boolean,\n wholePage: boolean\n): void {\n useEffect(() => {\n const el = ref.current;\n if (!active || !el) return undefined;\n const doc = el.ownerDocument;\n\n const held: Element[] = [];\n for (\n let node: Element | undefined = el;\n node && node !== doc.body;\n node = wholePage ? above(node) : undefined\n ) {\n held.push(...siblingsOf(node).filter(hold));\n }\n\n // Focus this takes away, to hand back later. It's looked up as `el`'s\n // tree sees it, where focus inside a nested shadow root reads as its\n // host, so it compares with what's held. What gets focused again is the\n // element that really had focus, inside that shadow root.\n const focused = getActiveElementInTree(el);\n const taken = held.some(other => other.contains(focused))\n ? (getDeepestActiveElement(doc) as HTMLElement | null)\n : null;\n\n return () => {\n held.forEach(release);\n if (doc.activeElement === doc.body) taken?.focus();\n };\n }, [ref, active, wholePage]);\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useInertSiblings.js","sources":["../../../src/helpers/useInertSiblings.ts"],"sourcesContent":["// INTERNAL: deliberately not exported from src/index.ts.\nimport { useEffect } from 'react';\nimport type { RefObject } from 'react';\nimport { getActiveElementInTree, getDeepestActiveElement } from './shadowDom';\n\n/**\n * How many callers hold each element inert. Shared across callers, the way\n * the scroll lock's count is, so two that cover the same element don't take\n * `inert` off it while the other still needs it. An element that was inert\n * before any caller came isn't counted, so it's left as it was.\n */\nconst holds = new Map<Element, number>();\n\n/**\n * Makes `el` inert for one more caller. Returns `false`, touching nothing,\n * when it was inert already and no caller made it so.\n */\nfunction hold(el: Element): boolean {\n const count = holds.get(el) ?? 0;\n if (count === 0 && el.hasAttribute('inert')) return false;\n holds.set(el, count + 1);\n el.setAttribute('inert', '');\n return true;\n}\n\n/** Lets go of one caller's hold, and takes `inert` off with the last one. */\nfunction release(el: Element): void {\n const count = (holds.get(el) as number) - 1;\n if (count > 0) {\n holds.set(el, count);\n return;\n }\n holds.delete(el);\n el.removeAttribute('inert');\n}\n\n/** The element's siblings, in a shadow root as anywhere else. */\nfunction siblingsOf(el: Element): Element[] {\n const siblings: Element[] = [];\n for (let s = el.previousElementSibling; s; s = s.previousElementSibling) {\n siblings.push(s);\n }\n for (let s = el.nextElementSibling; s; s = s.nextElementSibling) {\n siblings.push(s);\n }\n return siblings;\n}\n\n/**\n * The element above `el` in the page: its parent, or the host of the shadow\n * root it sits at the top of. Nothing above the top of a detached tree.\n */\nconst above = (el: Element): Element | undefined =>\n el.parentElement ?? (el.getRootNode() as Partial<ShadowRoot>).host;\n\n/**\n * Makes the elements beside `ref`'s element inert while `active` is true,\n * and takes it off again when `active` turns false or the caller unmounts.\n * With `wholePage`, it does the same beside each element above it up to\n * `<body>`, so everything on the page but `ref`'s element is inert, the way a\n * modal layer leaves it. The element itself and its own content are never\n * touched.\n *\n * It sets the `inert` attribute through the DOM in an effect, so it works the\n * same on React 18 and 19 and a server render has nothing to show for it. An\n * element that was inert already is left alone, and keeps its `inert` once\n * this lets go. It makes inert what's there when it turns on: an element that\n * mounts beside it afterwards isn't. It follows the DOM, so content a web\n * component renders around a slot this sits in, and text with no element of\n * its own, stays as it is.\n *\n * Focus on an element that turns inert is lost: the browser moves it to\n * `<body>`. So when focus was inside what this made inert, and is still on\n * `<body>` when this lets go, it goes back to the element that had it. Focus\n * this never took isn't handed anywhere, and focus the user or the app has\n * put somewhere else meanwhile stays there.\n *\n * @function useInertSiblings\n * @param ref - Ref to the element that stays reachable.\n * @param active - Whether what's beside it is inert.\n * @param wholePage - Whether that goes for the whole page rather than only\n * the element's siblings.\n */\nexport function useInertSiblings(\n ref: RefObject<Element | null>,\n active: boolean,\n wholePage: boolean\n): void {\n useEffect(() => {\n const el = ref.current;\n if (!active || !el) return undefined;\n const doc = el.ownerDocument;\n\n const held: Element[] = [];\n for (\n let node: Element | undefined = el;\n node && node !== doc.body;\n node = wholePage ? above(node) : undefined\n ) {\n held.push(...siblingsOf(node).filter(hold));\n }\n\n // Focus this takes away, to hand back later. It's looked up as `el`'s\n // tree sees it, where focus inside a nested shadow root reads as its\n // host, so it compares with what's held. What gets focused again is the\n // element that really had focus, inside that shadow root.\n const focused = getActiveElementInTree(el);\n const taken = held.some(other => other.contains(focused))\n ? (getDeepestActiveElement(doc) as HTMLElement | null)\n : null;\n\n return () => {\n held.forEach(release);\n if (doc.activeElement === doc.body) taken?.focus();\n };\n }, [ref, active, wholePage]);\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useIsHydrated.js","sources":["../../../src/helpers/useIsHydrated.ts"],"sourcesContent":["import { useSyncExternalStore } from 'react';\n\n// \"Are we past hydration?\" as a store that never changes: React reads the\n// server snapshot both while server-rendering and while hydrating, and the\n// client snapshot from the first post-hydration render onward.\nconst subscribeToNothing = () => () => {};\nconst getClientSnapshot = () => true;\nconst getServerSnapshot = () => false;\n\n/**\n * Reports whether the component has passed hydration.\n *\n * Returns `false` during server rendering and during the hydrating client\n * render, then `true` from the commit that follows. Use it to defer\n * DOM-only behaviour (such as portalling) past hydration so the first client\n * render matches the server markup instead of tripping hydration recovery.\n *\n * Only a component that hydrates ever sees `false` on the client. One that\n * mounts later (after a route change, or in an app with no server rendering)\n * gets `true` on its first render, so it pays for no extra render. It\n * subscribes to nothing, so no later render flips it back.\n *\n * @function useIsHydrated\n * @returns `true` once the hydrating render has completed, otherwise `false`.\n */\nexport function useIsHydrated(): boolean {\n return useSyncExternalStore(\n subscribeToNothing,\n getClientSnapshot,\n getServerSnapshot\n );\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useIsHydrated.js","sources":["../../../src/helpers/useIsHydrated.ts"],"sourcesContent":["import { useSyncExternalStore } from 'react';\n\n// \"Are we past hydration?\" as a store that never changes: React reads the\n// server snapshot both while server-rendering and while hydrating, and the\n// client snapshot from the first post-hydration render onward.\nconst subscribeToNothing = () => () => {};\nconst getClientSnapshot = () => true;\nconst getServerSnapshot = () => false;\n\n/**\n * Reports whether the component has passed hydration.\n *\n * Returns `false` during server rendering and during the hydrating client\n * render, then `true` from the commit that follows. Use it to defer\n * DOM-only behaviour (such as portalling) past hydration so the first client\n * render matches the server markup instead of tripping hydration recovery.\n *\n * Only a component that hydrates ever sees `false` on the client. One that\n * mounts later (after a route change, or in an app with no server rendering)\n * gets `true` on its first render, so it pays for no extra render. It\n * subscribes to nothing, so no later render flips it back.\n *\n * @function useIsHydrated\n * @returns `true` once the hydrating render has completed, otherwise `false`.\n */\nexport function useIsHydrated(): boolean {\n return useSyncExternalStore(\n subscribeToNothing,\n getClientSnapshot,\n getServerSnapshot\n );\n}\n"],"names":[],"mappings":";;;;AAEA;AACA;AACA;AACA,MAAM,kBAAkB,GAAG,MAAM,MAAK,EAAE,CAAC;AACzC,MAAM,iBAAiB,GAAG,MAAM,IAAI;AACpC,MAAM,iBAAiB,GAAG,MAAM,KAAK;AAErC;;;;;;;;;;;;;;;AAeG;SACa,aAAa,GAAA;IAC3B,OAAO,oBAAoB,CACzB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,CAClB;AACH;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLatestRef.js","sources":["../../../src/helpers/useLatestRef.ts"],"sourcesContent":["// INTERNAL: deliberately not exported from src/index.ts.\nimport { useRef } from 'react';\nimport { useClientLayoutEffect } from './useClientLayoutEffect';\n\n/**\n * A ref holding the value from the latest commit, for a listener that is\n * added once and calls whatever callback it was given most recently.\n *\n * A listener whose effect lists a callback prop is taken off and added again\n * each time that callback changes, and an inline one changes on every render\n * of its parent. React can render between two listeners for the same\n * keypress, and the browser runs neither a listener taken off mid-event nor\n * one added mid-event, so that keypress never reaches it (#1088). Reading the\n * callback from here keeps the listener where it is.\n *\n * It's updated before paint on each commit, so a listener reads the props of\n * the render that's on screen.\n *\n * @function useLatestRef\n * @param value - The value to keep current, usually a callback prop.\n * @returns A ref whose `current` is `value` as of the latest commit.\n */\nexport function useLatestRef<T>(value: T): { readonly current: T } {\n const ref = useRef(value);\n useClientLayoutEffect(() => {\n ref.current = value;\n });\n return ref;\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useLatestRef.js","sources":["../../../src/helpers/useLatestRef.ts"],"sourcesContent":["// INTERNAL: deliberately not exported from src/index.ts.\nimport { useRef } from 'react';\nimport { useClientLayoutEffect } from './useClientLayoutEffect';\n\n/**\n * A ref holding the value from the latest commit, for a listener that is\n * added once and calls whatever callback it was given most recently.\n *\n * A listener whose effect lists a callback prop is taken off and added again\n * each time that callback changes, and an inline one changes on every render\n * of its parent. React can render between two listeners for the same\n * keypress, and the browser runs neither a listener taken off mid-event nor\n * one added mid-event, so that keypress never reaches it (#1088). Reading the\n * callback from here keeps the listener where it is.\n *\n * It's updated before paint on each commit, so a listener reads the props of\n * the render that's on screen.\n *\n * @function useLatestRef\n * @param value - The value to keep current, usually a callback prop.\n * @returns A ref whose `current` is `value` as of the latest commit.\n */\nexport function useLatestRef<T>(value: T): { readonly current: T } {\n const ref = useRef(value);\n useClientLayoutEffect(() => {\n ref.current = value;\n });\n return ref;\n}\n"],"names":[],"mappings":";;;;;AAAA;AAIA;;;;;;;;;;;;;;;;;AAiBG;AACG,SAAU,YAAY,CAAI,KAAQ,EAAA;AACtC,IAAA,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC;IACzB,qBAAqB,CAAC,MAAK;AACzB,QAAA,GAAG,CAAC,OAAO,GAAG,KAAK;AACrB,IAAA,CAAC,CAAC;AACF,IAAA,OAAO,GAAG;AACZ;;"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
|
+
'use client';
|
|
2
3
|
import { useMemo } from 'react';
|
|
3
4
|
import { getActiveElementInTree } from './shadowDom.js';
|
|
4
5
|
import { useTypeahead, typeaheadMatch } from './typeahead.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMenuKeys.js","sources":["../../../src/helpers/useMenuKeys.ts"],"sourcesContent":["// INTERNAL: deliberately not exported from src/index.ts.\nimport { useMemo } from 'react';\nimport type { KeyboardEvent } from 'react';\nimport { getActiveElementInTree } from './shadowDom';\nimport { typeaheadMatch, useTypeahead } from './typeahead';\n\nexport { TYPEAHEAD_TIMEOUT } from './typeahead';\n\n/**\n * Every menu-item role, so an item a caller gives `menuitemcheckbox` or\n * `menuitemradio` (a filter or sort menu) stays reachable.\n */\nexport const MENU_ITEM_SELECTOR =\n '[role=\"menuitem\"], [role=\"menuitemcheckbox\"], [role=\"menuitemradio\"]';\n\n/**\n * Whether a menu item can be reached and activated: it isn't disabled, as a\n * native control or through `aria-disabled=\"true\"`.\n */\nexport function isEnabledMenuItem(item: Element): boolean {\n return (\n !item.hasAttribute('disabled') &&\n item.getAttribute('aria-disabled') !== 'true'\n );\n}\n\n/** Whether `node` is one of `menu`'s items, enabled or not. */\nconst isItemOf = (menu: Element, node: Element | null): boolean =>\n Boolean(node && menu.contains(node) && node.matches(MENU_ITEM_SELECTOR));\n\n/** The items in `menu` the keys reach, in document order. */\nexport function enabledMenuItems(menu: Element): HTMLElement[] {\n return Array.from(\n menu.querySelectorAll<HTMLElement>(MENU_ITEM_SELECTOR)\n ).filter(isEnabledMenuItem);\n}\n\n/**\n * Whether the browser activates `item` on this key by itself: a `<button>`\n * answers Enter and Space, and a link with an `href` answers Enter.\n */\nfunction activatesItself(item: HTMLElement, key: string): boolean {\n if (item.tagName === 'BUTTON') return true;\n return key === 'Enter' && item.tagName === 'A' && item.hasAttribute('href');\n}\n\n/** What `useMenuKeys` returns. */\nexport interface MenuKeys {\n /** The `keydown` handler for the `role=\"menu\"` element. */\n onKeyDown: (event: KeyboardEvent<HTMLElement>) => void;\n /**\n * Forgets what has been typed, so the next character starts a new search.\n * Call it when the menu opens, so a search typed just before it last\n * closed doesn't carry over.\n */\n reset: () => void;\n}\n\n/**\n * Moves through and activates the items of a `role=\"menu\"` element from the\n * keyboard. Put the `onKeyDown` it returns on that element. It reads the\n * items from the element each time, so items that come and go are found,\n * and it renders nothing.\n *\n * It acts only while focus is on the menu element itself or on one of its\n * items, so anything else focusable inside the menu, such as a filter\n * field, keeps its own keys.\n *\n * - Down and Up move to the next and previous enabled item, wrapping, and\n * Home and End go to the first and last. From no item, Down goes to the\n * first and Up to the last.\n * - Enter and Space activate the focused item. A `<button>`, and a link with\n * an `href` for Enter, get the browser's own click, and anything else gets\n * one from here, so its `onClick` runs either way. A key held down\n * activates it once. A key handler on the item that called\n * `preventDefault()` has claimed the key, and nothing else runs it.\n * - A printable character moves to the next enabled item whose text starts\n * with what has been typed. Characters typed within `TYPEAHEAD_TIMEOUT` of\n * each other add up to one search, until `reset` starts a new one.\n *\n * Items are `MENU_ITEM_SELECTOR` matches, and disabled ones are skipped by\n * all of the above. Escape and Tab are left to the menu's owner, since what\n * closing means is up to it, and so is any other key.\n *\n * @function useMenuKeys\n * @returns The `keydown` handler for the menu element, and a way to forget\n * what has been typed.\n */\nexport function useMenuKeys(): MenuKeys {\n const typeahead = useTypeahead();\n\n return useMemo<MenuKeys>(() => {\n const onKeyDown = (event: KeyboardEvent<HTMLElement>) => {\n const menu = event.currentTarget;\n const focused = getActiveElementInTree(menu);\n if (focused !== menu && !isItemOf(menu, focused)) return;\n const items = enabledMenuItems(menu);\n if (!items.length) return;\n const current = items.indexOf(focused as HTMLElement);\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n items[current >= 0 ? (current + 1) % items.length : 0].focus();\n return;\n case 'ArrowUp':\n event.preventDefault();\n items[\n current >= 0\n ? (current - 1 + items.length) % items.length\n : items.length - 1\n ].focus();\n return;\n case 'Home':\n event.preventDefault();\n items[0].focus();\n return;\n case 'End':\n event.preventDefault();\n items[items.length - 1].focus();\n return;\n case 'Enter':\n case ' ': {\n if (current < 0 || event.defaultPrevented) return;\n const item = items[current];\n if (activatesItself(item, event.key)) {\n // The browser clicks on Enter's keydown, and a held key sends one\n // per auto-repeat. Space clicks on keyup, once however long it's\n // held.\n if (event.repeat && event.key === 'Enter') event.preventDefault();\n return;\n }\n // Space would scroll the page, and a held key repeats.\n event.preventDefault();\n if (!event.repeat) item.click();\n return;\n }\n default:\n break;\n }\n\n if (\n event.key.length !== 1 ||\n event.ctrlKey ||\n event.metaKey ||\n event.altKey ||\n event.defaultPrevented\n ) {\n return;\n }\n event.preventDefault();\n typeaheadMatch(items, typeahead.type(event.key), current)?.focus();\n };\n return { onKeyDown, reset: typeahead.reset };\n }, [typeahead]);\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useMenuKeys.js","sources":["../../../src/helpers/useMenuKeys.ts"],"sourcesContent":["// INTERNAL: deliberately not exported from src/index.ts.\nimport { useMemo } from 'react';\nimport type { KeyboardEvent } from 'react';\nimport { getActiveElementInTree } from './shadowDom';\nimport { typeaheadMatch, useTypeahead } from './typeahead';\n\nexport { TYPEAHEAD_TIMEOUT } from './typeahead';\n\n/**\n * Every menu-item role, so an item a caller gives `menuitemcheckbox` or\n * `menuitemradio` (a filter or sort menu) stays reachable.\n */\nexport const MENU_ITEM_SELECTOR =\n '[role=\"menuitem\"], [role=\"menuitemcheckbox\"], [role=\"menuitemradio\"]';\n\n/**\n * Whether a menu item can be reached and activated: it isn't disabled, as a\n * native control or through `aria-disabled=\"true\"`.\n */\nexport function isEnabledMenuItem(item: Element): boolean {\n return (\n !item.hasAttribute('disabled') &&\n item.getAttribute('aria-disabled') !== 'true'\n );\n}\n\n/** Whether `node` is one of `menu`'s items, enabled or not. */\nconst isItemOf = (menu: Element, node: Element | null): boolean =>\n Boolean(node && menu.contains(node) && node.matches(MENU_ITEM_SELECTOR));\n\n/** The items in `menu` the keys reach, in document order. */\nexport function enabledMenuItems(menu: Element): HTMLElement[] {\n return Array.from(\n menu.querySelectorAll<HTMLElement>(MENU_ITEM_SELECTOR)\n ).filter(isEnabledMenuItem);\n}\n\n/**\n * Whether the browser activates `item` on this key by itself: a `<button>`\n * answers Enter and Space, and a link with an `href` answers Enter.\n */\nfunction activatesItself(item: HTMLElement, key: string): boolean {\n if (item.tagName === 'BUTTON') return true;\n return key === 'Enter' && item.tagName === 'A' && item.hasAttribute('href');\n}\n\n/** What `useMenuKeys` returns. */\nexport interface MenuKeys {\n /** The `keydown` handler for the `role=\"menu\"` element. */\n onKeyDown: (event: KeyboardEvent<HTMLElement>) => void;\n /**\n * Forgets what has been typed, so the next character starts a new search.\n * Call it when the menu opens, so a search typed just before it last\n * closed doesn't carry over.\n */\n reset: () => void;\n}\n\n/**\n * Moves through and activates the items of a `role=\"menu\"` element from the\n * keyboard. Put the `onKeyDown` it returns on that element. It reads the\n * items from the element each time, so items that come and go are found,\n * and it renders nothing.\n *\n * It acts only while focus is on the menu element itself or on one of its\n * items, so anything else focusable inside the menu, such as a filter\n * field, keeps its own keys.\n *\n * - Down and Up move to the next and previous enabled item, wrapping, and\n * Home and End go to the first and last. From no item, Down goes to the\n * first and Up to the last.\n * - Enter and Space activate the focused item. A `<button>`, and a link with\n * an `href` for Enter, get the browser's own click, and anything else gets\n * one from here, so its `onClick` runs either way. A key held down\n * activates it once. A key handler on the item that called\n * `preventDefault()` has claimed the key, and nothing else runs it.\n * - A printable character moves to the next enabled item whose text starts\n * with what has been typed. Characters typed within `TYPEAHEAD_TIMEOUT` of\n * each other add up to one search, until `reset` starts a new one.\n *\n * Items are `MENU_ITEM_SELECTOR` matches, and disabled ones are skipped by\n * all of the above. Escape and Tab are left to the menu's owner, since what\n * closing means is up to it, and so is any other key.\n *\n * @function useMenuKeys\n * @returns The `keydown` handler for the menu element, and a way to forget\n * what has been typed.\n */\nexport function useMenuKeys(): MenuKeys {\n const typeahead = useTypeahead();\n\n return useMemo<MenuKeys>(() => {\n const onKeyDown = (event: KeyboardEvent<HTMLElement>) => {\n const menu = event.currentTarget;\n const focused = getActiveElementInTree(menu);\n if (focused !== menu && !isItemOf(menu, focused)) return;\n const items = enabledMenuItems(menu);\n if (!items.length) return;\n const current = items.indexOf(focused as HTMLElement);\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n items[current >= 0 ? (current + 1) % items.length : 0].focus();\n return;\n case 'ArrowUp':\n event.preventDefault();\n items[\n current >= 0\n ? (current - 1 + items.length) % items.length\n : items.length - 1\n ].focus();\n return;\n case 'Home':\n event.preventDefault();\n items[0].focus();\n return;\n case 'End':\n event.preventDefault();\n items[items.length - 1].focus();\n return;\n case 'Enter':\n case ' ': {\n if (current < 0 || event.defaultPrevented) return;\n const item = items[current];\n if (activatesItself(item, event.key)) {\n // The browser clicks on Enter's keydown, and a held key sends one\n // per auto-repeat. Space clicks on keyup, once however long it's\n // held.\n if (event.repeat && event.key === 'Enter') event.preventDefault();\n return;\n }\n // Space would scroll the page, and a held key repeats.\n event.preventDefault();\n if (!event.repeat) item.click();\n return;\n }\n default:\n break;\n }\n\n if (\n event.key.length !== 1 ||\n event.ctrlKey ||\n event.metaKey ||\n event.altKey ||\n event.defaultPrevented\n ) {\n return;\n }\n event.preventDefault();\n typeaheadMatch(items, typeahead.type(event.key), current)?.focus();\n };\n return { onKeyDown, reset: typeahead.reset };\n }, [typeahead]);\n}\n"],"names":[],"mappings":"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"}
|