@astryxdesign/core 0.4.7 → 0.5.0-canary.32a62fd
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +161 -1
- package/dist/AppShell/AppShell.d.ts.map +1 -1
- package/dist/AppShell/AppShell.js +3 -2
- package/dist/AspectRatio/AspectRatio.d.ts +24 -0
- package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
- package/dist/AspectRatio/AspectRatio.js +23 -4
- package/dist/Avatar/Avatar.d.ts +25 -1
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +58 -19
- package/dist/Avatar/index.d.ts +2 -2
- package/dist/Avatar/index.d.ts.map +1 -1
- package/dist/Avatar/index.js +1 -1
- package/dist/AvatarGroup/AvatarGroup.d.ts +9 -2
- package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroup.js +8 -4
- package/dist/AvatarGroup/AvatarGroupContext.d.ts +2 -1
- package/dist/AvatarGroup/AvatarGroupContext.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.js +10 -6
- package/dist/Banner/Banner.d.ts +40 -15
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +51 -18
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheet.js +17 -4
- package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetSwitcher.js +3 -2
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +11 -10
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +5 -4
- package/dist/ButtonGroup/ButtonGroup.d.ts +5 -1
- package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
- package/dist/ButtonGroup/ButtonGroup.js +26 -7
- package/dist/Calendar/Calendar.d.ts +3 -1
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +13 -14
- package/dist/Calendar/getInitialFocusDate.d.ts +46 -0
- package/dist/Calendar/getInitialFocusDate.d.ts.map +1 -0
- package/dist/Calendar/getInitialFocusDate.js +62 -0
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +3 -3
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +2 -3
- package/dist/Chat/ChatLayout.d.ts.map +1 -1
- package/dist/Chat/ChatLayout.js +3 -3
- package/dist/Chat/ChatMessageList.d.ts +22 -3
- package/dist/Chat/ChatMessageList.d.ts.map +1 -1
- package/dist/Chat/ChatMessageList.js +6 -3
- package/dist/Chat/ChatTokenizedText.d.ts +2 -0
- package/dist/Chat/ChatTokenizedText.d.ts.map +1 -1
- package/dist/Chat/ChatTokenizedText.js +11 -6
- package/dist/Chat/useChatComposerTokens.js +1 -1
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +10 -3
- package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
- package/dist/ClickableCard/ClickableCard.js +3 -3
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +15 -4
- package/dist/CodeBlock/highlightRanges.js +1 -1
- package/dist/CodeBlock/highlightStyles.js +13 -13
- package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteInput.js +3 -2
- package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteItem.js +3 -2
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +4 -3
- package/dist/DateInput/DateInput.d.ts +43 -0
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +22 -2
- package/dist/DateInput/NativeDateField.d.ts +10 -0
- package/dist/DateInput/NativeDateField.d.ts.map +1 -0
- package/dist/DateInput/NativeDateField.js +477 -0
- package/dist/DateInput/TouchDateField.d.ts.map +1 -1
- package/dist/DateInput/TouchDateField.js +38 -4
- package/dist/DateInput/index.d.ts +1 -1
- package/dist/DateInput/index.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts +21 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +406 -12
- package/dist/DateTimeInput/index.d.ts +1 -1
- package/dist/DateTimeInput/index.d.ts.map +1 -1
- package/dist/Dialog/Dialog.d.ts +7 -1
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +89 -58
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.js +6 -5
- package/dist/Field/FieldLabel.d.ts.map +1 -1
- package/dist/Field/FieldLabel.js +9 -1
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +3 -2
- package/dist/Heading/Heading.d.ts.map +1 -1
- package/dist/Heading/Heading.js +6 -2
- package/dist/HoverCard/HoverCard.d.ts +6 -0
- package/dist/HoverCard/HoverCard.d.ts.map +1 -1
- package/dist/HoverCard/HoverCard.js +52 -3
- package/dist/HoverCard/useHoverCard.d.ts +16 -2
- package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +74 -26
- package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
- package/dist/Indicator/CheckboxIndicator.js +6 -2
- package/dist/Item/Item.d.ts.map +1 -1
- package/dist/Item/Item.js +4 -2
- package/dist/Kbd/Kbd.d.ts.map +1 -1
- package/dist/Kbd/Kbd.js +2 -19
- package/dist/Layer/LayerDepthContext.d.ts +52 -0
- package/dist/Layer/LayerDepthContext.d.ts.map +1 -0
- package/dist/Layer/LayerDepthContext.js +66 -0
- package/dist/Layer/index.d.ts +3 -0
- package/dist/Layer/index.d.ts.map +1 -1
- package/dist/Layer/index.js +4 -0
- package/dist/Layer/layerStack.d.ts +98 -0
- package/dist/Layer/layerStack.d.ts.map +1 -0
- package/dist/Layer/layerStack.js +306 -0
- package/dist/Layer/useLayerDismissal.d.ts +93 -0
- package/dist/Layer/useLayerDismissal.d.ts.map +1 -0
- package/dist/Layer/useLayerDismissal.js +91 -0
- package/dist/Lightbox/Lightbox.d.ts +1 -0
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +103 -81
- package/dist/Markdown/index.d.ts +1 -1
- package/dist/Markdown/index.d.ts.map +1 -1
- package/dist/Markdown/parser.d.ts +38 -1
- package/dist/Markdown/parser.d.ts.map +1 -1
- package/dist/Markdown/parser.js +177 -29
- package/dist/MobileNav/MobileNav.d.ts +6 -1
- package/dist/MobileNav/MobileNav.d.ts.map +1 -1
- package/dist/MobileNav/MobileNav.js +61 -36
- package/dist/MultiSelector/MultiSelector.d.ts +15 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +17 -6
- package/dist/MultiSelector/index.d.ts +1 -1
- package/dist/MultiSelector/index.d.ts.map +1 -1
- package/dist/NavIcon/NavIcon.d.ts.map +1 -1
- package/dist/NavIcon/NavIcon.js +5 -1
- package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
- package/dist/NavMenu/NavHeadingMenu.js +3 -2
- package/dist/NumberInput/NumberInput.d.ts +1 -0
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +27 -37
- package/dist/NumberInput/numberParser.d.ts +43 -0
- package/dist/NumberInput/numberParser.d.ts.map +1 -0
- package/dist/NumberInput/numberParser.js +293 -0
- package/dist/Outline/Outline.d.ts.map +1 -1
- package/dist/Outline/Outline.js +3 -2
- package/dist/OverflowList/OverflowList.d.ts.map +1 -1
- package/dist/OverflowList/OverflowList.js +3 -2
- package/dist/Overlay/Overlay.d.ts.map +1 -1
- package/dist/Overlay/Overlay.js +3 -2
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +20 -17
- package/dist/PowerSearch/PowerSearch.d.ts +8 -2
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +19 -6
- package/dist/PowerSearch/PowerSearchEditPopover.d.ts +3 -1
- package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchEditPopover.js +28 -6
- package/dist/PowerSearch/PowerSearchValueEditor.d.ts +3 -1
- package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchValueEditor.js +19 -9
- package/dist/PowerSearch/types.d.ts +2 -0
- package/dist/PowerSearch/types.d.ts.map +1 -1
- package/dist/PowerSearch/usePowerSearchSource.d.ts +6 -1
- package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
- package/dist/PowerSearch/usePowerSearchSource.js +14 -5
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +18 -5
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +3 -2
- package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControl.js +3 -2
- package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
- package/dist/SelectableCard/SelectableCard.js +3 -3
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +5 -0
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +4 -3
- package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
- package/dist/SideNav/SideNavHeading.js +8 -7
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +4 -3
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +3 -3
- package/dist/Spinner/Spinner.d.ts +3 -3
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +130 -78
- package/dist/StatusDot/StatusDot.d.ts.map +1 -1
- package/dist/StatusDot/StatusDot.js +6 -1
- package/dist/Stepper/Step.d.ts +140 -0
- package/dist/Stepper/Step.d.ts.map +1 -0
- package/dist/Stepper/Step.js +1045 -0
- package/dist/Stepper/StepStatus.d.ts +24 -0
- package/dist/Stepper/StepStatus.d.ts.map +1 -0
- package/dist/Stepper/StepStatus.js +1 -0
- package/dist/Stepper/Stepper.d.ts +93 -0
- package/dist/Stepper/Stepper.d.ts.map +1 -0
- package/dist/Stepper/Stepper.js +184 -0
- package/dist/Stepper/StepperContext.d.ts +41 -0
- package/dist/Stepper/StepperContext.d.ts.map +1 -0
- package/dist/Stepper/StepperContext.js +34 -0
- package/dist/Stepper/index.d.ts +8 -0
- package/dist/Stepper/index.d.ts.map +1 -0
- package/dist/Stepper/index.js +7 -0
- package/dist/Stepper/stepper.stylex.d.ts +15 -0
- package/dist/Stepper/stepper.stylex.d.ts.map +1 -0
- package/dist/Stepper/stepper.stylex.js +20 -0
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +8 -3
- package/dist/TabList/Tab.d.ts +15 -2
- package/dist/TabList/Tab.d.ts.map +1 -1
- package/dist/TabList/Tab.js +32 -7
- package/dist/TabList/TabList.d.ts +46 -7
- package/dist/TabList/TabList.d.ts.map +1 -1
- package/dist/TabList/TabList.js +353 -23
- package/dist/TabList/TabListContext.d.ts +11 -1
- package/dist/TabList/TabListContext.d.ts.map +1 -1
- package/dist/TabList/TabListContext.js +11 -2
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +3 -2
- package/dist/TabList/index.d.ts +2 -2
- package/dist/TabList/index.d.ts.map +1 -1
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +10 -2
- package/dist/Table/Table.d.ts.map +1 -1
- package/dist/Table/Table.js +3 -6
- package/dist/Table/TableRow.d.ts +16 -5
- package/dist/Table/TableRow.d.ts.map +1 -1
- package/dist/Table/TableRow.js +16 -5
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +20 -8
- package/dist/Table/plugins/pagination/useTablePagination.d.ts +3 -0
- package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
- package/dist/Table/plugins/pagination/useTablePagination.js +21 -4
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +7 -2
- package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -1
- package/dist/Table/plugins/rowStatus/useTableRowStatus.js +10 -3
- package/dist/Table/plugins/selection/useTableSelection.d.ts +16 -0
- package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
- package/dist/Table/plugins/selection/useTableSelection.js +19 -5
- package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
- package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +3 -4
- package/dist/Table/types.d.ts +22 -4
- package/dist/Table/types.d.ts.map +1 -1
- package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
- package/dist/Table/useBaseTablePlugins.js +5 -0
- package/dist/Text/Text.d.ts.map +1 -1
- package/dist/Text/Text.js +6 -2
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +9 -3
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +3 -2
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +3 -2
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +5 -3
- package/dist/Token/TokenLink.d.ts.map +1 -1
- package/dist/Token/TokenLink.js +2 -2
- package/dist/Tokenizer/Tokenizer.d.ts +3 -1
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +2 -0
- package/dist/Tooltip/Tooltip.d.ts +5 -0
- package/dist/Tooltip/Tooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.d.ts +5 -0
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +39 -24
- package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
- package/dist/TopNav/TopNavHeading.js +5 -4
- package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenu.js +3 -3
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +4 -3
- package/dist/Typeahead/BaseTypeahead.d.ts +2 -0
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +68 -29
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +3 -2
- package/dist/astryx.css +94 -7
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useFocusTrap.d.ts +7 -4
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +60 -95
- package/dist/hooks/useHotkeys.d.ts.map +1 -1
- package/dist/hooks/useHotkeys.js +3 -18
- package/dist/hooks/useMergedRefs.d.ts +18 -0
- package/dist/hooks/useMergedRefs.d.ts.map +1 -0
- package/dist/hooks/useMergedRefs.js +25 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
- package/dist/theme/derivedVarRegistry.js +24 -4
- package/dist/theme/syntax/tokens.d.ts +1 -1
- package/dist/theme/syntax/tokens.d.ts.map +1 -1
- package/dist/theme/syntax/tokens.js +5 -2
- package/dist/utils/groupItems.d.ts +5 -1
- package/dist/utils/groupItems.d.ts.map +1 -1
- package/dist/utils/groupItems.js +9 -9
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/isApplePlatform.d.ts +18 -0
- package/dist/utils/isApplePlatform.d.ts.map +1 -0
- package/dist/utils/isApplePlatform.js +37 -0
- package/dist/utils/sharedResizeObserver.d.ts.map +1 -1
- package/dist/utils/sharedResizeObserver.js +10 -1
- package/dist/utils/timeParser.d.ts +1 -1
- package/dist/utils/timeParser.d.ts.map +1 -1
- package/dist/utils/timeParser.js +26 -12
- package/locales/en.json +31 -7
- package/locales/pseudo.json +18 -0
- package/package.json +8 -3
- package/src/AppShell/AppShell.tsx +3 -2
- package/src/AspectRatio/AspectRatio.doc.mjs +6 -3
- package/src/AspectRatio/AspectRatio.test.tsx +57 -10
- package/src/AspectRatio/AspectRatio.tsx +40 -1
- package/src/Avatar/Avatar.doc.mjs +14 -3
- package/src/Avatar/Avatar.test.tsx +97 -0
- package/src/Avatar/Avatar.tsx +125 -52
- package/src/Avatar/index.ts +2 -2
- package/src/AvatarGroup/AvatarGroup.doc.mjs +8 -2
- package/src/AvatarGroup/AvatarGroup.test.tsx +24 -0
- package/src/AvatarGroup/AvatarGroup.tsx +15 -6
- package/src/AvatarGroup/AvatarGroupContext.ts +5 -3
- package/src/AvatarGroup/AvatarGroupOverflow.tsx +10 -4
- package/src/Banner/Banner.doc.mjs +13 -11
- package/src/Banner/Banner.test.tsx +102 -15
- package/src/Banner/Banner.tsx +72 -25
- package/src/BottomSheet/BottomSheet.test.tsx +123 -0
- package/src/BottomSheet/BottomSheet.tsx +17 -4
- package/src/BottomSheet/BottomSheetSwitcher.tsx +3 -2
- package/src/Breadcrumbs/BreadcrumbItem.tsx +9 -5
- package/src/Button/Button.doc.mjs +22 -0
- package/src/Button/Button.test.tsx +13 -0
- package/src/Button/Button.tsx +5 -4
- package/src/ButtonGroup/ButtonGroup.doc.mjs +10 -1
- package/src/ButtonGroup/ButtonGroup.test.tsx +391 -1
- package/src/ButtonGroup/ButtonGroup.tsx +23 -5
- package/src/Calendar/Calendar.doc.mjs +4 -3
- package/src/Calendar/Calendar.test.tsx +52 -0
- package/src/Calendar/Calendar.tsx +18 -15
- package/src/Calendar/getInitialFocusDate.test.ts +94 -0
- package/src/Calendar/getInitialFocusDate.ts +93 -0
- package/src/Carousel/Carousel.tsx +3 -2
- package/src/Chat/ChatComposerInput.tsx +5 -1
- package/src/Chat/ChatLayout.tsx +3 -2
- package/src/Chat/ChatMessageList.doc.mjs +9 -1
- package/src/Chat/ChatMessageList.test.tsx +46 -0
- package/src/Chat/ChatMessageList.tsx +28 -4
- package/src/Chat/ChatTokenizedText.tsx +17 -10
- package/src/Chat/useChatComposerTokens.ts +1 -1
- package/src/CheckboxInput/CheckboxInput.doc.mjs +2 -0
- package/src/CheckboxInput/CheckboxInput.test.tsx +15 -0
- package/src/CheckboxInput/CheckboxInput.tsx +8 -2
- package/src/ClickableCard/ClickableCard.tsx +3 -2
- package/src/CodeBlock/CodeBlock.doc.mjs +16 -4
- package/src/CodeBlock/CodeBlock.test.tsx +37 -0
- package/src/CodeBlock/CodeBlock.tsx +20 -4
- package/src/CodeBlock/highlightRanges.ts +1 -1
- package/src/CodeBlock/highlightStyles.ts +13 -13
- package/src/CommandPalette/CommandPaletteInput.tsx +3 -2
- package/src/CommandPalette/CommandPaletteItem.tsx +3 -2
- package/src/ContextMenu/ContextMenu.doc.mjs +5 -0
- package/src/ContextMenu/ContextMenu.tsx +4 -3
- package/src/DateInput/DateInput.doc.mjs +16 -0
- package/src/DateInput/DateInput.tsx +55 -2
- package/src/DateInput/DateInputTouch.test.tsx +68 -9
- package/src/DateInput/NativeDateField.test.tsx +565 -0
- package/src/DateInput/NativeDateField.tsx +623 -0
- package/src/DateInput/TouchDateField.tsx +38 -4
- package/src/DateInput/index.ts +1 -0
- package/src/DateTimeInput/DateTimeInput.doc.mjs +18 -0
- package/src/DateTimeInput/DateTimeInput.test.tsx +1088 -0
- package/src/DateTimeInput/DateTimeInput.tsx +556 -24
- package/src/DateTimeInput/index.ts +1 -0
- package/src/Dialog/Dialog.doc.mjs +8 -3
- package/src/Dialog/Dialog.test.tsx +261 -1
- package/src/Dialog/Dialog.tsx +109 -58
- package/src/DropdownMenu/DropdownMenu.doc.mjs +6 -0
- package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +8 -5
- package/src/DropdownMenu/DropdownMenuSubMenu.tsx +8 -11
- package/src/Field/Field.doc.mjs +13 -2
- package/src/Field/FieldLabel.test.tsx +29 -0
- package/src/Field/FieldLabel.tsx +7 -0
- package/src/FileInput/FileInput.tsx +3 -2
- package/src/Heading/Heading.test.tsx +23 -1
- package/src/Heading/Heading.tsx +6 -2
- package/src/HoverCard/HoverCard.doc.mjs +13 -2
- package/src/HoverCard/HoverCard.test.tsx +101 -4
- package/src/HoverCard/HoverCard.tsx +71 -3
- package/src/HoverCard/useHoverCard.tsx +101 -33
- package/src/Indicator/CheckboxIndicator.tsx +14 -8
- package/src/Indicator/Indicator.doc.mjs +2 -0
- package/src/Item/Item.tsx +5 -4
- package/src/Kbd/Kbd.tsx +2 -19
- package/src/Layer/LayerDepthContext.tsx +62 -0
- package/src/Layer/index.ts +9 -0
- package/src/Layer/layerDismissalFamilies.test.tsx +359 -0
- package/src/Layer/layerDismissalInvariants.test.tsx +417 -0
- package/src/Layer/layerStack.ts +362 -0
- package/src/Layer/useLayerDismissal.test.tsx +469 -0
- package/src/Layer/useLayerDismissal.ts +173 -0
- package/src/Layout/Layout.doc.mjs +2 -2
- package/src/Lightbox/Lightbox.doc.mjs +12 -0
- package/src/Lightbox/Lightbox.tsx +124 -102
- package/src/Link/Link.doc.mjs +39 -0
- package/src/Markdown/Markdown.doc.mjs +5 -0
- package/src/Markdown/incremental.test.ts +16 -2
- package/src/Markdown/index.ts +1 -0
- package/src/Markdown/parser.perf.test.ts +96 -1
- package/src/Markdown/parser.test.ts +145 -2
- package/src/Markdown/parser.ts +244 -29
- package/src/MobileNav/MobileNav.tsx +62 -36
- package/src/MoreMenu/MoreMenu.doc.mjs +7 -1
- package/src/MultiSelector/MultiSelector.doc.mjs +27 -0
- package/src/MultiSelector/MultiSelector.test.tsx +106 -0
- package/src/MultiSelector/MultiSelector.tsx +50 -6
- package/src/MultiSelector/index.ts +1 -0
- package/src/NavIcon/NavIcon.doc.mjs +8 -2
- package/src/NavIcon/NavIcon.test.tsx +9 -0
- package/src/NavIcon/NavIcon.tsx +5 -1
- package/src/NavMenu/NavHeadingMenu.tsx +3 -2
- package/src/NumberInput/NumberInput.doc.mjs +20 -0
- package/src/NumberInput/NumberInput.tsx +28 -29
- package/src/NumberInput/numberParser.test.ts +137 -0
- package/src/NumberInput/numberParser.ts +346 -0
- package/src/Outline/Outline.tsx +3 -2
- package/src/OverflowList/OverflowList.tsx +3 -2
- package/src/Overlay/Overlay.tsx +3 -2
- package/src/Popover/usePopover.tsx +31 -28
- package/src/PowerSearch/PowerSearch.doc.mjs +160 -28
- package/src/PowerSearch/PowerSearch.test.tsx +240 -1
- package/src/PowerSearch/PowerSearch.tsx +34 -5
- package/src/PowerSearch/PowerSearchEditPopover.test.tsx +201 -0
- package/src/PowerSearch/PowerSearchEditPopover.tsx +29 -2
- package/src/PowerSearch/PowerSearchValueEditor.test.tsx +47 -1
- package/src/PowerSearch/PowerSearchValueEditor.tsx +15 -0
- package/src/PowerSearch/types.ts +2 -0
- package/src/PowerSearch/usePowerSearchSource.test.ts +82 -2
- package/src/PowerSearch/usePowerSearchSource.ts +13 -3
- package/src/ProgressBar/ProgressBar.doc.mjs +42 -6
- package/src/ProgressBar/ProgressBar.test.tsx +24 -0
- package/src/ProgressBar/ProgressBar.tsx +28 -8
- package/src/Resizable/ResizeHandle.tsx +3 -2
- package/src/SegmentedControl/SegmentedControl.tsx +3 -2
- package/src/SelectableCard/SelectableCard.tsx +3 -2
- package/src/Selector/Selector.doc.mjs +12 -1
- package/src/Selector/Selector.test.tsx +45 -1
- package/src/Selector/Selector.tsx +11 -1
- package/src/SideNav/SideNav.doc.mjs +6 -6
- package/src/SideNav/SideNav.tsx +4 -2
- package/src/SideNav/SideNavHeading.tsx +12 -12
- package/src/SideNav/SideNavItem.tsx +4 -2
- package/src/Slider/Slider.tsx +3 -2
- package/src/Spinner/Spinner.test.tsx +147 -1
- package/src/Spinner/Spinner.tsx +120 -101
- package/src/StatusDot/StatusDot.doc.mjs +8 -2
- package/src/StatusDot/StatusDot.test.tsx +9 -0
- package/src/StatusDot/StatusDot.tsx +7 -1
- package/src/Stepper/Step.doc.mjs +98 -0
- package/src/Stepper/Step.tsx +1638 -0
- package/src/Stepper/StepStatus.ts +26 -0
- package/src/Stepper/Stepper.doc.mjs +370 -0
- package/src/Stepper/Stepper.test.tsx +1024 -0
- package/src/Stepper/Stepper.tsx +258 -0
- package/src/Stepper/StepperContext.ts +71 -0
- package/src/Stepper/index.ts +18 -0
- package/src/Stepper/stepper.stylex.ts +19 -0
- package/src/Switch/Switch.doc.mjs +2 -0
- package/src/Switch/Switch.test.tsx +10 -0
- package/src/Switch/Switch.tsx +6 -2
- package/src/TabList/Tab.doc.mjs +13 -1
- package/src/TabList/Tab.tsx +67 -7
- package/src/TabList/TabList.doc.mjs +22 -6
- package/src/TabList/TabList.test.tsx +559 -0
- package/src/TabList/TabList.tsx +490 -22
- package/src/TabList/TabListContext.ts +13 -2
- package/src/TabList/TabMenu.tsx +3 -2
- package/src/TabList/index.ts +6 -2
- package/src/Table/BaseTable.tsx +12 -4
- package/src/Table/Table.doc.mjs +9 -2
- package/src/Table/Table.test.tsx +132 -14
- package/src/Table/Table.tsx +3 -8
- package/src/Table/TableBody.doc.mjs +40 -0
- package/src/Table/TableFooter.doc.mjs +40 -0
- package/src/Table/TableHeader.doc.mjs +40 -0
- package/src/Table/TableRow.tsx +16 -5
- package/src/Table/plugins/groupedRows/useTableGroupedRows-perf.test.tsx +112 -0
- package/src/Table/plugins/groupedRows/useTableGroupedRows.test.tsx +100 -0
- package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +17 -8
- package/src/Table/plugins/pagination/useTablePagination.test.tsx +34 -4
- package/src/Table/plugins/pagination/useTablePagination.tsx +35 -3
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +9 -2
- package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +13 -7
- package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +12 -3
- package/src/Table/plugins/selection/useTableSelection.test.tsx +76 -0
- package/src/Table/plugins/selection/useTableSelection.tsx +40 -7
- package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +1 -2
- package/src/Table/types.ts +22 -4
- package/src/Table/useBaseTablePlugins.ts +5 -0
- package/src/Table/useTableGroupedRows.doc.mjs +5 -4
- package/src/Table/useTablePagination.doc.mjs +7 -0
- package/src/Table/useTableRowStatus.doc.mjs +1 -1
- package/src/Table/useTableSelection.doc.mjs +31 -0
- package/src/Text/Text.test.tsx +23 -1
- package/src/Text/Text.tsx +6 -2
- package/src/TextArea/TextArea.doc.mjs +8 -2
- package/src/TextArea/TextArea.test.tsx +11 -0
- package/src/TextArea/TextArea.tsx +15 -8
- package/src/TextInput/TextInput.doc.mjs +10 -0
- package/src/TextInput/TextInput.tsx +3 -2
- package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
- package/src/TimeInput/TimeInput.tsx +2 -2
- package/src/Timestamp/Timestamp.tsx +4 -2
- package/src/Toast/Toast.doc.mjs +6 -0
- package/src/Token/TokenLink.tsx +2 -2
- package/src/Tokenizer/Tokenizer.doc.mjs +27 -0
- package/src/Tokenizer/Tokenizer.tsx +4 -0
- package/src/Toolbar/Toolbar.doc.mjs +5 -0
- package/src/Tooltip/Tooltip.doc.mjs +5 -0
- package/src/Tooltip/Tooltip.test.tsx +32 -0
- package/src/Tooltip/Tooltip.tsx +5 -0
- package/src/Tooltip/useTooltip.tsx +47 -24
- package/src/TopNav/TopNavHeading.tsx +5 -4
- package/src/TopNav/TopNavMegaMenu.tsx +3 -2
- package/src/TopNav/TopNavMenu.tsx +4 -3
- package/src/Typeahead/BaseTypeahead.doc.mjs +11 -0
- package/src/Typeahead/BaseTypeahead.tsx +85 -33
- package/src/Typeahead/Typeahead.doc.mjs +5 -0
- package/src/Typeahead/Typeahead.test.tsx +49 -0
- package/src/Typeahead/Typeahead.tsx +3 -2
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useContainerReveal.doc.mjs +2 -2
- package/src/hooks/useFocusTrap.test.tsx +14 -0
- package/src/hooks/useFocusTrap.ts +62 -111
- package/src/hooks/useFocusTrapEscapeShim.test.tsx +165 -0
- package/src/hooks/useHotkeys.ts +3 -18
- package/src/hooks/useMergedRefs.doc.mjs +79 -0
- package/src/hooks/useMergedRefs.test.tsx +62 -0
- package/src/hooks/useMergedRefs.ts +36 -0
- package/src/index.ts +1 -0
- package/src/theme/MediaTheme.doc.mjs +5 -5
- package/src/theme/derivedVarRegistry.test.ts +19 -3
- package/src/theme/derivedVarRegistry.ts +23 -4
- package/src/theme/generateThemeRules.test.ts +37 -0
- package/src/theme/syntax/tokens.ts +5 -2
- package/src/utils/groupItems.ts +12 -6
- package/src/utils/index.ts +1 -1
- package/src/utils/isApplePlatform.test.ts +61 -0
- package/src/utils/isApplePlatform.ts +37 -0
- package/src/utils/sharedResizeObserver.ts +10 -2
- package/src/utils/timeParser.test.ts +6 -1
- package/src/utils/timeParser.ts +35 -13
- package/src/__tests__/inputWidthContract.test.tsx +0 -69
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file useLayerDismissal.test.tsx
|
|
5
|
+
* @input Uses @testing-library/react, vitest
|
|
6
|
+
* @output Tests for the shared layer dismissal stack
|
|
7
|
+
* @position Colocated tests for useLayerDismissal + layerStack
|
|
8
|
+
*
|
|
9
|
+
* Every case here is "one Escape press, which layers reacted?" — the single
|
|
10
|
+
* question the stack exists to answer.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {render, fireEvent} from '@testing-library/react';
|
|
14
|
+
import {describe, expect, it, vi, afterEach} from 'vitest';
|
|
15
|
+
import {StrictMode, useRef} from 'react';
|
|
16
|
+
|
|
17
|
+
import {LayerDepthProvider} from './LayerDepthContext';
|
|
18
|
+
import {useLayerDismissal} from './useLayerDismissal';
|
|
19
|
+
import {resetLayerStackForTests} from './layerStack';
|
|
20
|
+
import type {LayerEscapeBehavior} from './layerStack';
|
|
21
|
+
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
resetLayerStackForTests();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/** A layer that registers itself and renders its children one level deeper. */
|
|
27
|
+
function Layer({
|
|
28
|
+
onDismiss,
|
|
29
|
+
behavior = 'close',
|
|
30
|
+
isEnabled = true,
|
|
31
|
+
isActive = true,
|
|
32
|
+
isPresent,
|
|
33
|
+
children,
|
|
34
|
+
}: {
|
|
35
|
+
onDismiss: () => void;
|
|
36
|
+
behavior?: LayerEscapeBehavior;
|
|
37
|
+
isEnabled?: boolean;
|
|
38
|
+
isActive?: boolean;
|
|
39
|
+
isPresent?: () => boolean;
|
|
40
|
+
children?: React.ReactNode;
|
|
41
|
+
}) {
|
|
42
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
43
|
+
useLayerDismissal({
|
|
44
|
+
isActive,
|
|
45
|
+
onDismiss,
|
|
46
|
+
escapeBehavior: behavior,
|
|
47
|
+
isEnabled,
|
|
48
|
+
isPresent,
|
|
49
|
+
getContainer: () => containerRef.current,
|
|
50
|
+
});
|
|
51
|
+
return (
|
|
52
|
+
<div ref={containerRef}>
|
|
53
|
+
<LayerDepthProvider>{children}</LayerDepthProvider>
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** A layer that reports no depth — like a bare focus trap, which renders nothing. */
|
|
59
|
+
function FlatLayer({
|
|
60
|
+
onDismiss,
|
|
61
|
+
children,
|
|
62
|
+
}: {
|
|
63
|
+
onDismiss: () => void;
|
|
64
|
+
children?: React.ReactNode;
|
|
65
|
+
}) {
|
|
66
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
67
|
+
useLayerDismissal({
|
|
68
|
+
isActive: true,
|
|
69
|
+
onDismiss,
|
|
70
|
+
getContainer: () => containerRef.current,
|
|
71
|
+
});
|
|
72
|
+
return <div ref={containerRef}>{children}</div>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const pressEscape = () => fireEvent.keyDown(document, {key: 'Escape'});
|
|
76
|
+
|
|
77
|
+
describe('useLayerDismissal', () => {
|
|
78
|
+
it('dismisses only the top-most layer when both open in the same commit', () => {
|
|
79
|
+
// The regression: an inner and outer layer that mount together. React runs
|
|
80
|
+
// child effects first, so registration order reports the inner layer as the
|
|
81
|
+
// OLDER one — nesting, not order, has to decide.
|
|
82
|
+
const outer = vi.fn();
|
|
83
|
+
const inner = vi.fn();
|
|
84
|
+
|
|
85
|
+
render(
|
|
86
|
+
<Layer onDismiss={outer}>
|
|
87
|
+
<Layer onDismiss={inner} />
|
|
88
|
+
</Layer>,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
pressEscape();
|
|
92
|
+
expect(inner).toHaveBeenCalledTimes(1);
|
|
93
|
+
expect(outer).not.toHaveBeenCalled();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('falls through to the outer layer once the inner one closes', () => {
|
|
97
|
+
const outer = vi.fn();
|
|
98
|
+
const inner = vi.fn();
|
|
99
|
+
const {rerender} = render(
|
|
100
|
+
<Layer onDismiss={outer}>
|
|
101
|
+
<Layer onDismiss={inner} />
|
|
102
|
+
</Layer>,
|
|
103
|
+
);
|
|
104
|
+
rerender(<Layer onDismiss={outer} />);
|
|
105
|
+
|
|
106
|
+
pressEscape();
|
|
107
|
+
expect(outer).toHaveBeenCalledTimes(1);
|
|
108
|
+
expect(inner).not.toHaveBeenCalled();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('dismisses the later of two unrelated layers', () => {
|
|
112
|
+
const first = vi.fn();
|
|
113
|
+
const second = vi.fn();
|
|
114
|
+
const {rerender} = render(<Layer onDismiss={first} />);
|
|
115
|
+
rerender(
|
|
116
|
+
<>
|
|
117
|
+
<Layer onDismiss={first} />
|
|
118
|
+
<Layer onDismiss={second} />
|
|
119
|
+
</>,
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
pressEscape();
|
|
123
|
+
expect(second).toHaveBeenCalledTimes(1);
|
|
124
|
+
expect(first).not.toHaveBeenCalled();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('resolves nesting by DOM containment when the tree reports equal depth', () => {
|
|
128
|
+
// A bare focus trap renders nothing, so it cannot push a depth provider.
|
|
129
|
+
// Containment is the only nesting signal those layers have.
|
|
130
|
+
const outer = vi.fn();
|
|
131
|
+
const inner = vi.fn();
|
|
132
|
+
render(
|
|
133
|
+
<FlatLayer onDismiss={outer}>
|
|
134
|
+
<FlatLayer onDismiss={inner} />
|
|
135
|
+
</FlatLayer>,
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
pressEscape();
|
|
139
|
+
expect(inner).toHaveBeenCalledTimes(1);
|
|
140
|
+
expect(outer).not.toHaveBeenCalled();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe('escapeBehavior', () => {
|
|
144
|
+
it("'close' consumes the press, so one Escape affects exactly one layer", () => {
|
|
145
|
+
// A hover tip inside a modal: the tip is on top, so it takes the press
|
|
146
|
+
// and the modal stays open. A second Escape closes the modal.
|
|
147
|
+
const modal = vi.fn();
|
|
148
|
+
const tip = vi.fn();
|
|
149
|
+
render(
|
|
150
|
+
<Layer onDismiss={modal}>
|
|
151
|
+
<Layer onDismiss={tip} />
|
|
152
|
+
</Layer>,
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
pressEscape();
|
|
156
|
+
expect(tip).toHaveBeenCalledTimes(1);
|
|
157
|
+
expect(modal).not.toHaveBeenCalled();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("'block' consumes the press without dismissing anything", () => {
|
|
161
|
+
const host = vi.fn();
|
|
162
|
+
const required = vi.fn();
|
|
163
|
+
render(
|
|
164
|
+
<Layer onDismiss={host}>
|
|
165
|
+
<Layer onDismiss={required} behavior="block" />
|
|
166
|
+
</Layer>,
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
pressEscape();
|
|
170
|
+
expect(required).not.toHaveBeenCalled();
|
|
171
|
+
expect(host).not.toHaveBeenCalled();
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
describe('registration order', () => {
|
|
176
|
+
/** Two unrelated same-depth layers; the older one's behavior is a prop. */
|
|
177
|
+
function Siblings({
|
|
178
|
+
olderBehavior,
|
|
179
|
+
onOlderDismiss,
|
|
180
|
+
onNewerDismiss,
|
|
181
|
+
}: {
|
|
182
|
+
olderBehavior: LayerEscapeBehavior;
|
|
183
|
+
onOlderDismiss: () => void;
|
|
184
|
+
onNewerDismiss: () => void;
|
|
185
|
+
}) {
|
|
186
|
+
return (
|
|
187
|
+
<>
|
|
188
|
+
<Layer onDismiss={onOlderDismiss} behavior={olderBehavior} />
|
|
189
|
+
<Layer onDismiss={onNewerDismiss} />
|
|
190
|
+
</>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
it('keeps a layer below the ones that opened after it when its escapeBehavior changes', () => {
|
|
195
|
+
// A Dialog whose `purpose` flips from required to info while it is open
|
|
196
|
+
// re-registers with the stack. Re-registration must not reorder it above
|
|
197
|
+
// a layer opened on top of it.
|
|
198
|
+
const older = vi.fn();
|
|
199
|
+
const newer = vi.fn();
|
|
200
|
+
const {rerender} = render(
|
|
201
|
+
<Siblings
|
|
202
|
+
olderBehavior="block"
|
|
203
|
+
onOlderDismiss={older}
|
|
204
|
+
onNewerDismiss={newer}
|
|
205
|
+
/>,
|
|
206
|
+
);
|
|
207
|
+
rerender(
|
|
208
|
+
<Siblings
|
|
209
|
+
olderBehavior="close"
|
|
210
|
+
onOlderDismiss={older}
|
|
211
|
+
onNewerDismiss={newer}
|
|
212
|
+
/>,
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
pressEscape();
|
|
216
|
+
expect(newer).toHaveBeenCalledTimes(1);
|
|
217
|
+
expect(older).not.toHaveBeenCalled();
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('does not let a re-registered layer swallow a press meant for the layer above it', () => {
|
|
221
|
+
// The same flip the other way: info to required. A `block` layer that
|
|
222
|
+
// jumped the queue would consume the press and nothing would close.
|
|
223
|
+
const older = vi.fn();
|
|
224
|
+
const newer = vi.fn();
|
|
225
|
+
const {rerender} = render(
|
|
226
|
+
<Siblings
|
|
227
|
+
olderBehavior="close"
|
|
228
|
+
onOlderDismiss={older}
|
|
229
|
+
onNewerDismiss={newer}
|
|
230
|
+
/>,
|
|
231
|
+
);
|
|
232
|
+
rerender(
|
|
233
|
+
<Siblings
|
|
234
|
+
olderBehavior="block"
|
|
235
|
+
onOlderDismiss={older}
|
|
236
|
+
onNewerDismiss={newer}
|
|
237
|
+
/>,
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
pressEscape();
|
|
241
|
+
expect(newer).toHaveBeenCalledTimes(1);
|
|
242
|
+
expect(older).not.toHaveBeenCalled();
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('keeps ordering under StrictMode, which mounts every effect twice', () => {
|
|
246
|
+
const first = vi.fn();
|
|
247
|
+
const second = vi.fn();
|
|
248
|
+
render(
|
|
249
|
+
<StrictMode>
|
|
250
|
+
<Layer onDismiss={first} />
|
|
251
|
+
<Layer onDismiss={second} />
|
|
252
|
+
</StrictMode>,
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
pressEscape();
|
|
256
|
+
expect(second).toHaveBeenCalledTimes(1);
|
|
257
|
+
expect(first).not.toHaveBeenCalled();
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
describe('presence', () => {
|
|
262
|
+
it('skips a registered layer that is not on screen', () => {
|
|
263
|
+
// Hover layers stay registered for their lifetime because their open
|
|
264
|
+
// state lags the DOM. An absent one must not claim the press — that is
|
|
265
|
+
// exactly the bug where a HoverCard trigger ate Escapes while idle.
|
|
266
|
+
const below = vi.fn();
|
|
267
|
+
const absent = vi.fn();
|
|
268
|
+
render(
|
|
269
|
+
<Layer onDismiss={below}>
|
|
270
|
+
<Layer onDismiss={absent} isPresent={() => false} />
|
|
271
|
+
</Layer>,
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
pressEscape();
|
|
275
|
+
expect(absent).not.toHaveBeenCalled();
|
|
276
|
+
expect(below).toHaveBeenCalledTimes(1);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('lets a present layer claim the press over the one beneath', () => {
|
|
280
|
+
const below = vi.fn();
|
|
281
|
+
const present = vi.fn();
|
|
282
|
+
render(
|
|
283
|
+
<Layer onDismiss={below}>
|
|
284
|
+
<Layer onDismiss={present} isPresent={() => true} />
|
|
285
|
+
</Layer>,
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
pressEscape();
|
|
289
|
+
expect(present).toHaveBeenCalledTimes(1);
|
|
290
|
+
expect(below).not.toHaveBeenCalled();
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
describe('opting out', () => {
|
|
295
|
+
it('skips a disabled layer entirely, so the press reaches the one below', () => {
|
|
296
|
+
const below = vi.fn();
|
|
297
|
+
const optedOut = vi.fn();
|
|
298
|
+
render(
|
|
299
|
+
<Layer onDismiss={below}>
|
|
300
|
+
<Layer onDismiss={optedOut} isEnabled={false} />
|
|
301
|
+
</Layer>,
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
pressEscape();
|
|
305
|
+
expect(optedOut).not.toHaveBeenCalled();
|
|
306
|
+
expect(below).toHaveBeenCalledTimes(1);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it('does not register an inactive layer', () => {
|
|
310
|
+
const onDismiss = vi.fn();
|
|
311
|
+
render(<Layer onDismiss={onDismiss} isActive={false} />);
|
|
312
|
+
|
|
313
|
+
pressEscape();
|
|
314
|
+
expect(onDismiss).not.toHaveBeenCalled();
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
describe('deferring to content', () => {
|
|
319
|
+
it('stands down when content already handled the press', () => {
|
|
320
|
+
// preventDefault from inside the layer — an editor claiming Escape for
|
|
321
|
+
// its own find widget, for instance.
|
|
322
|
+
const onDismiss = vi.fn();
|
|
323
|
+
render(
|
|
324
|
+
<Layer onDismiss={onDismiss}>
|
|
325
|
+
<button
|
|
326
|
+
type="button"
|
|
327
|
+
data-testid="editor"
|
|
328
|
+
onKeyDown={e => {
|
|
329
|
+
if (e.key === 'Escape') {
|
|
330
|
+
e.preventDefault();
|
|
331
|
+
}
|
|
332
|
+
}}
|
|
333
|
+
/>
|
|
334
|
+
</Layer>,
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
fireEvent.keyDown(document.querySelector('[data-testid="editor"]')!, {
|
|
338
|
+
key: 'Escape',
|
|
339
|
+
});
|
|
340
|
+
expect(onDismiss).not.toHaveBeenCalled();
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it('ignores Escape that is cancelling an IME composition', () => {
|
|
344
|
+
const onDismiss = vi.fn();
|
|
345
|
+
render(<Layer onDismiss={onDismiss} />);
|
|
346
|
+
|
|
347
|
+
fireEvent.keyDown(document, {key: 'Escape', isComposing: true});
|
|
348
|
+
expect(onDismiss).not.toHaveBeenCalled();
|
|
349
|
+
|
|
350
|
+
fireEvent.keyDown(document, {key: 'Escape', keyCode: 229});
|
|
351
|
+
expect(onDismiss).not.toHaveBeenCalled();
|
|
352
|
+
|
|
353
|
+
pressEscape();
|
|
354
|
+
expect(onDismiss).toHaveBeenCalledTimes(1);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it('claims the composing Escape so the browser raises no close request', () => {
|
|
358
|
+
// Not dismissing is only half of it. An unclaimed Escape lets the browser
|
|
359
|
+
// raise its own close request, which reaches the layer's `cancel` handler
|
|
360
|
+
// and dismisses it on the same keypress — so the guard has to swallow the
|
|
361
|
+
// press, not merely skip the dismissal.
|
|
362
|
+
render(<Layer onDismiss={vi.fn()} />);
|
|
363
|
+
const event = new KeyboardEvent('keydown', {
|
|
364
|
+
key: 'Escape',
|
|
365
|
+
bubbles: true,
|
|
366
|
+
cancelable: true,
|
|
367
|
+
});
|
|
368
|
+
Object.defineProperty(event, 'isComposing', {value: true});
|
|
369
|
+
|
|
370
|
+
document.dispatchEvent(event);
|
|
371
|
+
expect(event.defaultPrevented).toBe(true);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('leaves a composing Escape alone when no layer is on screen', () => {
|
|
375
|
+
// With nothing to protect the press is not ours to take: an idle hover
|
|
376
|
+
// layer in the tree must not suppress the page's own close watcher.
|
|
377
|
+
render(<Layer onDismiss={vi.fn()} isPresent={() => false} />);
|
|
378
|
+
const event = new KeyboardEvent('keydown', {
|
|
379
|
+
key: 'Escape',
|
|
380
|
+
bubbles: true,
|
|
381
|
+
cancelable: true,
|
|
382
|
+
});
|
|
383
|
+
Object.defineProperty(event, 'isComposing', {value: true});
|
|
384
|
+
|
|
385
|
+
document.dispatchEvent(event);
|
|
386
|
+
expect(event.defaultPrevented).toBe(false);
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
describe('close requests the browser starts itself', () => {
|
|
391
|
+
// The Android back gesture and the platform close watcher arrive as a
|
|
392
|
+
// `cancel` with no keydown to read, so the keydown guard cannot see them.
|
|
393
|
+
function CloseRequestLayer({onAsk}: {onAsk: (answer: boolean) => void}) {
|
|
394
|
+
const {shouldDismissOnCloseRequest} = useLayerDismissal({
|
|
395
|
+
isActive: true,
|
|
396
|
+
onDismiss: () => {},
|
|
397
|
+
});
|
|
398
|
+
return (
|
|
399
|
+
<>
|
|
400
|
+
<input aria-label="composing field" />
|
|
401
|
+
<button
|
|
402
|
+
type="button"
|
|
403
|
+
onClick={() => onAsk(shouldDismissOnCloseRequest())}>
|
|
404
|
+
ask
|
|
405
|
+
</button>
|
|
406
|
+
</>
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const ask = (onAsk: ReturnType<typeof vi.fn>): boolean => {
|
|
411
|
+
fireEvent.click(document.querySelector('button')!);
|
|
412
|
+
return Boolean(onAsk.mock.lastCall?.[0]);
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
it('declines while an IME composition is running, and again once it ends', () => {
|
|
416
|
+
const onAsk = vi.fn();
|
|
417
|
+
render(<CloseRequestLayer onAsk={onAsk} />);
|
|
418
|
+
const field = document.querySelector('input')!;
|
|
419
|
+
|
|
420
|
+
expect(ask(onAsk)).toBe(true);
|
|
421
|
+
|
|
422
|
+
fireEvent.compositionStart(field);
|
|
423
|
+
expect(ask(onAsk)).toBe(false);
|
|
424
|
+
|
|
425
|
+
fireEvent.compositionEnd(field);
|
|
426
|
+
expect(ask(onAsk)).toBe(true);
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
it('stops declining when focus leaves a field mid-composition', () => {
|
|
430
|
+
// A compositionend the stack never sees would otherwise leave the flag
|
|
431
|
+
// stuck on, and every later back gesture would be swallowed.
|
|
432
|
+
const onAsk = vi.fn();
|
|
433
|
+
render(<CloseRequestLayer onAsk={onAsk} />);
|
|
434
|
+
const field = document.querySelector('input')!;
|
|
435
|
+
|
|
436
|
+
fireEvent.compositionStart(field);
|
|
437
|
+
expect(ask(onAsk)).toBe(false);
|
|
438
|
+
|
|
439
|
+
fireEvent.blur(field);
|
|
440
|
+
expect(ask(onAsk)).toBe(true);
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it('leaves the event alone when no layer is open', () => {
|
|
445
|
+
// Nothing registered: the browser keeps its own Escape behavior (exiting
|
|
446
|
+
// fullscreen, closing a native picker).
|
|
447
|
+
render(<div />);
|
|
448
|
+
const event = new KeyboardEvent('keydown', {
|
|
449
|
+
key: 'Escape',
|
|
450
|
+
bubbles: true,
|
|
451
|
+
cancelable: true,
|
|
452
|
+
});
|
|
453
|
+
document.dispatchEvent(event);
|
|
454
|
+
expect(event.defaultPrevented).toBe(false);
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
it('claims the press it handles so the browser does not act too', () => {
|
|
458
|
+
// preventDefault is what stops the native close-watcher dismissing a second
|
|
459
|
+
// layer behind the stack's back.
|
|
460
|
+
render(<Layer onDismiss={vi.fn()} />);
|
|
461
|
+
const event = new KeyboardEvent('keydown', {
|
|
462
|
+
key: 'Escape',
|
|
463
|
+
bubbles: true,
|
|
464
|
+
cancelable: true,
|
|
465
|
+
});
|
|
466
|
+
document.dispatchEvent(event);
|
|
467
|
+
expect(event.defaultPrevented).toBe(true);
|
|
468
|
+
});
|
|
469
|
+
});
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file useLayerDismissal.ts
|
|
7
|
+
* @input Uses React hooks, the layer stack, and LayerDepthContext
|
|
8
|
+
* @output Exports useLayerDismissal + its option/return types
|
|
9
|
+
* @position The API overlays use to join the shared dismissal stack. Wraps
|
|
10
|
+
* layerStack (registration + the single Escape listener) and reads nesting
|
|
11
|
+
* depth from LayerDepthContext.
|
|
12
|
+
*
|
|
13
|
+
* SYNC: When modified, update:
|
|
14
|
+
* - /packages/core/src/Layer/index.ts
|
|
15
|
+
* - /packages/core/src/Layer/useLayerDismissal.test.tsx
|
|
16
|
+
* - /packages/core/src/Layer/layerStack.ts
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import {useCallback, useEffect, useRef} from 'react';
|
|
20
|
+
|
|
21
|
+
import {useLayerDepth} from './LayerDepthContext';
|
|
22
|
+
import {
|
|
23
|
+
isTextComposing,
|
|
24
|
+
isTopmostLayer,
|
|
25
|
+
registerLayer,
|
|
26
|
+
type LayerEscapeBehavior,
|
|
27
|
+
} from './layerStack';
|
|
28
|
+
|
|
29
|
+
export type {LayerEscapeBehavior};
|
|
30
|
+
|
|
31
|
+
export interface UseLayerDismissalOptions {
|
|
32
|
+
/**
|
|
33
|
+
* Whether this layer is participating right now. Usually the layer's open
|
|
34
|
+
* state. A layer whose open state lags the DOM should pass `true` for its
|
|
35
|
+
* lifetime and answer `isPresent` from the DOM instead.
|
|
36
|
+
*/
|
|
37
|
+
isActive: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Dismiss this layer. Called by the stack when this layer is the one that
|
|
40
|
+
* should respond to an Escape press. Not called for `escapeBehavior: 'block'`.
|
|
41
|
+
*/
|
|
42
|
+
onDismiss: () => void;
|
|
43
|
+
/**
|
|
44
|
+
* What this layer does with an Escape press that reaches it.
|
|
45
|
+
* @default 'close'
|
|
46
|
+
*/
|
|
47
|
+
escapeBehavior?: LayerEscapeBehavior;
|
|
48
|
+
/**
|
|
49
|
+
* This layer's container element, read lazily. Optional: supply it when the
|
|
50
|
+
* layer cannot wrap its content in `LayerDepthProvider` (a bare focus trap
|
|
51
|
+
* renders nothing), so nesting can still be recovered from the DOM.
|
|
52
|
+
*/
|
|
53
|
+
getContainer?: () => HTMLElement | null;
|
|
54
|
+
/**
|
|
55
|
+
* Whether the layer is really on screen, asked at press time. Supply it when
|
|
56
|
+
* `isActive` is `true` for the layer's lifetime because its open state lags
|
|
57
|
+
* the DOM by a frame; the stack skips layers that answer `false`.
|
|
58
|
+
*/
|
|
59
|
+
isPresent?: () => boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Whether this layer takes part in the shared stack at all. When `false` the
|
|
62
|
+
* layer is invisible to dismissal: a press flows past it to the layer below,
|
|
63
|
+
* exactly as if it were not open.
|
|
64
|
+
*
|
|
65
|
+
* Deliberately separate from `escapeBehavior: 'block'` — `'block'` is a layer
|
|
66
|
+
* that is present and swallows the press; this is a layer that is not there
|
|
67
|
+
* at all. One case uses it: `Dialog`'s inline rendering mode, where content
|
|
68
|
+
* sits in normal flow with nothing layered over anything.
|
|
69
|
+
*
|
|
70
|
+
* A controlled layer is NOT one of these. It stays registered and takes the
|
|
71
|
+
* press like any other, answering it by calling the consumer's change
|
|
72
|
+
* handler; whether it then closes is the consumer's decision.
|
|
73
|
+
*
|
|
74
|
+
* Layers that are never Escape-dismissible (toasts) should simply not call
|
|
75
|
+
* this hook.
|
|
76
|
+
*
|
|
77
|
+
* @default true
|
|
78
|
+
*/
|
|
79
|
+
isEnabled?: boolean;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface UseLayerDismissalReturn {
|
|
83
|
+
/**
|
|
84
|
+
* Whether a close request the browser started on its own — a `<dialog>`'s
|
|
85
|
+
* `cancel`, the Android back gesture, the platform close watcher — should
|
|
86
|
+
* dismiss this layer. As well as the top-most rule the stack applies to an
|
|
87
|
+
* Escape press, it declines a request that arrives while an IME composition
|
|
88
|
+
* is running, which no `cancel` handler can tell on its own because the
|
|
89
|
+
* event carries no composition state.
|
|
90
|
+
*/
|
|
91
|
+
shouldDismissOnCloseRequest: () => boolean;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Join the shared layer dismissal stack for as long as this layer is active.
|
|
96
|
+
*
|
|
97
|
+
* The layer does NOT attach a key listener — the stack owns one listener and
|
|
98
|
+
* routes each Escape press to the top-most REGISTERED layer, so one press
|
|
99
|
+
* dismisses exactly one of them. Dialog (and what is built on it), Popover and
|
|
100
|
+
* the menus built on it, Tooltip, HoverCard, Lightbox and MobileNav register
|
|
101
|
+
* today; `BottomSheetSwitcher` registers through its focus trap when modal and
|
|
102
|
+
* still handles its own press when not.
|
|
103
|
+
*
|
|
104
|
+
* `BottomSheet`, `CommandPalette`, `ContextMenu`, `DropdownMenuSubMenu`,
|
|
105
|
+
* `PowerSearchEditPopover` and lab's `Drawer` still run their own Escape
|
|
106
|
+
* listener. They stay safe next to the stack only because each claims the press
|
|
107
|
+
* at element level, and the stack stands down on an already-`defaultPrevented`
|
|
108
|
+
* press — but a registered layer opened INSIDE one of them does not get the
|
|
109
|
+
* press: the host takes it and closes instead. Migrating them is the fix.
|
|
110
|
+
*
|
|
111
|
+
* Wrap the layer's own content in `LayerDepthProvider` so anything opened from
|
|
112
|
+
* inside it registers as nested.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```tsx
|
|
116
|
+
* useLayerDismissal({
|
|
117
|
+
* isActive: isOpen,
|
|
118
|
+
* onDismiss: () => onOpenChange(false),
|
|
119
|
+
* });
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
export function useLayerDismissal(
|
|
123
|
+
options: UseLayerDismissalOptions,
|
|
124
|
+
): UseLayerDismissalReturn {
|
|
125
|
+
const {
|
|
126
|
+
isActive,
|
|
127
|
+
onDismiss,
|
|
128
|
+
escapeBehavior = 'close',
|
|
129
|
+
getContainer,
|
|
130
|
+
isPresent,
|
|
131
|
+
isEnabled = true,
|
|
132
|
+
} = options;
|
|
133
|
+
|
|
134
|
+
const depth = useLayerDepth();
|
|
135
|
+
|
|
136
|
+
// Identity for this layer's stack entry, stable across re-registration.
|
|
137
|
+
const tokenRef = useRef<object>({});
|
|
138
|
+
|
|
139
|
+
// The stack calls these during an event, outside React's render, so they must
|
|
140
|
+
// reach the latest closures rather than the ones captured at registration.
|
|
141
|
+
const onDismissRef = useRef(onDismiss);
|
|
142
|
+
const getContainerRef = useRef(getContainer);
|
|
143
|
+
const isPresentRef = useRef(isPresent);
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
onDismissRef.current = onDismiss;
|
|
146
|
+
getContainerRef.current = getContainer;
|
|
147
|
+
isPresentRef.current = isPresent;
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const isRegistered = isActive && isEnabled;
|
|
151
|
+
|
|
152
|
+
useEffect(() => {
|
|
153
|
+
if (!isRegistered) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
return registerLayer({
|
|
157
|
+
token: tokenRef.current,
|
|
158
|
+
depth,
|
|
159
|
+
behavior: escapeBehavior,
|
|
160
|
+
getContainer: () => getContainerRef.current?.() ?? null,
|
|
161
|
+
isPresent: () => isPresentRef.current?.() ?? true,
|
|
162
|
+
dismiss: () => onDismissRef.current(),
|
|
163
|
+
});
|
|
164
|
+
}, [isRegistered, depth, escapeBehavior]);
|
|
165
|
+
|
|
166
|
+
const shouldDismissOnCloseRequest = useCallback(
|
|
167
|
+
() =>
|
|
168
|
+
isRegistered && !isTextComposing() && isTopmostLayer(tokenRef.current),
|
|
169
|
+
[isRegistered],
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
return {shouldDismissOnCloseRequest};
|
|
173
|
+
}
|
|
@@ -95,9 +95,9 @@ export const docs = {
|
|
|
95
95
|
},
|
|
96
96
|
{
|
|
97
97
|
name: 'contentWidth',
|
|
98
|
-
type: '
|
|
98
|
+
type: 'SizeValue',
|
|
99
99
|
description:
|
|
100
|
-
'Maximum width of the content within each slot (header, content, footer, panels), centered when narrower than the available space. Dividers stay full-bleed. Numbers are pixels, strings are used as-is (e.g. `60ch`). Common page widths: 640 for forms, settings, and text-focused pages; 960 for content pages and wider layouts.',
|
|
100
|
+
'Maximum width of the content within each slot (header, content, footer, panels), centered when narrower than the available space. Dividers stay full-bleed. Numbers are treated as pixels, strings are used as-is (e.g. `60ch`). Common page widths: 640 for forms, settings, and text-focused pages; 960 for content pages and wider layouts.',
|
|
101
101
|
},
|
|
102
102
|
{
|
|
103
103
|
name: 'padding',
|
|
@@ -47,6 +47,18 @@ export const docs = {
|
|
|
47
47
|
description: 'Enable zoom on double-click, or Enter/Space/+/- via keyboard (images only). When zoomed, drag or use arrow keys to pan.',
|
|
48
48
|
default: 'false',
|
|
49
49
|
},
|
|
50
|
+
{
|
|
51
|
+
name: 'defaultIndex',
|
|
52
|
+
type: 'number',
|
|
53
|
+
description: 'Initial image index in gallery mode for uncontrolled usage.',
|
|
54
|
+
default: '0',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: 'hasAutoPlay',
|
|
58
|
+
type: 'boolean',
|
|
59
|
+
description: 'Automatically start video playback when a video media item is shown.',
|
|
60
|
+
default: 'false',
|
|
61
|
+
},
|
|
50
62
|
{
|
|
51
63
|
name: 'xstyle',
|
|
52
64
|
type: 'StyleXStyles',
|