@astryxdesign/core 0.7.5-canary.fccd771 → 0.7.5
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 +211 -10
- package/README.md +15 -12
- package/dist/AlertDialog/AlertDialog.js +1 -1
- package/dist/AppShell/AppShell.d.ts +0 -22
- package/dist/AppShell/AppShell.d.ts.map +1 -1
- package/dist/AppShell/AppShell.js +155 -1
- package/dist/Avatar/AvatarStatusDot.js +2 -2
- package/dist/Badge/Badge.d.ts.map +1 -1
- package/dist/Badge/Badge.js +18 -2
- package/dist/Banner/Banner.d.ts +1 -0
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +64 -9
- package/dist/BottomSheet/BottomSheet.d.ts +53 -5
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheet.js +435 -198
- package/dist/BottomSheet/BottomSheetHostContext.d.ts +8 -0
- package/dist/BottomSheet/BottomSheetHostContext.d.ts.map +1 -0
- package/dist/BottomSheet/BottomSheetHostContext.js +35 -0
- package/dist/BottomSheet/BottomSheetPanel.d.ts +9 -1
- package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetPanel.js +77 -23
- package/dist/BottomSheet/BottomSheetSwitcher.d.ts +53 -14
- package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetSwitcher.js +229 -27
- package/dist/BottomSheet/BottomSheetSwitcherContext.d.ts +6 -0
- package/dist/BottomSheet/BottomSheetSwitcherContext.d.ts.map +1 -1
- package/dist/BottomSheet/bottomSheetEscape.d.ts +3 -0
- package/dist/BottomSheet/bottomSheetEscape.d.ts.map +1 -0
- package/dist/BottomSheet/bottomSheetEscape.js +20 -0
- package/dist/BottomSheet/bottomSheetStack.d.ts +262 -0
- package/dist/BottomSheet/bottomSheetStack.d.ts.map +1 -0
- package/dist/BottomSheet/bottomSheetStack.js +1675 -0
- package/dist/BottomSheet/useSheetGestures.d.ts +19 -3
- package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
- package/dist/BottomSheet/useSheetGestures.js +43 -14
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +36 -3
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +18 -7
- package/dist/Chat/ChatComposer.d.ts.map +1 -1
- package/dist/Chat/ChatComposer.js +25 -32
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +55 -16
- package/dist/Chat/ChatMessageMetadata.js +2 -2
- package/dist/Chat/ChatPastedTextToken.d.ts.map +1 -1
- package/dist/Chat/ChatPastedTextToken.js +7 -6
- package/dist/Chat/ChatToolCalls.js +4 -4
- package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
- package/dist/ClickableCard/ClickableCard.js +10 -6
- package/dist/CodeBlock/CodeBlock.d.ts +25 -2
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +58 -19
- package/dist/CodeBlock/grammar.d.ts +134 -0
- package/dist/CodeBlock/grammar.d.ts.map +1 -0
- package/dist/CodeBlock/grammar.js +219 -0
- package/dist/CodeBlock/index.d.ts +2 -0
- package/dist/CodeBlock/index.d.ts.map +1 -1
- package/dist/CodeBlock/index.js +1 -0
- package/dist/CodeBlock/languages/dockerfile.d.ts +3 -0
- package/dist/CodeBlock/languages/dockerfile.d.ts.map +1 -0
- package/dist/CodeBlock/languages/dockerfile.js +45 -0
- package/dist/CodeBlock/languages/graphql.d.ts +3 -0
- package/dist/CodeBlock/languages/graphql.d.ts.map +1 -0
- package/dist/CodeBlock/languages/graphql.js +27 -0
- package/dist/CodeBlock/languages/java.d.ts +3 -0
- package/dist/CodeBlock/languages/java.d.ts.map +1 -0
- package/dist/CodeBlock/languages/java.js +24 -0
- package/dist/CodeBlock/languages/lua.d.ts +3 -0
- package/dist/CodeBlock/languages/lua.d.ts.map +1 -0
- package/dist/CodeBlock/languages/lua.js +21 -0
- package/dist/CodeBlock/languages/objective-c.d.ts +3 -0
- package/dist/CodeBlock/languages/objective-c.d.ts.map +1 -0
- package/dist/CodeBlock/languages/objective-c.js +27 -0
- package/dist/CodeBlock/languages/powershell.d.ts +3 -0
- package/dist/CodeBlock/languages/powershell.d.ts.map +1 -0
- package/dist/CodeBlock/languages/powershell.js +30 -0
- package/dist/CodeBlock/languages/ruby.d.ts +3 -0
- package/dist/CodeBlock/languages/ruby.d.ts.map +1 -0
- package/dist/CodeBlock/languages/ruby.js +24 -0
- package/dist/CodeBlock/languages/thrift.d.ts +3 -0
- package/dist/CodeBlock/languages/thrift.d.ts.map +1 -0
- package/dist/CodeBlock/languages/thrift.js +18 -0
- package/dist/CodeBlock/languages/toml.d.ts +3 -0
- package/dist/CodeBlock/languages/toml.d.ts.map +1 -0
- package/dist/CodeBlock/languages/toml.js +53 -0
- package/dist/CodeBlock/tokenizer.d.ts +8 -4
- package/dist/CodeBlock/tokenizer.d.ts.map +1 -1
- package/dist/CodeBlock/tokenizer.js +158 -8
- package/dist/Collapsible/Collapsible.d.ts +8 -1
- package/dist/Collapsible/Collapsible.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.js +84 -2
- package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPalette.js +15 -1
- package/dist/CommandPalette/CommandPaletteEmpty.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteEmpty.js +1 -0
- package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteInput.js +14 -2
- package/dist/CommandPalette/busyStartSettle.d.ts +4 -0
- package/dist/CommandPalette/busyStartSettle.d.ts.map +1 -0
- package/dist/CommandPalette/busyStartSettle.js +23 -0
- package/dist/ComplexSelector/ComplexSelector.d.ts +20 -2
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +42 -7
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +48 -13
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +12 -5
- package/dist/DateInput/NativeDateField.d.ts.map +1 -1
- package/dist/DateInput/NativeDateField.js +2 -1
- package/dist/DateInput/TouchDateField.d.ts.map +1 -1
- package/dist/DateInput/TouchDateField.js +2 -1
- package/dist/DateInput/Wheel.d.ts.map +1 -1
- package/dist/DateInput/Wheel.js +1 -1
- package/dist/Dialog/Dialog.d.ts +4 -0
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +20 -8
- package/dist/Drawer/Drawer.d.ts.map +1 -1
- package/dist/Drawer/Drawer.js +18 -13
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +34 -30
- package/dist/DropdownMenu/DropdownMenuGroup.d.ts +7 -4
- package/dist/DropdownMenu/DropdownMenuGroup.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuGroup.js +10 -4
- package/dist/DropdownMenu/DropdownMenuItem.js +3 -3
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +10 -5
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioItem.js +13 -5
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +6 -6
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.js +0 -3
- package/dist/DropdownMenu/MenuBottomSheet.d.ts +11 -4
- package/dist/DropdownMenu/MenuBottomSheet.d.ts.map +1 -1
- package/dist/DropdownMenu/MenuBottomSheet.js +35 -9
- package/dist/DropdownMenu/MenuBottomSheetActionList.js +3 -3
- package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts +2 -0
- package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts.map +1 -1
- package/dist/DropdownMenu/lazyMenuBottomSheet.js +25 -3
- package/dist/DropdownMenu/menuBottomSheet.stylex.d.ts +23 -0
- package/dist/DropdownMenu/menuBottomSheet.stylex.d.ts.map +1 -0
- package/dist/DropdownMenu/menuBottomSheet.stylex.js +29 -0
- package/dist/DropdownMenu/menuItemHover.d.ts +3 -0
- package/dist/DropdownMenu/menuItemHover.d.ts.map +1 -1
- package/dist/DropdownMenu/menuItemHover.js +14 -1
- package/dist/DropdownMenu/menuTouchAction.stylex.d.ts +25 -0
- package/dist/DropdownMenu/menuTouchAction.stylex.d.ts.map +1 -0
- package/dist/DropdownMenu/menuTouchAction.stylex.js +29 -0
- package/dist/DropdownMenu/useMenuDrillIn.d.ts.map +1 -1
- package/dist/DropdownMenu/useMenuDrillIn.js +7 -5
- package/dist/EmptyState/EmptyState.d.ts +4 -2
- package/dist/EmptyState/EmptyState.d.ts.map +1 -1
- package/dist/EmptyState/EmptyState.js +6 -4
- package/dist/Field/Field.js +1 -1
- package/dist/Field/FieldLabel.js +1 -1
- package/dist/Field/PanelSearchInput.d.ts +9 -1
- package/dist/Field/PanelSearchInput.d.ts.map +1 -1
- package/dist/Field/PanelSearchInput.js +31 -22
- package/dist/Field/inputDisabledFocus.d.ts +14 -0
- package/dist/Field/inputDisabledFocus.d.ts.map +1 -0
- package/dist/Field/inputDisabledFocus.js +19 -0
- package/dist/Field/inputStyles.stylex.d.ts +9 -9
- package/dist/Field/inputStyles.stylex.js +9 -9
- package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
- package/dist/FieldStatus/FieldStatus.js +7 -3
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +22 -12
- package/dist/Icon/Icon.d.ts +37 -5
- package/dist/Icon/Icon.d.ts.map +1 -1
- package/dist/Icon/Icon.js +65 -11
- package/dist/Icon/IconSize.stylex.d.ts +16 -11
- package/dist/Icon/IconSize.stylex.d.ts.map +1 -1
- package/dist/Icon/IconSize.stylex.js +27 -11
- package/dist/Icon/globalIconRegistry.d.ts +71 -4
- package/dist/Icon/globalIconRegistry.d.ts.map +1 -1
- package/dist/Icon/globalIconRegistry.js +163 -23
- package/dist/Icon/index.d.ts +26 -2
- package/dist/Icon/index.d.ts.map +1 -1
- package/dist/Icon/index.js +24 -1
- package/dist/Icon/useIcon.d.ts +5 -1
- package/dist/Icon/useIcon.d.ts.map +1 -1
- package/dist/Icon/useIcon.js +5 -1
- package/dist/InputGroup/InputGroup.d.ts +1 -1
- package/dist/InputGroup/InputGroup.d.ts.map +1 -1
- package/dist/InputGroup/InputGroup.js +46 -36
- package/dist/InputGroup/InputGroupInternalContext.d.ts +4 -0
- package/dist/InputGroup/InputGroupInternalContext.d.ts.map +1 -0
- package/dist/InputGroup/InputGroupInternalContext.js +15 -0
- package/dist/InputGroup/InputGroupText.d.ts.map +1 -1
- package/dist/InputGroup/InputGroupText.js +2 -0
- package/dist/InputGroup/groupStyles.d.ts +9 -0
- package/dist/InputGroup/groupStyles.d.ts.map +1 -1
- package/dist/InputGroup/groupStyles.js +28 -14
- package/dist/Item/Item.d.ts +5 -1
- package/dist/Item/Item.d.ts.map +1 -1
- package/dist/Item/Item.js +23 -7
- package/dist/Item/ItemMarkerColumns.a11y.chromium.spec.d.ts +2 -0
- package/dist/Item/ItemMarkerColumns.a11y.chromium.spec.d.ts.map +1 -0
- package/dist/Item/ItemMarkerColumns.a11y.chromium.spec.js +132 -0
- package/dist/Item/ItemMarkerColumnsContext.d.ts +14 -0
- package/dist/Item/ItemMarkerColumnsContext.d.ts.map +1 -0
- package/dist/Item/ItemMarkerColumnsContext.js +18 -0
- package/dist/Item/ItemSwipeLayer.d.ts.map +1 -1
- package/dist/Item/ItemSwipeLayer.js +3 -0
- package/dist/Item/useSwipeAction.d.ts.map +1 -1
- package/dist/Item/useSwipeAction.js +60 -5
- package/dist/Kbd/Kbd.d.ts +2 -2
- package/dist/Kbd/Kbd.d.ts.map +1 -1
- package/dist/Kbd/Kbd.js +18 -11
- package/dist/Layer/layerAnimations.stylex.d.ts +9 -5
- package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
- package/dist/Layer/layerAnimations.stylex.js +23 -10
- package/dist/Layer/layerSlideRules.d.ts +62 -2
- package/dist/Layer/layerSlideRules.d.ts.map +1 -1
- package/dist/Layer/layerSlideRules.js +124 -3
- package/dist/Layer/layerStack.d.ts +12 -9
- package/dist/Layer/layerStack.d.ts.map +1 -1
- package/dist/Layer/layerStack.js +116 -53
- package/dist/Layer/modalOutlet.d.ts +44 -6
- package/dist/Layer/modalOutlet.d.ts.map +1 -1
- package/dist/Layer/modalOutlet.js +150 -14
- package/dist/Layer/preserveAnimations.d.ts +11 -0
- package/dist/Layer/preserveAnimations.d.ts.map +1 -0
- package/dist/Layer/preserveAnimations.js +245 -0
- package/dist/Layer/useLayer.d.ts +19 -2
- package/dist/Layer/useLayer.d.ts.map +1 -1
- package/dist/Layer/useLayer.js +531 -44
- package/dist/Layer/useLayerDismissal.d.ts +10 -7
- package/dist/Layer/useLayerDismissal.d.ts.map +1 -1
- package/dist/Layer/useLayerDismissal.js +8 -8
- package/dist/Layer/useTouchTrigger.d.ts +14 -1
- package/dist/Layer/useTouchTrigger.d.ts.map +1 -1
- package/dist/Layer/useTouchTrigger.js +72 -17
- package/dist/Layout/Layout.d.ts.map +1 -1
- package/dist/Layout/Layout.js +4 -0
- package/dist/Layout/LayoutContent.d.ts.map +1 -1
- package/dist/Layout/LayoutContent.js +2 -2
- package/dist/Layout/LayoutFooter.d.ts.map +1 -1
- package/dist/Layout/LayoutFooter.js +2 -1
- package/dist/Layout/LayoutHeader.d.ts.map +1 -1
- package/dist/Layout/LayoutHeader.js +2 -1
- package/dist/Layout/layoutEdges.stylex.d.ts +20 -0
- package/dist/Layout/layoutEdges.stylex.d.ts.map +1 -0
- package/dist/Layout/layoutEdges.stylex.js +28 -0
- package/dist/Lightbox/Lightbox.d.ts +5 -2
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +111 -89
- package/dist/Lightbox/LightboxKeyboard.a11y.browser.spec.d.ts +2 -0
- package/dist/Lightbox/LightboxKeyboard.a11y.browser.spec.d.ts.map +1 -0
- package/dist/Lightbox/LightboxKeyboard.a11y.browser.spec.js +78 -0
- package/dist/Lightbox/LightboxViewport.a11y.browser.spec.d.ts +2 -0
- package/dist/Lightbox/LightboxViewport.a11y.browser.spec.d.ts.map +1 -0
- package/dist/Lightbox/LightboxViewport.a11y.browser.spec.js +65 -0
- package/dist/List/List.d.ts +2 -1
- package/dist/List/List.d.ts.map +1 -1
- package/dist/List/List.js +9 -2
- package/dist/List/ListContext.d.ts +16 -4
- package/dist/List/ListContext.d.ts.map +1 -1
- package/dist/List/ListContext.js +10 -5
- package/dist/List/ListItem.d.ts +7 -1
- package/dist/List/ListItem.d.ts.map +1 -1
- package/dist/List/ListItem.js +52 -8
- package/dist/List/ListMarkers.a11y.chromium.spec.d.ts +2 -0
- package/dist/List/ListMarkers.a11y.chromium.spec.d.ts.map +1 -0
- package/dist/List/ListMarkers.a11y.chromium.spec.js +677 -0
- package/dist/Markdown/Markdown.d.ts +11 -1
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.fr23.bench.js +3 -1
- package/dist/Markdown/Markdown.fr23.sampling.d.ts +16 -0
- package/dist/Markdown/Markdown.fr23.sampling.d.ts.map +1 -1
- package/dist/Markdown/Markdown.fr23.sampling.js +13 -4
- package/dist/Markdown/Markdown.js +41 -14
- package/dist/Markdown/MarkdownTable.a11y.chromium.spec.js +1 -0
- package/dist/MobileNav/MobileNav.d.ts +2 -1
- package/dist/MobileNav/MobileNav.d.ts.map +1 -1
- package/dist/MobileNav/MobileNav.js +46 -37
- package/dist/MobileNav/MobileNavPinchZoom.a11y.chromium.spec.js +1 -0
- package/dist/MobileNav/MobileNavToggle.d.ts +7 -3
- package/dist/MobileNav/MobileNavToggle.d.ts.map +1 -1
- package/dist/MobileNav/MobileNavToggle.js +11 -3
- package/dist/MultiSelector/CreateRow.a11y.chromium.spec.js +53 -6
- package/dist/MultiSelector/MultiSelector.d.ts +1 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.disabledFocus.a11y.chromium.spec.js +1 -0
- package/dist/MultiSelector/MultiSelector.js +21 -5
- package/dist/MultiSelector/OptionActions.a11y.chromium.spec.js +1 -0
- package/dist/NavMenu/NavHeadingMenu.d.ts +1 -1
- package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
- package/dist/NavMenu/NavHeadingMenu.js +29 -5
- package/dist/NavMenu/NavHeadingMenuItem.d.ts.map +1 -1
- package/dist/NavMenu/NavHeadingMenuItem.js +51 -7
- package/dist/NavMenu/NavMenuContext.d.ts +9 -0
- package/dist/NavMenu/NavMenuContext.d.ts.map +1 -1
- package/dist/NavMenu/NavMenuContext.js +9 -0
- package/dist/NumberInput/NumberInput.d.ts +4 -4
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +61 -24
- package/dist/Overlay/Overlay.d.ts +3 -3
- package/dist/Overlay/Overlay.d.ts.map +1 -1
- package/dist/Overlay/Overlay.js +12 -5
- package/dist/Popover/Popover.d.ts +41 -2
- package/dist/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +276 -44
- package/dist/Popover/index.d.ts +1 -1
- package/dist/Popover/index.d.ts.map +1 -1
- package/dist/Popover/lazyPopoverBottomSheet.d.ts +23 -0
- package/dist/Popover/lazyPopoverBottomSheet.d.ts.map +1 -0
- package/dist/Popover/lazyPopoverBottomSheet.js +40 -0
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +11 -5
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +7 -5
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +8 -0
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +10 -2
- package/dist/ScrollableArea/ScrollableArea.d.ts.map +1 -1
- package/dist/ScrollableArea/ScrollableArea.js +1 -1
- package/dist/SegmentedControl/SegmentedControl.d.ts +1 -1
- package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControl.js +6 -2
- package/dist/SelectableCard/SelectableCard.d.ts +5 -5
- package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
- package/dist/SelectableCard/SelectableCard.js +15 -11
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +27 -8
- package/dist/Selector/hooks.d.ts +52 -6
- package/dist/Selector/hooks.d.ts.map +1 -1
- package/dist/Selector/hooks.js +145 -2
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +58 -2
- package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
- package/dist/SideNav/SideNavHeading.js +79 -3
- package/dist/SideNav/SideNavItem.d.ts +5 -1
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +36 -13
- package/dist/SideNav/railMotion.d.ts +54 -0
- package/dist/SideNav/railMotion.d.ts.map +1 -0
- package/dist/SideNav/railMotion.js +253 -0
- package/dist/Skeleton/Skeleton.d.ts.map +1 -1
- package/dist/Skeleton/Skeleton.js +12 -3
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +52 -15
- package/dist/StatusDot/StatusDot.d.ts.map +1 -1
- package/dist/StatusDot/StatusDot.js +12 -0
- package/dist/Stepper/Step.d.ts.map +1 -1
- package/dist/Stepper/Step.js +10 -6
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +5 -5
- package/dist/Switch/switch.markers.stylex.d.ts +7 -2
- package/dist/Switch/switch.markers.stylex.d.ts.map +1 -1
- package/dist/Switch/switch.markers.stylex.js +8 -3
- package/dist/TabList/Tab.d.ts.map +1 -1
- package/dist/TabList/Tab.js +5 -1
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +25 -3
- package/dist/Table/TableCell.d.ts.map +1 -1
- package/dist/Table/TableCell.js +5 -1
- package/dist/Table/TableHeaderCell.d.ts.map +1 -1
- package/dist/Table/TableHeaderCell.js +1 -0
- package/dist/Table/TableRow.d.ts.map +1 -1
- package/dist/Table/TableRow.js +3 -0
- package/dist/Table/glidingDivider.d.ts +96 -0
- package/dist/Table/glidingDivider.d.ts.map +1 -0
- package/dist/Table/glidingDivider.js +275 -0
- package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
- package/dist/Table/plugins/columnResize/useTableColumnResize.js +11 -2
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts +8 -0
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +374 -13
- package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
- package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +244 -6
- package/dist/Table/rowDivider.d.ts +34 -0
- package/dist/Table/rowDivider.d.ts.map +1 -0
- package/dist/Table/rowDivider.js +118 -0
- package/dist/TextArea/TextArea.d.ts +6 -4
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +15 -12
- package/dist/TextInput/TextInput.d.ts +7 -5
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +43 -22
- package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
- package/dist/Thumbnail/Thumbnail.js +45 -11
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +12 -4
- package/dist/TimeInput/TouchTimeField.d.ts.map +1 -1
- package/dist/TimeInput/TouchTimeField.js +2 -1
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +62 -6
- package/dist/Tokenizer/Tokenizer.d.ts +71 -4
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +430 -45
- package/dist/Tokenizer/TokenizerBottomSheet.d.ts +98 -0
- package/dist/Tokenizer/TokenizerBottomSheet.d.ts.map +1 -0
- package/dist/Tokenizer/TokenizerBottomSheet.js +721 -0
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +9 -3
- package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
- package/dist/TopNav/TopNavHeading.js +26 -0
- package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenu.js +4 -4
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +32 -4
- package/dist/TreeList/TreeList.d.ts +8 -23
- package/dist/TreeList/TreeList.d.ts.map +1 -1
- package/dist/TreeList/TreeList.js +175 -3
- package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
- package/dist/TreeList/TreeListBranches.js +7 -5
- package/dist/TreeList/TreeListItem.d.ts +7 -1
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +5 -3
- package/dist/Typeahead/BaseTypeahead.d.ts +1 -1
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.emptyState.a11y.chromium.spec.d.ts +2 -0
- package/dist/Typeahead/BaseTypeahead.emptyState.a11y.chromium.spec.d.ts.map +1 -0
- package/dist/Typeahead/BaseTypeahead.emptyState.a11y.chromium.spec.js +118 -0
- package/dist/Typeahead/BaseTypeahead.js +27 -11
- package/dist/Typeahead/Typeahead.d.ts +1 -1
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +31 -14
- package/dist/astryx.css +164 -41
- package/dist/gestures/GestureArena.d.ts +175 -0
- package/dist/gestures/GestureArena.d.ts.map +1 -0
- package/dist/gestures/GestureArena.js +816 -0
- package/dist/gestures/GestureRecognizer.d.ts +239 -0
- package/dist/gestures/GestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/GestureRecognizer.js +487 -0
- package/dist/gestures/LongPressGestureRecognizer.d.ts +32 -0
- package/dist/gestures/LongPressGestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/LongPressGestureRecognizer.js +135 -0
- package/dist/gestures/PanGestureRecognizer.d.ts +117 -0
- package/dist/gestures/PanGestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/PanGestureRecognizer.js +349 -0
- package/dist/gestures/TapGestureRecognizer.d.ts +39 -0
- package/dist/gestures/TapGestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/TapGestureRecognizer.js +151 -0
- package/dist/gestures/VelocityTracker.d.ts +51 -0
- package/dist/gestures/VelocityTracker.d.ts.map +1 -0
- package/dist/gestures/VelocityTracker.js +186 -0
- package/dist/gestures/geometry.d.ts +17 -0
- package/dist/gestures/geometry.d.ts.map +1 -0
- package/dist/gestures/geometry.js +55 -0
- package/dist/gestures/types.d.ts +139 -0
- package/dist/gestures/types.d.ts.map +1 -0
- package/dist/gestures/types.js +67 -0
- package/dist/hooks/AnnounceRenderedText.d.ts +17 -0
- package/dist/hooks/AnnounceRenderedText.d.ts.map +1 -0
- package/dist/hooks/AnnounceRenderedText.js +32 -0
- package/dist/hooks/ModalAnnounceProvider.d.ts +41 -0
- package/dist/hooks/ModalAnnounceProvider.d.ts.map +1 -0
- package/dist/hooks/ModalAnnounceProvider.js +133 -0
- package/dist/hooks/actionGroupTracker.d.ts +98 -0
- package/dist/hooks/actionGroupTracker.d.ts.map +1 -0
- package/dist/hooks/actionGroupTracker.js +338 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/menuPressOnItems.d.ts +19 -0
- package/dist/hooks/menuPressOnItems.d.ts.map +1 -0
- package/dist/hooks/menuPressOnItems.js +32 -0
- package/dist/hooks/modalAnnounce.d.ts +18 -0
- package/dist/hooks/modalAnnounce.d.ts.map +1 -0
- package/dist/hooks/modalAnnounce.js +382 -0
- package/dist/hooks/scrollGeometry.d.ts +28 -0
- package/dist/hooks/scrollGeometry.d.ts.map +1 -1
- package/dist/hooks/scrollGeometry.js +36 -2
- package/dist/hooks/useAdaptivePresentation.d.ts +9 -1
- package/dist/hooks/useAdaptivePresentation.d.ts.map +1 -1
- package/dist/hooks/useAdaptivePresentation.js +33 -8
- package/dist/hooks/useAnimatedLayout.d.ts +42 -0
- package/dist/hooks/useAnimatedLayout.d.ts.map +1 -0
- package/dist/hooks/useAnimatedLayout.js +46 -0
- package/dist/hooks/useAnnounce.d.ts +132 -1
- package/dist/hooks/useAnnounce.d.ts.map +1 -1
- package/dist/hooks/useAnnounce.js +356 -27
- package/dist/hooks/useAnnounceRenderedText.d.ts +19 -2
- package/dist/hooks/useAnnounceRenderedText.d.ts.map +1 -1
- package/dist/hooks/useAnnounceRenderedText.js +52 -11
- package/dist/hooks/useBusyAnnouncement.d.ts +9 -5
- package/dist/hooks/useBusyAnnouncement.d.ts.map +1 -1
- package/dist/hooks/useBusyAnnouncement.js +58 -46
- package/dist/hooks/useFocusTrap.d.ts +1 -1
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +57 -26
- package/dist/hooks/useMenuHover.d.ts.map +1 -1
- package/dist/hooks/useMenuHover.js +6 -2
- package/dist/hooks/useMenuPress.d.ts +4 -2
- package/dist/hooks/useMenuPress.d.ts.map +1 -1
- package/dist/hooks/useMenuPress.js +58 -33
- package/dist/hooks/usePressFeedback.d.ts +5 -2
- package/dist/hooks/usePressFeedback.d.ts.map +1 -1
- package/dist/hooks/usePressFeedback.js +5 -2
- package/dist/hooks/useScrollableArea.d.ts +5 -0
- package/dist/hooks/useScrollableArea.d.ts.map +1 -1
- package/dist/hooks/useScrollableArea.js +104 -11
- package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/en.generated.js +33 -0
- package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pseudo.generated.js +33 -0
- package/dist/internal/tokenizer-presentation.d.ts +21 -0
- package/dist/internal/tokenizer-presentation.d.ts.map +1 -0
- package/dist/internal/tokenizer-presentation.js +21 -0
- package/dist/internal/touch.d.ts +26 -0
- package/dist/internal/touch.d.ts.map +1 -0
- package/dist/internal/touch.js +28 -0
- package/dist/motion/LayoutChangeSnapshot.d.ts +33 -0
- package/dist/motion/LayoutChangeSnapshot.d.ts.map +1 -0
- package/dist/motion/LayoutChangeSnapshot.js +37 -0
- package/dist/motion/animatedLayout.d.ts +190 -0
- package/dist/motion/animatedLayout.d.ts.map +1 -0
- package/dist/motion/animatedLayout.js +1502 -0
- package/dist/motion/animator.d.ts +240 -0
- package/dist/motion/animator.d.ts.map +1 -0
- package/dist/motion/animator.js +2203 -0
- package/dist/motion/bezier.d.ts +18 -0
- package/dist/motion/bezier.d.ts.map +1 -0
- package/dist/motion/bezier.js +60 -0
- package/dist/motion/coast.d.ts +61 -0
- package/dist/motion/coast.d.ts.map +1 -0
- package/dist/motion/coast.js +107 -0
- package/dist/motion/decay.d.ts +41 -0
- package/dist/motion/decay.d.ts.map +1 -0
- package/dist/motion/decay.js +90 -0
- package/dist/motion/projection.d.ts +36 -0
- package/dist/motion/projection.d.ts.map +1 -0
- package/dist/motion/projection.js +92 -0
- package/dist/motion/reducedMotion.d.ts +25 -0
- package/dist/motion/reducedMotion.d.ts.map +1 -0
- package/dist/motion/reducedMotion.js +85 -0
- package/dist/motion/reducedMotion.stylex.d.ts +19 -0
- package/dist/motion/reducedMotion.stylex.d.ts.map +1 -0
- package/dist/motion/reducedMotion.stylex.js +37 -0
- package/dist/motion/reducedMotionPlan.d.ts +89 -0
- package/dist/motion/reducedMotionPlan.d.ts.map +1 -0
- package/dist/motion/reducedMotionPlan.js +194 -0
- package/dist/motion/resolveSnapPoint.d.ts +52 -0
- package/dist/motion/resolveSnapPoint.d.ts.map +1 -0
- package/dist/motion/resolveSnapPoint.js +135 -0
- package/dist/motion/roleValue.d.ts +33 -0
- package/dist/motion/roleValue.d.ts.map +1 -0
- package/dist/motion/roleValue.js +84 -0
- package/dist/motion/roles.d.ts +79 -0
- package/dist/motion/roles.d.ts.map +1 -0
- package/dist/motion/roles.js +199 -0
- package/dist/motion/rubberBand.d.ts +20 -0
- package/dist/motion/rubberBand.d.ts.map +1 -0
- package/dist/motion/rubberBand.js +91 -0
- package/dist/motion/selectPage.d.ts +26 -0
- package/dist/motion/selectPage.d.ts.map +1 -0
- package/dist/motion/selectPage.js +55 -0
- package/dist/motion/spring.d.ts +193 -0
- package/dist/motion/spring.d.ts.map +1 -0
- package/dist/motion/spring.js +442 -0
- package/dist/motion/transientCopy.d.ts +33 -0
- package/dist/motion/transientCopy.d.ts.map +1 -0
- package/dist/motion/transientCopy.js +314 -0
- package/dist/motion/uikit/gestures.d.ts +642 -0
- package/dist/motion/uikit/gestures.d.ts.map +1 -0
- package/dist/motion/uikit/gestures.js +428 -0
- package/dist/motion/uikit/index.d.ts +24 -0
- package/dist/motion/uikit/index.d.ts.map +1 -0
- package/dist/motion/uikit/index.js +26 -0
- package/dist/motion/uikit/navigation.d.ts +54 -0
- package/dist/motion/uikit/navigation.d.ts.map +1 -0
- package/dist/motion/uikit/navigation.js +83 -0
- package/dist/motion/uikit/scroll.d.ts +116 -0
- package/dist/motion/uikit/scroll.d.ts.map +1 -0
- package/dist/motion/uikit/scroll.js +146 -0
- package/dist/motion/uikit/springs.d.ts +545 -0
- package/dist/motion/uikit/springs.d.ts.map +1 -0
- package/dist/motion/uikit/springs.js +548 -0
- package/dist/motion/uikit/timing.d.ts +202 -0
- package/dist/motion/uikit/timing.d.ts.map +1 -0
- package/dist/motion/uikit/timing.js +201 -0
- package/dist/reset.a11y.chromium.spec.js +1 -0
- package/dist/reset.until-found.a11y.browser.spec.js +1 -0
- package/dist/theme/declarationBoundary.d.ts +13 -0
- package/dist/theme/declarationBoundary.d.ts.map +1 -1
- package/dist/theme/declarationBoundary.js +264 -1
- package/dist/theme/defineTheme.d.ts +111 -11
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/defineTheme.js +97 -7
- package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
- package/dist/theme/derivedVarRegistry.js +9 -0
- package/dist/theme/generateThemeRules.d.ts +6 -2
- package/dist/theme/generateThemeRules.d.ts.map +1 -1
- package/dist/theme/generateThemeRules.js +468 -24
- package/dist/theme/index.d.ts +2 -2
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +1 -1
- package/dist/theme/statusColor.d.ts +48 -0
- package/dist/theme/statusColor.d.ts.map +1 -0
- package/dist/theme/statusColor.js +203 -0
- package/dist/theme/statusRoleDerivation.d.ts +11 -0
- package/dist/theme/statusRoleDerivation.d.ts.map +1 -0
- package/dist/theme/statusRoleDerivation.js +236 -0
- package/dist/theme/syntax/SyntaxTheme.d.ts +1 -1
- package/dist/theme/syntax/SyntaxTheme.js +1 -1
- package/dist/theme/syntax/defineSyntaxTheme.js +1 -1
- package/dist/theme/syntax/presets.d.ts +1 -1
- package/dist/theme/syntax/presets.js +1 -1
- package/dist/theme/syntax/tokens.d.ts +1 -1
- package/dist/theme/syntax/tokens.js +1 -1
- package/dist/theme/themeAdaptations.d.ts +25 -1
- package/dist/theme/themeAdaptations.d.ts.map +1 -1
- package/dist/theme/themeAdaptations.js +30 -1
- package/dist/theme/tokens.d.ts.map +1 -1
- package/dist/theme/tokens.js +16 -1
- package/dist/theme/tokens.stylex.d.ts +42 -2
- package/dist/theme/tokens.stylex.d.ts.map +1 -1
- package/dist/theme/tokens.stylex.js +54 -3
- package/dist/theme/useTheme.d.ts +4 -2
- package/dist/theme/useTheme.d.ts.map +1 -1
- package/dist/theme/useTheme.js +10 -4
- package/dist/utils/edgeAutoscroll.d.ts +53 -0
- package/dist/utils/edgeAutoscroll.d.ts.map +1 -0
- package/dist/utils/edgeAutoscroll.js +81 -0
- package/dist/utils/hitArea.d.ts +72 -0
- package/dist/utils/hitArea.d.ts.map +1 -0
- package/dist/utils/hitArea.js +681 -0
- package/dist/utils/interactionOverlay.stylex.d.ts +4 -1
- package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
- package/dist/utils/interactionOverlay.stylex.js +4 -1
- package/dist/utils/pressFeedback.d.ts +5 -1
- package/dist/utils/pressFeedback.d.ts.map +1 -1
- package/dist/utils/pressFeedback.js +285 -54
- package/dist/utils/pressGesture.d.ts +13 -1
- package/dist/utils/pressGesture.d.ts.map +1 -1
- package/dist/utils/pressGesture.js +40 -1
- package/dist/utils/touchPresentation.d.ts +48 -0
- package/dist/utils/touchPresentation.d.ts.map +1 -0
- package/dist/utils/touchPresentation.js +133 -0
- package/locales/af-ZA.json +1 -1
- package/locales/ar-SA.json +1 -1
- package/locales/ca-ES.json +1 -1
- package/locales/cs-CZ.json +1 -1
- package/locales/da-DK.json +1 -1
- package/locales/de-DE.json +1 -1
- package/locales/el-GR.json +1 -1
- package/locales/en.json +132 -0
- package/locales/es-ES.json +1 -1
- package/locales/fi-FI.json +1 -1
- package/locales/fr-FR.json +1 -1
- package/locales/he-IL.json +1 -1
- package/locales/hu-HU.json +1 -1
- package/locales/it-IT.json +1 -1
- package/locales/ja-JP.json +1 -1
- package/locales/ko-KR.json +1 -1
- package/locales/nl-NL.json +1 -1
- package/locales/no-NO.json +1 -1
- package/locales/pl-PL.json +1 -1
- package/locales/pseudo.json +99 -0
- package/locales/pt-BR.json +1 -1
- package/locales/pt-PT.json +1 -1
- package/locales/ro-RO.json +1 -1
- package/locales/ru-RU.json +1 -1
- package/locales/sr-SP.json +1 -1
- package/locales/sv-SE.json +1 -1
- package/locales/tr-TR.json +1 -1
- package/locales/uk-UA.json +1 -1
- package/locales/vi-VN.json +1 -1
- package/locales/zh-CN.json +1 -1
- package/locales/zh-TW.json +1 -1
- package/package.json +63 -8
- package/src/AlertDialog/AlertDialog.spec.md +85 -31
- package/src/AlertDialog/AlertDialog.tsx +1 -1
- package/src/AlertDialog/__tests__/AlertDialog.a11y.chromium.spec.ts +1 -0
- package/src/AppShell/AppShell.spec.md +334 -0
- package/src/AppShell/AppShell.tsx +170 -2
- package/src/AspectRatio/AspectRatio.spec.md +179 -0
- package/src/AspectRatio/__tests__/AspectRatioFitLayers.a11y.browser.spec.ts +1 -0
- package/src/Avatar/Avatar.spec.md +253 -0
- package/src/Avatar/AvatarStatusDot.spec.md +93 -79
- package/src/Avatar/AvatarStatusDot.test.tsx +22 -0
- package/src/Avatar/AvatarStatusDot.tsx +2 -2
- package/src/AvatarGroup/AvatarGroup.spec.md +205 -0
- package/src/AvatarGroup/AvatarGroupOverflow.spec.md +123 -120
- package/src/Badge/Badge.spec.md +207 -0
- package/src/Badge/Badge.test.tsx +13 -0
- package/src/Badge/Badge.tsx +38 -2
- package/src/Banner/Banner.spec.md +41 -28
- package/src/Banner/Banner.test.tsx +118 -16
- package/src/Banner/Banner.tsx +94 -7
- package/src/Blockquote/Blockquote.spec.md +171 -0
- package/src/BottomSheet/BottomSheet.doc.mjs +106 -24
- package/src/BottomSheet/BottomSheet.spec.md +271 -40
- package/src/BottomSheet/BottomSheet.test.tsx +491 -5
- package/src/BottomSheet/BottomSheet.tsx +544 -145
- package/src/BottomSheet/BottomSheetHostContext.ts +44 -0
- package/src/BottomSheet/BottomSheetPanel.test.tsx +73 -19
- package/src/BottomSheet/BottomSheetPanel.tsx +178 -63
- package/src/BottomSheet/BottomSheetReplace.test.tsx +1638 -0
- package/src/BottomSheet/BottomSheetStacking.announce.test.tsx +246 -0
- package/src/BottomSheet/BottomSheetStacking.test.tsx +3184 -0
- package/src/BottomSheet/BottomSheetSwitcher.doc.mjs +27 -31
- package/src/BottomSheet/BottomSheetSwitcher.spec.md +138 -43
- package/src/BottomSheet/BottomSheetSwitcher.tsx +275 -26
- package/src/BottomSheet/BottomSheetSwitcherContext.ts +6 -0
- package/src/BottomSheet/__tests__/BottomSheetDrag.a11y.chromium.spec.ts +1 -0
- package/src/BottomSheet/__tests__/BottomSheetFocusReturn.a11y.browser.spec.ts +1 -0
- package/src/BottomSheet/__tests__/BottomSheetKeyboard.a11y.browser.spec.ts +4 -1
- package/src/BottomSheet/__tests__/BottomSheetNativeModal.a11y.browser.spec.ts +1 -0
- package/src/BottomSheet/__tests__/BottomSheetPinchZoom.a11y.chromium.spec.ts +1 -0
- package/src/BottomSheet/__tests__/BottomSheetToastOutlet.a11y.chromium.spec.ts +83 -0
- package/src/BottomSheet/bottomSheetEscape.ts +21 -0
- package/src/BottomSheet/bottomSheetStack.ts +2054 -0
- package/src/BottomSheet/useSheetGestures.ts +68 -12
- package/src/Breadcrumbs/BreadcrumbItem.spec.md +104 -60
- package/src/Breadcrumbs/BreadcrumbItem.tsx +30 -1
- package/src/Breadcrumbs/Breadcrumbs.spec.md +165 -0
- package/src/Breadcrumbs/Breadcrumbs.test.tsx +90 -0
- package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.chromium.spec.ts +1 -0
- package/src/Button/Button.spec.md +322 -0
- package/src/Button/Button.test.tsx +8 -1
- package/src/Button/Button.tsx +36 -7
- package/src/Button/__tests__/Button.a11y.chromium.spec.ts +1 -0
- package/src/ButtonGroup/ButtonGroup.spec.md +220 -0
- package/src/Calendar/__tests__/Calendar.a11y.chromium.spec.ts +1 -0
- package/src/Card/Card.spec.md +205 -0
- package/src/Carousel/__tests__/Carousel.a11y.chromium.spec.ts +1 -0
- package/src/Center/Center.spec.md +104 -124
- package/src/Chat/ChatComposer.test.tsx +54 -0
- package/src/Chat/ChatComposer.tsx +41 -27
- package/src/Chat/ChatComposerInput.test.tsx +165 -21
- package/src/Chat/ChatComposerInput.tsx +73 -16
- package/src/Chat/ChatMessageMetadata.tsx +1 -1
- package/src/Chat/ChatPastedTextToken.test.tsx +71 -0
- package/src/Chat/ChatPastedTextToken.tsx +8 -7
- package/src/Chat/ChatToolCalls.tsx +5 -5
- package/src/Chat/__tests__/ChatLayoutScrollButton.a11y.chromium.spec.ts +22 -4
- package/src/Chat/__tests__/ChatLayoutScrollButtonSurface.a11y.chromium.spec.ts +1 -0
- package/src/Chat/__tests__/ChatMessageList.a11y.chromium.spec.ts +1 -0
- package/src/Chat/__tests__/ChatMessageMetadata.a11y.chromium.spec.ts +1 -0
- package/src/Chat/__tests__/ChatSendButton.a11y.chromium.spec.ts +1 -0
- package/src/Chat/__tests__/ChatSystemMessage.a11y.chromium.spec.ts +1 -0
- package/src/Chat/__tests__/ChatTokenizedText.a11y.chromium.spec.ts +1 -0
- package/src/Chat/__tests__/useChatStreamScroll.a11y.chromium.spec.ts +1 -0
- package/src/CheckboxInput/CheckboxInput.spec.md +97 -66
- package/src/CheckboxInput/__tests__/Checkbox.a11y.chromium.spec.ts +1 -0
- package/src/CheckboxList/CheckboxList.spec.md +92 -78
- package/src/CheckboxList/CheckboxListItem.spec.md +86 -60
- package/src/CheckboxList/__tests__/CheckboxList.a11y.chromium.spec.ts +1 -0
- package/src/ClickableCard/ClickableCard.doc.mjs +1 -2
- package/src/ClickableCard/ClickableCard.spec.md +124 -128
- package/src/ClickableCard/ClickableCard.test.tsx +14 -3
- package/src/ClickableCard/ClickableCard.tsx +44 -28
- package/src/Code/Code.spec.md +30 -32
- package/src/CodeBlock/CodeBlock.doc.mjs +21 -4
- package/src/CodeBlock/CodeBlock.spec.md +96 -61
- package/src/CodeBlock/CodeBlock.test.tsx +97 -1
- package/src/CodeBlock/CodeBlock.tsx +132 -22
- package/src/CodeBlock/CodeLanguage.test.tsx +173 -0
- package/src/CodeBlock/__tests__/CodeBlockCopyClearance.a11y.chromium.spec.ts +290 -0
- package/src/CodeBlock/__tests__/CodeBlockLanguages.a11y.chromium.spec.ts +64 -0
- package/src/CodeBlock/__tests__/CodeBlockOverflowStop.a11y.chromium.spec.ts +1 -0
- package/src/CodeBlock/__tests__/DiffRowPaint.a11y.chromium.spec.ts +1 -0
- package/src/CodeBlock/grammar.ts +291 -0
- package/src/CodeBlock/index.ts +7 -0
- package/src/CodeBlock/languages/dockerfile.ts +43 -0
- package/src/CodeBlock/languages/graphql.ts +30 -0
- package/src/CodeBlock/languages/java.ts +28 -0
- package/src/CodeBlock/languages/lua.ts +21 -0
- package/src/CodeBlock/languages/objective-c.ts +26 -0
- package/src/CodeBlock/languages/powershell.ts +31 -0
- package/src/CodeBlock/languages/ruby.ts +29 -0
- package/src/CodeBlock/languages/thrift.ts +32 -0
- package/src/CodeBlock/languages/toml.ts +36 -0
- package/src/CodeBlock/languages.test.ts +402 -0
- package/src/CodeBlock/tokenizer.ts +176 -16
- package/src/CodeBlock/tokenizerBudget.test.ts +112 -0
- package/src/Collapsible/Collapsible.doc.mjs +6 -0
- package/src/Collapsible/Collapsible.spec.md +26 -23
- package/src/Collapsible/Collapsible.tsx +121 -2
- package/src/Collapsible/__tests__/Collapsible.a11y.chromium.spec.ts +1 -0
- package/src/Collapsible/__tests__/CollapsibleGroup.a11y.chromium.spec.ts +1 -0
- package/src/Collapsible/__tests__/CollapsibleGroupAccordion.a11y.chromium.spec.ts +1 -0
- package/src/CommandPalette/CommandPalette.perf.test.ts +8 -4
- package/src/CommandPalette/CommandPalette.tsx +17 -1
- package/src/CommandPalette/CommandPaletteEmpty.doc.mjs +6 -3
- package/src/CommandPalette/CommandPaletteEmpty.spec.md +190 -0
- package/src/CommandPalette/CommandPaletteEmpty.tsx +4 -0
- package/src/CommandPalette/CommandPaletteInput.tsx +21 -2
- package/src/CommandPalette/__tests__/CommandPalette.a11y.chromium.spec.ts +1 -0
- package/src/CommandPalette/__tests__/CommandPaletteEmptyAudit.a11y.chromium.spec.ts +715 -0
- package/src/CommandPalette/__tests__/CommandPaletteFooter.a11y.chromium.spec.ts +3 -2
- package/src/CommandPalette/__tests__/CommandPaletteInputAudit.a11y.chromium.spec.ts +1 -0
- package/src/CommandPalette/__tests__/CommandPaletteItemAudit.a11y.chromium.spec.ts +1 -0
- package/src/CommandPalette/busyStartSettle.ts +26 -0
- package/src/ComplexSelector/ComplexSelector.doc.mjs +1 -1
- package/src/ComplexSelector/ComplexSelector.focusableDisabled.test.tsx +1 -1
- package/src/ComplexSelector/ComplexSelector.fr10.test.tsx +9 -9
- package/src/ComplexSelector/ComplexSelector.spec.md +161 -107
- package/src/ComplexSelector/ComplexSelector.test.tsx +236 -17
- package/src/ComplexSelector/ComplexSelector.tsx +69 -4
- package/src/ComplexSelector/__tests__/ComplexSelector.a11y.chromium.spec.ts +965 -0
- package/src/ContextMenu/ContextMenu.doc.mjs +1 -1
- package/src/ContextMenu/ContextMenu.spec.md +38 -30
- package/src/ContextMenu/ContextMenu.test.tsx +189 -11
- package/src/ContextMenu/ContextMenu.tsx +75 -17
- package/src/ContextMenu/__tests__/ContextMenuAudit.a11y.chromium.spec.ts +1 -0
- package/src/ContextMenu/__tests__/ContextMenuCursorAnchor.a11y.browser.spec.ts +1 -0
- package/src/ContextMenu/__tests__/ContextMenuLongPress.a11y.chromium.spec.ts +117 -0
- package/src/DateInput/DateInput.tsx +9 -4
- package/src/DateInput/DateInputTouch.test.tsx +10 -1
- package/src/DateInput/NativeDateField.tsx +2 -1
- package/src/DateInput/TouchDateField.tsx +2 -1
- package/src/DateInput/Wheel.tsx +4 -1
- package/src/DateInput/__tests__/DateInputAudit.a11y.chromium.spec.ts +1 -0
- package/src/DateInput/__tests__/WheelPinchZoom.a11y.chromium.spec.ts +171 -0
- package/src/Dialog/Dialog.doc.mjs +201 -47
- package/src/Dialog/Dialog.spec.md +20 -4
- package/src/Dialog/Dialog.tsx +37 -10
- package/src/Dialog/__tests__/Dialog.a11y.browser.spec.ts +1 -0
- package/src/Dialog/__tests__/Dialog.a11y.chromium.spec.ts +1 -0
- package/src/Dialog/__tests__/Dialog.reduced-motion.a11y.browser.spec.ts +252 -0
- package/src/Divider/Divider.spec.md +79 -70
- package/src/Drawer/Drawer.tsx +38 -35
- package/src/Drawer/__tests__/DrawerAudit.a11y.chromium.spec.ts +1 -0
- package/src/Drawer/__tests__/DrawerHeaderAudit.a11y.chromium.spec.ts +1 -0
- package/src/DropdownMenu/DropdownMenu.spec.md +14 -6
- package/src/DropdownMenu/DropdownMenu.ssr.test.tsx +44 -8
- package/src/DropdownMenu/DropdownMenu.test.tsx +107 -5
- package/src/DropdownMenu/DropdownMenu.tsx +51 -34
- package/src/DropdownMenu/DropdownMenuGroup.doc.mjs +2 -2
- package/src/DropdownMenu/DropdownMenuGroup.tsx +12 -6
- package/src/DropdownMenu/DropdownMenuItem.tsx +5 -5
- package/src/DropdownMenu/DropdownMenuRadioItem.doc.mjs +5 -2
- package/src/DropdownMenu/DropdownMenuRadioItem.spec.md +226 -0
- package/src/DropdownMenu/DropdownMenuRadioItem.tsx +32 -16
- package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +158 -0
- package/src/DropdownMenu/DropdownMenuSheetFirstOpen.test.tsx +72 -0
- package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +9 -0
- package/src/DropdownMenu/DropdownMenuSubMenu.spec.md +225 -0
- package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +105 -0
- package/src/DropdownMenu/DropdownMenuSubMenu.tsx +6 -6
- package/src/DropdownMenu/MenuBottomSheet.tsx +42 -7
- package/src/DropdownMenu/MenuBottomSheetActionList.tsx +3 -3
- package/src/DropdownMenu/__tests__/DropdownMenuDividerAudit.a11y.chromium.spec.ts +1 -0
- package/src/DropdownMenu/__tests__/DropdownMenuDrillIn.a11y.chromium.spec.ts +1 -0
- package/src/DropdownMenu/__tests__/DropdownMenuFocusReturn.a11y.browser.spec.ts +1 -0
- package/src/DropdownMenu/__tests__/DropdownMenuGroupAudit.a11y.chromium.spec.ts +1 -0
- package/src/DropdownMenu/__tests__/DropdownMenuItemAudit.a11y.chromium.spec.ts +1 -0
- package/src/DropdownMenu/__tests__/DropdownMenuRadioGroupAudit.a11y.chromium.spec.ts +1 -0
- package/src/DropdownMenu/__tests__/DropdownMenuRadioItemAudit.a11y.chromium.spec.ts +540 -0
- package/src/DropdownMenu/__tests__/DropdownMenuSubMenuAudit.a11y.chromium.spec.ts +671 -0
- package/src/DropdownMenu/__tests__/Menu.a11y.chromium.spec.ts +1 -0
- package/src/DropdownMenu/__tests__/MenuButton.a11y.chromium.spec.ts +1 -0
- package/src/DropdownMenu/__tests__/MenuHeaderFooter.a11y.chromium.spec.ts +1 -0
- package/src/DropdownMenu/__tests__/MenuHoverFocus.a11y.chromium.spec.ts +238 -0
- package/src/DropdownMenu/__tests__/MenuPinchZoom.a11y.chromium.spec.ts +1 -0
- package/src/DropdownMenu/__tests__/MenuPress.a11y.chromium.spec.ts +1 -0
- package/src/DropdownMenu/__tests__/SubMenuHeaderFooter.a11y.chromium.spec.ts +1 -0
- package/src/DropdownMenu/lazyMenuBottomSheet.ts +28 -2
- package/src/DropdownMenu/menuBottomSheet.stylex.ts +33 -0
- package/src/DropdownMenu/menuItemHover.ts +13 -1
- package/src/DropdownMenu/menuTouchAction.stylex.ts +28 -0
- package/src/DropdownMenu/modules/useMenuPress.spec.md +90 -62
- package/src/DropdownMenu/useMenuDrillIn.tsx +7 -5
- package/src/EmptyState/EmptyState.announce.test.tsx +227 -0
- package/src/EmptyState/EmptyState.doc.mjs +1 -1
- package/src/EmptyState/EmptyState.spec.md +22 -14
- package/src/EmptyState/EmptyState.test.tsx +8 -8
- package/src/EmptyState/EmptyState.tsx +7 -5
- package/src/Field/Field.spec.md +57 -48
- package/src/Field/Field.tsx +1 -1
- package/src/Field/FieldLabel.spec.md +70 -59
- package/src/Field/FieldLabel.tsx +1 -1
- package/src/Field/PanelSearchInput.tsx +23 -13
- package/src/Field/__tests__/FieldLabelAudit.a11y.chromium.spec.ts +1 -0
- package/src/Field/inputDisabledFocus.ts +28 -0
- package/src/Field/inputStyles.stylex.ts +15 -15
- package/src/FieldStatus/FieldStatus.test.tsx +12 -0
- package/src/FieldStatus/FieldStatus.tsx +12 -3
- package/src/FieldStatus/__tests__/StatusMessage.a11y.chromium.spec.ts +1 -0
- package/src/FileInput/FileInput.doc.mjs +1 -1
- package/src/FileInput/FileInput.test.tsx +102 -0
- package/src/FileInput/FileInput.tsx +18 -12
- package/src/FormLayout/FormLayout.spec.md +69 -51
- package/src/Grid/Grid.spec.md +72 -69
- package/src/Grid/GridSpan.spec.md +158 -0
- package/src/Heading/Heading.spec.md +198 -0
- package/src/HoverCard/HoverCard.spec.md +244 -0
- package/src/HoverCard/HoverCard.test.tsx +93 -3
- package/src/Icon/Icon.doc.mjs +67 -5
- package/src/Icon/Icon.test.tsx +548 -9
- package/src/Icon/Icon.tsx +79 -15
- package/src/Icon/IconSize.stylex.ts +27 -11
- package/src/Icon/globalIconRegistry.test.tsx +395 -3
- package/src/Icon/globalIconRegistry.tsx +184 -36
- package/src/Icon/index.ts +29 -1
- package/src/Icon/useIcon.ts +5 -1
- package/src/IconButton/IconButton.spec.md +197 -0
- package/src/IconButton/IconButton.test.tsx +5 -2
- package/src/InputGroup/InputGroup.spec.md +199 -0
- package/src/InputGroup/InputGroup.test.tsx +120 -0
- package/src/InputGroup/InputGroup.tsx +59 -52
- package/src/InputGroup/InputGroupInternalContext.ts +15 -0
- package/src/InputGroup/InputGroupText.spec.md +139 -0
- package/src/InputGroup/InputGroupText.tsx +2 -0
- package/src/InputGroup/groupStyles.ts +22 -11
- package/src/Item/Item.test.tsx +158 -1
- package/src/Item/Item.tsx +42 -7
- package/src/Item/ItemMarkerColumns.a11y.chromium.spec.ts +211 -0
- package/src/Item/ItemMarkerColumnsContext.tsx +20 -0
- package/src/Item/ItemSwipeLayer.tsx +9 -0
- package/src/Item/__tests__/ItemSwipeActions.a11y.chromium.spec.ts +1 -0
- package/src/Item/__tests__/ItemSwipePinchZoom.a11y.chromium.spec.ts +186 -0
- package/src/Item/useSwipeAction.ts +71 -5
- package/src/Kbd/Kbd.test.tsx +95 -0
- package/src/Kbd/Kbd.tsx +30 -23
- package/src/Layer/LayerProvider.spec.md +149 -0
- package/src/Layer/__tests__/LayerReducedMotion.a11y.chromium.spec.ts +176 -0
- package/src/Layer/__tests__/TouchTrigger.a11y.chromium.spec.ts +200 -0
- package/src/Layer/layerAnimations.stylex.ts +22 -2
- package/src/Layer/layerDismissalInvariants.test.tsx +9 -0
- package/src/Layer/layerSlideRules.ts +141 -8
- package/src/Layer/layerStack.ts +129 -62
- package/src/Layer/modalOutlet.ts +156 -15
- package/src/Layer/preserveAnimations.ts +304 -0
- package/src/Layer/useLayer.doc.mjs +1 -1
- package/src/Layer/useLayer.test.tsx +850 -0
- package/src/Layer/useLayer.tsx +644 -58
- package/src/Layer/useLayerDismissal.test.tsx +85 -2
- package/src/Layer/useLayerDismissal.ts +12 -9
- package/src/Layer/useTouchTrigger.ts +95 -41
- package/src/Layout/Layout.tsx +18 -0
- package/src/Layout/LayoutContent.tsx +5 -14
- package/src/Layout/LayoutFooter.tsx +2 -0
- package/src/Layout/LayoutHeader.tsx +2 -0
- package/src/Layout/__tests__/Layout.a11y.chromium.spec.ts +1 -0
- package/src/Layout/layoutEdges.stylex.ts +25 -0
- package/src/Lightbox/Lightbox.spec.md +9 -8
- package/src/Lightbox/Lightbox.test.tsx +83 -0
- package/src/Lightbox/Lightbox.tsx +149 -125
- package/src/Lightbox/LightboxKeyboard.a11y.browser.spec.ts +78 -0
- package/src/Lightbox/LightboxViewport.a11y.browser.spec.ts +70 -0
- package/src/Link/Link.spec.md +229 -0
- package/src/Link/LinkProvider.spec.md +167 -0
- package/src/Link/__tests__/Link.a11y.chromium.spec.ts +1 -0
- package/src/Link/__tests__/Link.navigation.a11y.chromium.spec.ts +1 -0
- package/src/List/List.spec.md +113 -34
- package/src/List/List.tsx +13 -1
- package/src/List/ListContext.tsx +19 -5
- package/src/List/ListItem.doc.mjs +3 -3
- package/src/List/ListItem.tsx +97 -28
- package/src/List/ListMarkers.a11y.chromium.spec.ts +904 -0
- package/src/Markdown/Markdown.codeLanguages.test.tsx +137 -0
- package/src/Markdown/Markdown.doc.mjs +12 -0
- package/src/Markdown/Markdown.fr23.bench.ts +3 -1
- package/src/Markdown/Markdown.fr23.perf.test.ts +45 -3
- package/src/Markdown/Markdown.fr23.sampling.ts +13 -4
- package/src/Markdown/Markdown.spec.md +71 -59
- package/src/Markdown/Markdown.test.tsx +50 -0
- package/src/Markdown/Markdown.tsx +61 -8
- package/src/Markdown/MarkdownTable.a11y.chromium.spec.ts +1 -0
- package/src/Markdown/__tests__/MarkdownNesting.a11y.chromium.spec.ts +1 -0
- package/src/MobileNav/MobileNav.doc.mjs +2 -2
- package/src/MobileNav/MobileNav.spec.md +161 -64
- package/src/MobileNav/MobileNav.test.tsx +27 -10
- package/src/MobileNav/MobileNav.tsx +51 -38
- package/src/MobileNav/MobileNavPinchZoom.a11y.chromium.spec.ts +1 -0
- package/src/MobileNav/MobileNavToggle.test.tsx +63 -0
- package/src/MobileNav/MobileNavToggle.tsx +13 -5
- package/src/MobileNav/__tests__/MobileNavWidth.a11y.chromium.spec.ts +103 -0
- package/src/MoreMenu/MoreMenu.doc.mjs +2 -2
- package/src/MoreMenu/MoreMenu.spec.md +88 -53
- package/src/MultiSelector/CreateRow.a11y.chromium.spec.ts +67 -9
- package/src/MultiSelector/MultiSelector.disabledFocus.a11y.chromium.spec.ts +1 -0
- package/src/MultiSelector/MultiSelector.test.tsx +122 -10
- package/src/MultiSelector/MultiSelector.tsx +24 -4
- package/src/MultiSelector/OptionActions.a11y.chromium.spec.ts +1 -0
- package/src/MultiSelector/__tests__/MultiSelector.a11y.chromium.spec.ts +1 -0
- package/src/NavIcon/NavIcon.spec.md +66 -38
- package/src/NavMenu/NavHeadingMenu.spec.md +104 -46
- package/src/NavMenu/NavHeadingMenu.test.tsx +276 -2
- package/src/NavMenu/NavHeadingMenu.tsx +42 -4
- package/src/NavMenu/NavHeadingMenuItem.tsx +60 -6
- package/src/NavMenu/NavMenuContext.tsx +11 -0
- package/src/NavMenu/__tests__/NavMenusSlideSelection.a11y.chromium.spec.ts +137 -0
- package/src/NumberInput/NumberInput.doc.mjs +6 -6
- package/src/NumberInput/NumberInput.spec.md +248 -0
- package/src/NumberInput/NumberInput.test.tsx +187 -14
- package/src/NumberInput/NumberInput.tsx +86 -24
- package/src/NumberInput/__tests__/NumberInput.a11y.chromium.spec.ts +1 -0
- package/src/Overlay/Overlay.doc.mjs +2 -2
- package/src/Overlay/Overlay.test.tsx +72 -4
- package/src/Overlay/Overlay.tsx +18 -8
- package/src/Pagination/Pagination.test.tsx +2 -2
- package/src/Popover/Popover.doc.mjs +38 -2
- package/src/Popover/Popover.spec.md +90 -11
- package/src/Popover/Popover.test.tsx +340 -2
- package/src/Popover/Popover.tsx +401 -35
- package/src/Popover/PopoverPresentation.test.tsx +952 -0
- package/src/Popover/__tests__/PopoverSecondDocument.a11y.chromium.spec.ts +205 -0
- package/src/Popover/index.ts +5 -1
- package/src/Popover/lazyPopoverBottomSheet.ts +57 -0
- package/src/Popover/usePopover.tsx +11 -5
- package/src/PowerSearch/PowerSearch.spec.md +423 -53
- package/src/PowerSearch/PowerSearch.test.tsx +77 -1
- package/src/PowerSearch/PowerSearch.tsx +7 -5
- package/src/ProgressBar/ProgressBar.test.tsx +12 -0
- package/src/ProgressBar/ProgressBar.tsx +18 -0
- package/src/ProgressBar/__tests__/ProgressBar.a11y.chromium.spec.ts +1 -0
- package/src/RadioList/RadioList.spec.md +227 -0
- package/src/RadioList/RadioList.test.tsx +34 -7
- package/src/RadioList/RadioListItem.press.source-build.test.mjs +80 -0
- package/src/RadioList/RadioListItem.spec.md +223 -0
- package/src/RadioList/RadioListItem.tsx +26 -12
- package/src/RadioList/__tests__/RadioGroup.a11y.chromium.spec.ts +1 -0
- package/src/Resizable/__tests__/ResizeHandle.a11y.chromium.spec.ts +1 -0
- package/src/ScrollableArea/ScrollableArea.spec.md +6 -5
- package/src/ScrollableArea/ScrollableArea.test.tsx +5 -0
- package/src/ScrollableArea/ScrollableArea.tsx +4 -0
- package/src/Section/Section.spec.md +102 -44
- package/src/SegmentedControl/SegmentedControl.spec.md +162 -53
- package/src/SegmentedControl/SegmentedControl.test.tsx +46 -0
- package/src/SegmentedControl/SegmentedControl.tsx +7 -3
- package/src/SegmentedControl/SegmentedControlItem.spec.md +206 -0
- package/src/SelectableCard/SelectableCard.doc.mjs +7 -6
- package/src/SelectableCard/SelectableCard.spec.md +230 -0
- package/src/SelectableCard/SelectableCard.test.tsx +11 -1
- package/src/SelectableCard/SelectableCard.tsx +49 -33
- package/src/Selector/Selector.sheetActiveDescendant.test.tsx +95 -0
- package/src/Selector/Selector.spec.md +12 -4
- package/src/Selector/Selector.test.tsx +114 -8
- package/src/Selector/Selector.tsx +30 -6
- package/src/Selector/__tests__/ComboboxField.a11y.chromium.spec.ts +458 -0
- package/src/Selector/__tests__/EmptyStateAnnouncement.a11y.chromium.spec.ts +1 -0
- package/src/Selector/__tests__/Listbox.a11y.chromium.spec.ts +1 -0
- package/src/Selector/__tests__/Selector.a11y.chromium.spec.ts +1 -0
- package/src/Selector/__tests__/Selector.combobox.a11y.chromium.spec.ts +1 -0
- package/src/Selector/hooks.ts +234 -10
- package/src/Selector/useCombobox.doc.mjs +156 -9
- package/src/Selector/useCombobox.public.test.tsx +89 -0
- package/src/Selector/useCombobox.test.tsx +444 -0
- package/src/SideNav/SideNav.spec.md +50 -30
- package/src/SideNav/SideNav.test.tsx +425 -25
- package/src/SideNav/SideNav.tsx +85 -1
- package/src/SideNav/SideNavCollapseButton.spec.md +161 -0
- package/src/SideNav/SideNavHeading.spec.md +226 -0
- package/src/SideNav/SideNavHeading.tsx +102 -6
- package/src/SideNav/SideNavItem.spec.md +246 -0
- package/src/SideNav/SideNavItem.tsx +40 -9
- package/src/SideNav/SideNavRailMotion.test.tsx +620 -0
- package/src/SideNav/SideNavSection.spec.md +153 -0
- package/src/SideNav/__tests__/SideNavRailMotion.interaction.browser.spec.ts +417 -0
- package/src/SideNav/railMotion.ts +315 -0
- package/src/Skeleton/Skeleton.spec.md +99 -42
- package/src/Skeleton/Skeleton.test.tsx +108 -1
- package/src/Skeleton/Skeleton.tsx +30 -2
- package/src/Slider/Slider.fr10.test.tsx +34 -0
- package/src/Slider/Slider.test.tsx +172 -0
- package/src/Slider/Slider.tsx +70 -14
- package/src/Slider/__tests__/Slider.a11y.chromium.spec.ts +1 -0
- package/src/Spinner/Spinner.spec.md +231 -0
- package/src/Stack/Stack.spec.md +104 -56
- package/src/StatusDot/StatusDot.spec.md +139 -51
- package/src/StatusDot/StatusDot.test.tsx +13 -0
- package/src/StatusDot/StatusDot.tsx +27 -0
- package/src/Stepper/Step.spec.md +206 -0
- package/src/Stepper/Step.tsx +19 -3
- package/src/Stepper/Stepper.test.tsx +50 -0
- package/src/Switch/Switch.focusRing.source-build.test.mjs +81 -0
- package/src/Switch/Switch.spec.md +150 -66
- package/src/Switch/Switch.tsx +11 -9
- package/src/Switch/__tests__/Switch.a11y.chromium.spec.ts +1 -0
- package/src/Switch/switch.markers.stylex.ts +7 -2
- package/src/TabList/Tab.spec.md +201 -0
- package/src/TabList/Tab.tsx +7 -1
- package/src/TabList/TabList.doc.mjs +3 -3
- package/src/TabList/TabList.test.tsx +27 -0
- package/src/TabList/TabMenu.spec.md +198 -0
- package/src/TabList/TabMenu.tsx +24 -1
- package/src/TabList/__tests__/TabListOverflow.interaction.browser.spec.ts +99 -0
- package/src/TabList/__tests__/Tabs.a11y.chromium.spec.ts +1 -0
- package/src/Table/Table.spec.md +54 -30
- package/src/Table/TableCell.tsx +9 -0
- package/src/Table/TableHeaderCell.tsx +2 -0
- package/src/Table/TableRow.tsx +3 -0
- package/src/Table/__tests__/Table.a11y.chromium.spec.ts +129 -0
- package/src/Table/__tests__/Table.a11y.contracts.ts +35 -0
- package/src/Table/__tests__/Table.a11y.renders.tsx +112 -0
- package/src/Table/__tests__/Table.a11y.states.ts +140 -0
- package/src/Table/__tests__/Table.a11y.test.tsx +198 -0
- package/src/Table/__tests__/TableMinWidth.a11y.chromium.spec.ts +1 -0
- package/src/Table/__tests__/TableRowGlide.a11y.browser.spec.ts +382 -0
- package/src/Table/__tests__/TableScroll.a11y.chromium.spec.ts +1 -0
- package/src/Table/__tests__/TableStickyDividers.a11y.browser.spec.ts +560 -0
- package/src/Table/glidingDivider.ts +336 -0
- package/src/Table/plugins/columnResize/useTableColumnResize.tsx +23 -8
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.glide.test.tsx +280 -0
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +91 -9
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +471 -10
- package/src/Table/plugins/rowStatus/useTableRowStatus.spec.md +4 -4
- package/src/Table/plugins/sortable/__tests__/useTableSortable.a11y.test.tsx +174 -0
- package/src/Table/plugins/sortable/__tests__/useTableSortable.a11y.ts +86 -0
- package/src/Table/plugins/sortable/useTableSortable.test.tsx +0 -12
- package/src/Table/plugins/stickyColumns/useTableStickyColumns.test.tsx +51 -1
- package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +312 -12
- package/src/Table/rowDivider.ts +154 -0
- package/src/Table/useTableRowExpansion.doc.mjs +8 -0
- package/src/Text/Text.spec.md +114 -127
- package/src/TextArea/TextArea.doc.mjs +6 -6
- package/src/TextArea/TextArea.spec.md +164 -64
- package/src/TextArea/TextArea.test.tsx +65 -6
- package/src/TextArea/TextArea.tsx +27 -15
- package/src/TextInput/TextInput.doc.mjs +8 -8
- package/src/TextInput/TextInput.spec.md +260 -0
- package/src/TextInput/TextInput.test.tsx +126 -10
- package/src/TextInput/TextInput.tsx +57 -23
- package/src/TextInput/__tests__/TextInput.a11y.chromium.spec.ts +1 -0
- package/src/Thumbnail/Thumbnail.doc.mjs +1 -2
- package/src/Thumbnail/Thumbnail.spec.md +244 -0
- package/src/Thumbnail/Thumbnail.test.tsx +120 -7
- package/src/Thumbnail/Thumbnail.tsx +77 -31
- package/src/TimeInput/TimeInput.spec.md +273 -0
- package/src/TimeInput/TimeInput.tsx +12 -4
- package/src/TimeInput/TouchTimeField.tsx +2 -1
- package/src/Timestamp/Timestamp.spec.md +252 -0
- package/src/Toast/Toast.spec.md +300 -0
- package/src/Toast/ToastViewport.modalHost.test.tsx +630 -5
- package/src/Toast/ToastViewport.test.tsx +137 -0
- package/src/Toast/ToastViewport.tsx +63 -6
- package/src/ToggleButton/ToggleButton.spec.md +256 -0
- package/src/ToggleButton/ToggleButtonGroup.spec.md +195 -0
- package/src/ToggleButton/__tests__/ToggleButton.a11y.chromium.spec.ts +1 -0
- package/src/Token/Token.spec.md +271 -0
- package/src/Token/__tests__/TokenRemoveTarget.a11y.chromium.spec.ts +1 -0
- package/src/Tokenizer/Tokenizer.props-type.test.tsx +126 -0
- package/src/Tokenizer/Tokenizer.spec.md +313 -72
- package/src/Tokenizer/Tokenizer.ssr.test.tsx +196 -0
- package/src/Tokenizer/Tokenizer.test.tsx +44 -8
- package/src/Tokenizer/Tokenizer.tsx +586 -41
- package/src/Tokenizer/TokenizerBottomSheet.test.tsx +2719 -0
- package/src/Tokenizer/TokenizerBottomSheet.tsx +957 -0
- package/src/Tokenizer/__tests__/TokenizerEndLane.a11y.chromium.spec.ts +61 -27
- package/src/Toolbar/Toolbar.spec.md +103 -49
- package/src/Toolbar/__tests__/Toolbar.a11y.chromium.spec.ts +1 -0
- package/src/Tooltip/Tooltip.spec.md +161 -36
- package/src/Tooltip/Tooltip.test.tsx +171 -3
- package/src/Tooltip/__tests__/Tooltip.a11y.chromium.spec.ts +1 -0
- package/src/Tooltip/__tests__/Tooltip.a11y.states.ts +2 -2
- package/src/Tooltip/__tests__/TooltipFocusReturn.a11y.chromium.spec.ts +1 -0
- package/src/Tooltip/__tests__/TooltipHoverBridge.a11y.chromium.spec.ts +1 -0
- package/src/Tooltip/__tests__/TooltipTallContent.a11y.browser.spec.ts +673 -0
- package/src/Tooltip/__tests__/TooltipTallContentTracking.a11y.browser.spec.ts +1162 -0
- package/src/Tooltip/useTooltip.tsx +7 -2
- package/src/TopNav/TopNav.spec.md +190 -0
- package/src/TopNav/TopNav.test.tsx +89 -0
- package/src/TopNav/TopNavHeading.spec.md +211 -0
- package/src/TopNav/TopNavHeading.tsx +35 -2
- package/src/TopNav/TopNavItem.spec.md +189 -0
- package/src/TopNav/TopNavMegaMenu.reduced-motion.source-build.test.mjs +83 -0
- package/src/TopNav/TopNavMegaMenu.spec.md +190 -0
- package/src/TopNav/TopNavMegaMenu.tsx +21 -4
- package/src/TopNav/TopNavMegaMenuFeaturedCard.spec.md +150 -0
- package/src/TopNav/TopNavMegaMenuItem.spec.md +155 -0
- package/src/TopNav/TopNavMenu.spec.md +202 -0
- package/src/TopNav/TopNavMenu.test.tsx +65 -1
- package/src/TopNav/TopNavMenu.tsx +30 -1
- package/src/TopNav/__tests__/TopNavMobileLongHeading.a11y.chromium.spec.ts +1 -0
- package/src/TreeList/TreeList.doc.mjs +13 -0
- package/src/TreeList/TreeList.spec.md +54 -35
- package/src/TreeList/TreeList.tsx +234 -2
- package/src/TreeList/TreeListBranches.tsx +8 -6
- package/src/TreeList/TreeListItem.tsx +12 -6
- package/src/TreeList/__tests__/TreeList.a11y.chromium.spec.ts +19 -6
- package/src/TreeList/__tests__/TreeListGuideAlignment.interaction.browser.spec.ts +127 -0
- package/src/TreeList/__tests__/TreeListRowNames.a11y.chromium.spec.ts +1 -0
- package/src/Typeahead/BaseTypeahead.emptyState.a11y.chromium.spec.ts +138 -0
- package/src/Typeahead/BaseTypeahead.test.tsx +201 -4
- package/src/Typeahead/BaseTypeahead.tsx +26 -10
- package/src/Typeahead/Typeahead.test.tsx +114 -1
- package/src/Typeahead/Typeahead.tsx +34 -16
- package/src/VisuallyHidden/VisuallyHidden.spec.md +170 -0
- package/src/__tests__/BusyOwnerAnnouncement.test.tsx +66 -5
- package/src/__tests__/CardPress.a11y.chromium.spec.ts +353 -0
- package/src/__tests__/DisabledTriggerFocus.a11y.chromium.spec.ts +2 -1
- package/src/__tests__/PressModality.a11y.chromium.spec.ts +392 -0
- package/src/__tests__/PressedState.a11y.chromium.spec.ts +1 -0
- package/src/__tests__/pressState.ts +43 -1
- package/src/__tests__/unthemedLength.ts +34 -0
- package/src/gestures/GestureArena.ts +966 -0
- package/src/gestures/GestureRecognizer.ts +626 -0
- package/src/gestures/LongPressGestureRecognizer.ts +186 -0
- package/src/gestures/PanGestureRecognizer.ts +410 -0
- package/src/gestures/TapGestureRecognizer.ts +192 -0
- package/src/gestures/VelocityTracker.ts +194 -0
- package/src/gestures/__tests__/LongPressGestureRecognizer.test.ts +141 -0
- package/src/gestures/__tests__/PanGestureRecognizer.test.ts +327 -0
- package/src/gestures/__tests__/TapGestureRecognizer.test.ts +154 -0
- package/src/gestures/__tests__/VelocityTracker.streams.test.ts +243 -0
- package/src/gestures/__tests__/VelocityTracker.test.ts +261 -0
- package/src/gestures/__tests__/arena.test.ts +824 -0
- package/src/gestures/__tests__/arenaContinuous.test.ts +207 -0
- package/src/gestures/__tests__/arenaTapLongPress.test.ts +211 -0
- package/src/gestures/__tests__/fixtures/pointer-streams/README.md +82 -0
- package/src/gestures/__tests__/fixtures/pointer-streams/android-chromium-fling.json +130 -0
- package/src/gestures/__tests__/fixtures/pointer-streams/android-chromium-slow.json +860 -0
- package/src/gestures/__tests__/fixtures/pointer-streams/android-chromium-stop-release.json +357 -0
- package/src/gestures/__tests__/fixtures/pointer-streams/android-firefox-fling.json +66 -0
- package/src/gestures/__tests__/fixtures/pointer-streams/android-firefox-slow.json +268 -0
- package/src/gestures/__tests__/fixtures/pointer-streams/android-firefox-stop-release.json +128 -0
- package/src/gestures/__tests__/fixtures/pointer-streams/capture.html +123 -0
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-mouse-fling.json +64 -0
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-mouse-slow.json +268 -0
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-mouse-stop-release.json +124 -0
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-pen-fling.json +64 -0
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-pen-slow.json +268 -0
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-pen-stop-release.json +124 -0
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-touch-fling.json +84 -0
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-touch-slow.json +381 -0
- package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-touch-stop-release.json +171 -0
- package/src/gestures/__tests__/fixtures/pointer-streams/ios-safari-fling.json +45 -0
- package/src/gestures/__tests__/fixtures/pointer-streams/ios-safari-slow.json +101 -0
- package/src/gestures/__tests__/fixtures/pointer-streams/ios-safari-stop-release.json +130 -0
- package/src/gestures/__tests__/harness.ts +204 -0
- package/src/gestures/geometry.ts +55 -0
- package/src/gestures/types.ts +161 -0
- package/src/hooks/AnnounceRenderedText.tsx +27 -0
- package/src/hooks/ModalAnnounceProvider.tsx +155 -0
- package/src/hooks/__tests__/MenuPressShadowRoot.a11y.chromium.spec.ts +98 -4
- package/src/hooks/__tests__/menuLinkClicks.ts +71 -0
- package/src/hooks/actionGroupTracker.exports.test.ts +28 -0
- package/src/hooks/actionGroupTracker.test.ts +510 -0
- package/src/hooks/actionGroupTracker.ts +449 -0
- package/src/hooks/index.ts +6 -0
- package/src/hooks/menuPressOnItems.ts +40 -0
- package/src/hooks/modalAnnounce.ts +439 -0
- package/src/hooks/scrollGeometry.ts +71 -4
- package/src/hooks/useAdaptivePresentation.ts +46 -9
- package/src/hooks/useAnimatedLayout.doc.mjs +101 -0
- package/src/hooks/useAnimatedLayout.ts +61 -0
- package/src/hooks/useAnnounce.doc.mjs +4 -4
- package/src/hooks/useAnnounce.modal.test.tsx +1291 -0
- package/src/hooks/useAnnounce.test.tsx +64 -4
- package/src/hooks/useAnnounce.ts +452 -28
- package/src/hooks/useAnnounceRenderedText.adopters.test.tsx +226 -0
- package/src/hooks/useAnnounceRenderedText.ts +81 -11
- package/src/hooks/useBusyAnnouncement.adopters.test.tsx +69 -0
- package/src/hooks/useBusyAnnouncement.test.tsx +112 -0
- package/src/hooks/useBusyAnnouncement.ts +64 -54
- package/src/hooks/useFocusTrap.doc.mjs +3 -3
- package/src/hooks/useFocusTrap.test.tsx +135 -2
- package/src/hooks/useFocusTrap.ts +59 -27
- package/src/hooks/useMenuHover.ts +6 -2
- package/src/hooks/useMenuPress.doc.mjs +2 -2
- package/src/hooks/useMenuPress.test.tsx +5 -2
- package/src/hooks/useMenuPress.ts +72 -37
- package/src/hooks/usePressFeedback.doc.mjs +4 -4
- package/src/hooks/usePressFeedback.ts +5 -2
- package/src/hooks/useScrollableArea.test.tsx +344 -3
- package/src/hooks/useScrollableArea.ts +120 -12
- package/src/i18n/generated-locales/en.generated.ts +35 -0
- package/src/i18n/generated-locales/pseudo.generated.ts +37 -0
- package/src/internal/tokenizer-presentation.test.ts +29 -0
- package/src/internal/tokenizer-presentation.ts +25 -0
- package/src/internal/touch.test.ts +44 -0
- package/src/internal/touch.ts +41 -0
- package/src/motion/LayoutChangeSnapshot.tsx +50 -0
- package/src/motion/MEASUREMENTS.md +271 -0
- package/src/motion/README.md +218 -0
- package/src/motion/__tests__/animatedLayout.interaction.browser.spec.ts +1613 -0
- package/src/motion/__tests__/animatedLayoutAdopters.interaction.browser.spec.ts +217 -0
- package/src/motion/__tests__/animatedLayoutBundle.ts +17 -0
- package/src/motion/animatedLayout.boundary.test.ts +155 -0
- package/src/motion/animatedLayout.swap.test.ts +827 -0
- package/src/motion/animatedLayout.test.ts +1154 -0
- package/src/motion/animatedLayout.ts +1801 -0
- package/src/motion/animatedLayoutAdopters.test.tsx +500 -0
- package/src/motion/animator.test.ts +2653 -0
- package/src/motion/animator.ts +2794 -0
- package/src/motion/bezier.test.ts +52 -0
- package/src/motion/bezier.ts +64 -0
- package/src/motion/coast.test.ts +99 -0
- package/src/motion/coast.ts +150 -0
- package/src/motion/decay.test.ts +120 -0
- package/src/motion/decay.ts +126 -0
- package/src/motion/projection.test.ts +107 -0
- package/src/motion/projection.ts +116 -0
- package/src/motion/reducedMotion.stylex.ts +41 -0
- package/src/motion/reducedMotion.test.ts +129 -0
- package/src/motion/reducedMotion.ts +94 -0
- package/src/motion/reducedMotionPlan.test.ts +182 -0
- package/src/motion/reducedMotionPlan.ts +263 -0
- package/src/motion/resolveSnapPoint.test.ts +189 -0
- package/src/motion/resolveSnapPoint.ts +169 -0
- package/src/motion/roleValue.ts +105 -0
- package/src/motion/roles.test.ts +256 -0
- package/src/motion/roles.ts +262 -0
- package/src/motion/rubberBand.test.ts +88 -0
- package/src/motion/rubberBand.ts +115 -0
- package/src/motion/selectPage.test.ts +31 -0
- package/src/motion/selectPage.ts +55 -0
- package/src/motion/spring.test.ts +650 -0
- package/src/motion/spring.ts +551 -0
- package/src/motion/transientCopy.ts +374 -0
- package/src/motion/uikit/gestures.test.ts +89 -0
- package/src/motion/uikit/gestures.ts +359 -0
- package/src/motion/uikit/index.ts +80 -0
- package/src/motion/uikit/navigation.ts +83 -0
- package/src/motion/uikit/scroll.ts +141 -0
- package/src/motion/uikit/springRows.test.ts +71 -0
- package/src/motion/uikit/springs.ts +514 -0
- package/src/motion/uikit/timing.ts +210 -0
- package/src/motion/uikit/uikit.test.ts +364 -0
- package/src/motion/uikit/values.test.ts +110 -0
- package/src/reset.a11y.chromium.spec.ts +1 -0
- package/src/reset.until-found.a11y.browser.spec.ts +1 -0
- package/src/serverSafeComponents.test.ts +10 -0
- package/src/tailwind-theme.css +51 -1
- package/src/tailwind-theme.test.ts +51 -0
- package/src/theme/Theme.test.tsx +28 -0
- package/src/theme/__tests__/SpacingRootFontSize.a11y.chromium.spec.ts +1 -0
- package/src/theme/declarationBoundary.ts +282 -1
- package/src/theme/defineTheme.test.ts +529 -12
- package/src/theme/defineTheme.ts +231 -19
- package/src/theme/derivedVarRegistry.test.ts +34 -5
- package/src/theme/derivedVarRegistry.ts +4 -0
- package/src/theme/expandMotionScale.test.ts +17 -0
- package/src/theme/extensibleAxes.test.ts +74 -29
- package/src/theme/generateThemeRules.ts +772 -35
- package/src/theme/index.ts +3 -0
- package/src/theme/onMediaTokens.test.ts +186 -0
- package/src/theme/statusColor.ts +189 -0
- package/src/theme/statusRoleDerivation.ts +289 -0
- package/src/theme/syntax/SyntaxTheme.tsx +1 -1
- package/src/theme/syntax/defineSyntaxTheme.ts +1 -1
- package/src/theme/syntax/presets.ts +1 -1
- package/src/theme/syntax/tokens.ts +1 -1
- package/src/theme/themeAdaptations.test.ts +412 -0
- package/src/theme/themeAdaptations.ts +43 -1
- package/src/theme/tokens.stylex.ts +36 -2
- package/src/theme/tokens.test.ts +111 -1
- package/src/theme/tokens.ts +23 -1
- package/src/theme/useTheme.ts +10 -4
- package/src/utils/customPropertyArmLayering.test.ts +131 -0
- package/src/utils/edgeAutoscroll.test.ts +192 -0
- package/src/utils/edgeAutoscroll.ts +111 -0
- package/src/utils/hitArea.test.ts +628 -0
- package/src/utils/hitArea.ts +770 -0
- package/src/utils/interactionOverlay.stylex.ts +4 -1
- package/src/utils/pressFeedback.test.ts +310 -2
- package/src/utils/pressFeedback.ts +306 -54
- package/src/utils/pressGesture.test.ts +50 -0
- package/src/utils/pressGesture.ts +40 -2
- package/src/utils/touchPresentation.test.ts +323 -0
- package/src/utils/touchPresentation.ts +153 -0
|
@@ -0,0 +1,1675 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file bottomSheetStack.ts
|
|
7
|
+
* @input Uses the DOM (dialog elements, document order) and focusForLastInput
|
|
8
|
+
* @output Exports the private bottom-sheet stack registry: registration of
|
|
9
|
+
* sheet hosts (modal sheets stack; non-modal sheets are a group of their
|
|
10
|
+
* own, hosted as manual popovers), open-order ranking with tree-order
|
|
11
|
+
* ties, native top-layer reconciliation, one view per host (top, covered,
|
|
12
|
+
* recede depth, scrim ownership, blocking), the replace handoff between a
|
|
13
|
+
* sheet closing and a sheet opening in the same update, and the native
|
|
14
|
+
* show/hide and focus helpers the hosts share
|
|
15
|
+
* @position Internal to BottomSheet; consumed by the standalone host in
|
|
16
|
+
* BottomSheet.tsx and by BottomSheetSwitcher. Not exported from the package
|
|
17
|
+
* root, and no public API reaches it.
|
|
18
|
+
*
|
|
19
|
+
* SYNC: When modified, update:
|
|
20
|
+
* - /packages/core/src/BottomSheet/BottomSheet.tsx
|
|
21
|
+
* - /packages/core/src/BottomSheet/BottomSheetSwitcher.tsx
|
|
22
|
+
* - /packages/core/src/BottomSheet/BottomSheetStacking.test.tsx
|
|
23
|
+
* - /packages/core/src/BottomSheet/BottomSheetReplace.test.tsx
|
|
24
|
+
* - /packages/core/src/BottomSheet/BottomSheet.doc.mjs
|
|
25
|
+
* - /apps/storybook/stories/BottomSheetReplace.stories.tsx
|
|
26
|
+
*
|
|
27
|
+
* ## Why a registry and not a component
|
|
28
|
+
*
|
|
29
|
+
* Every modal sheet keeps its own native `<dialog>` and its own open state. A
|
|
30
|
+
* sheet opened while another sheet is open is a stacked sheet: the browser's
|
|
31
|
+
* top layer already paints the later `showModal()` above the earlier one, and
|
|
32
|
+
* the later modal dialog already blocks the earlier one. What the browser does
|
|
33
|
+
* NOT know is that the two dialogs are sheets of one stack, so this registry
|
|
34
|
+
* supplies the rest: which sheet is on top, how far back each covered sheet
|
|
35
|
+
* recedes, whose `::backdrop` is the one visible scrim, and when the native
|
|
36
|
+
* order would come out wrong (several sheets opening in one commit) it puts
|
|
37
|
+
* the top layer right before the frame paints. Nothing here is a public
|
|
38
|
+
* surface; the hosts call it from their commit-phase effects only.
|
|
39
|
+
*
|
|
40
|
+
* ## Order is open order, ties are tree order
|
|
41
|
+
*
|
|
42
|
+
* Each open cycle of a sheet gets a rank: the batch it opened in, then a
|
|
43
|
+
* running sequence. A batch is one React commit: registrations that arrive
|
|
44
|
+
* between two seals belong to the same commit, because the hosts seal in
|
|
45
|
+
* passive effects and React runs every pending passive effect before the next
|
|
46
|
+
* commit's layout effects (and flushes them synchronously after a sync
|
|
47
|
+
* commit). Later batch is higher. Within one batch there is no open order, so
|
|
48
|
+
* document order of the dialog elements decides: a sheet before the sheets
|
|
49
|
+
* rendered in its content, siblings in the order they are written. That is the
|
|
50
|
+
* order the first painted frame of a deep link must show, and effect order
|
|
51
|
+
* (child first) gets it backwards; the rank is read at compare time, after
|
|
52
|
+
* every registration of the commit has arrived, so it does not.
|
|
53
|
+
*
|
|
54
|
+
* ## The top layer is reconciled, not trusted
|
|
55
|
+
*
|
|
56
|
+
* The registry remembers the order it called `showModal()` in. After every
|
|
57
|
+
* change it computes the order the ranks want; where the two diverge it closes
|
|
58
|
+
* the dialogs from the first divergence up and shows them again in rank order.
|
|
59
|
+
* That happens inside the commit, before paint, so a deep link whose inner
|
|
60
|
+
* sheet registered first is repaired without a frame in between. A sheet that
|
|
61
|
+
* closes and reopens gets a new rank and is re-shown on top the same way;
|
|
62
|
+
* only the dialogs that have to move up are re-shown, the rest keep their
|
|
63
|
+
* place, their backdrop and any motion in flight.
|
|
64
|
+
*
|
|
65
|
+
* ## A replace is a move the person makes from one sheet to the next
|
|
66
|
+
*
|
|
67
|
+
* One sheet closing and another opening in the same commit, where the closing
|
|
68
|
+
* sheet was the top open sheet of its stack (no other open sheet above it,
|
|
69
|
+
* the opening sheet aside) and the interaction came from inside it (the
|
|
70
|
+
* control last pressed — or, with no fresh press, the focused element — was
|
|
71
|
+
* in its dialog), is a replace: the person is moving from one step to the
|
|
72
|
+
* next, not stacking one on the other. The registry then runs the handoff a BottomSheetSwitcher runs
|
|
73
|
+
* between its steps, on the two standalone dialogs: the entering sheet rises
|
|
74
|
+
* above the retained one; a taller retained sheet moves down to meet it; the
|
|
75
|
+
* retained sheet fades only after both motions complete, then its dialog
|
|
76
|
+
* closes and the entering sheet takes over the scrim in the same frame. The
|
|
77
|
+
* retained sheet is inert at once, keeps no place in the dismissal order, and
|
|
78
|
+
* lends its opener to the sheet replacing it, so closing the flow later
|
|
79
|
+
* returns focus to where the flow began. A covered sheet closing (not the
|
|
80
|
+
* top), a sheet closing in a different update from the open, or a close and
|
|
81
|
+
* an open driven from a control outside the closing sheet (two owners acting
|
|
82
|
+
* in one update) is never a replace, so a push over a stack and a pop out of
|
|
83
|
+
* one keep their motion and their own openers. Non-modal sheets
|
|
84
|
+
* (`hasScrim={false}`) replace each other too: they are hosted as manual
|
|
85
|
+
* popovers, so the top layer orders them, but they are a group of their own
|
|
86
|
+
* that never stacks, recedes, or owns a scrim.
|
|
87
|
+
*
|
|
88
|
+
* ## One state per page, per document
|
|
89
|
+
*
|
|
90
|
+
* The state lives on `globalThis` so that two copies of this module on one
|
|
91
|
+
* page that speak the same stack protocol version share one stack (see
|
|
92
|
+
* StackState); everything the stack reads and writes across commits — the
|
|
93
|
+
* entries, each document's shown order, which documents have the press
|
|
94
|
+
* listeners, the deferred non-modal sheets and their watchers, the element to
|
|
95
|
+
* keep focused across a re-show — is in that shared state and none of it is
|
|
96
|
+
* per copy. The one module-level value is the engine probe's answer, a fact
|
|
97
|
+
* about the browser that every copy measures alike. A copy on another
|
|
98
|
+
* protocol version keeps a stack of its own. Within a state, each document is
|
|
99
|
+
* its own stack: a sheet portaled into another document has that document's
|
|
100
|
+
* top layer, focus and order.
|
|
101
|
+
*/
|
|
102
|
+
import { notifyTopLayerEntry } from "../Layer/modalOutlet.js";
|
|
103
|
+
import { focusForLastInput } from "../utils/focusReturn.js";
|
|
104
|
+
|
|
105
|
+
/** The motions a host's panel reports to the registry during a replace. */
|
|
106
|
+
|
|
107
|
+
export const BOTTOM_SHEET_STACK_DEFAULT_VIEW = {
|
|
108
|
+
isStacked: false,
|
|
109
|
+
isTop: false,
|
|
110
|
+
isCovered: false,
|
|
111
|
+
depth: 0,
|
|
112
|
+
ownsScrim: false,
|
|
113
|
+
isScrimHandoff: false,
|
|
114
|
+
isBlocked: false,
|
|
115
|
+
isScrimTakenOver: false,
|
|
116
|
+
replacedMotion: null,
|
|
117
|
+
alignmentOffset: 0,
|
|
118
|
+
isReplacing: false
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Why the registry moves focus into a sheet: it was shown on top (`open`), a
|
|
123
|
+
* close above it revealed it (`reveal`), or it is replacing the sheet the
|
|
124
|
+
* person was in (`replace`: focus follows the person from one step to the
|
|
125
|
+
* next, whatever the native host did with it).
|
|
126
|
+
*/
|
|
127
|
+
|
|
128
|
+
/** The sheets of one document's modal stack that close in the same update. */
|
|
129
|
+
|
|
130
|
+
const ALIGNMENT_THRESHOLD_PX = 1;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* The registry's state. One per JavaScript realm, shared by every copy of this
|
|
134
|
+
* module in it that speaks the same stack protocol version: two copies of
|
|
135
|
+
* the package on one page (two bundles, a dev server serving the same file
|
|
136
|
+
* under two URLs, a micro-frontend) must see one stack, or each would show
|
|
137
|
+
* its own sheets over the other's with two scrims and no order. The state
|
|
138
|
+
* lives on `globalThis` under a symbol that names the protocol version
|
|
139
|
+
* (STACK_PROTOCOL_VERSION), so a copy on another version finds no state of
|
|
140
|
+
* its own and keeps a stack of its own; the shape it carries is the guard
|
|
141
|
+
* against a key collision.
|
|
142
|
+
*/
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* The stack protocol version: the shape and meaning of Entry, the
|
|
146
|
+
* registration, the batch/seal protocol and the shared listeners. Copies of
|
|
147
|
+
* the package on one page share one stack per document when they speak the
|
|
148
|
+
* same protocol (`component:BottomSheet` PR1); the version is part of the
|
|
149
|
+
* document-global key, so a copy on another protocol finds no state of its
|
|
150
|
+
* own version and keeps a stack of its own — two independent stacks, each
|
|
151
|
+
* ordering only the sheets its copy renders, never a shared state read with
|
|
152
|
+
* the wrong shape. Bump it whenever any of those change.
|
|
153
|
+
*/
|
|
154
|
+
const STACK_PROTOCOL_VERSION = 8;
|
|
155
|
+
const STACK_STATE_KEY = Symbol.for(`astryx.bottomSheetStack.v${STACK_PROTOCOL_VERSION}`);
|
|
156
|
+
const STACK_STATE_SHAPE = STACK_PROTOCOL_VERSION;
|
|
157
|
+
function createStackState() {
|
|
158
|
+
return {
|
|
159
|
+
shape: STACK_STATE_SHAPE,
|
|
160
|
+
entries: [],
|
|
161
|
+
shownOrder: new WeakMap(),
|
|
162
|
+
listenedDocuments: new WeakMap(),
|
|
163
|
+
batchOpened: [],
|
|
164
|
+
batchClosed: [],
|
|
165
|
+
batchActor: null,
|
|
166
|
+
batchPair: null,
|
|
167
|
+
lastPress: new WeakMap(),
|
|
168
|
+
pendingDrops: [],
|
|
169
|
+
pendingReplaceFocus: null,
|
|
170
|
+
closingTogether: new Map(),
|
|
171
|
+
rankByKey: new WeakMap(),
|
|
172
|
+
currentBatch: 0,
|
|
173
|
+
nextSeq: 0,
|
|
174
|
+
isBatchDirty: false,
|
|
175
|
+
isSealScheduled: false,
|
|
176
|
+
focusToKeep: new WeakMap(),
|
|
177
|
+
deferredTopLayer: new WeakMap()
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
function acquireStackState() {
|
|
181
|
+
const holder = globalThis;
|
|
182
|
+
const shared = holder[STACK_STATE_KEY];
|
|
183
|
+
if (shared != null) {
|
|
184
|
+
// The key names the version, so a state found under it is this
|
|
185
|
+
// protocol's; the shape check is the guard against a key collision.
|
|
186
|
+
return shared.shape === STACK_STATE_SHAPE ? shared : createStackState();
|
|
187
|
+
}
|
|
188
|
+
const created = createStackState();
|
|
189
|
+
holder[STACK_STATE_KEY] = created;
|
|
190
|
+
return created;
|
|
191
|
+
}
|
|
192
|
+
const state = acquireStackState();
|
|
193
|
+
function takeRank() {
|
|
194
|
+
state.isBatchDirty = true;
|
|
195
|
+
scheduleSeal();
|
|
196
|
+
return {
|
|
197
|
+
batch: state.currentBatch,
|
|
198
|
+
seq: state.nextSeq++
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function scheduleSeal() {
|
|
202
|
+
// Backstop for a commit whose hosts run no passive effect afterwards (a
|
|
203
|
+
// subtree React just hid). A later task is always a later batch.
|
|
204
|
+
if (state.isSealScheduled || typeof queueMicrotask !== 'function') {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
state.isSealScheduled = true;
|
|
208
|
+
queueMicrotask(() => {
|
|
209
|
+
state.isSealScheduled = false;
|
|
210
|
+
sealBottomSheetBatch();
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* End the current batch. Hosts call it from a passive effect, which React runs
|
|
216
|
+
* after every layout effect of the same commit and before the next commit;
|
|
217
|
+
* registrations on either side of it are therefore different open orders.
|
|
218
|
+
*/
|
|
219
|
+
export function sealBottomSheetBatch() {
|
|
220
|
+
flushPendingDrops();
|
|
221
|
+
sealClosingGroups();
|
|
222
|
+
if (state.isBatchDirty) {
|
|
223
|
+
state.currentBatch += 1;
|
|
224
|
+
state.isBatchDirty = false;
|
|
225
|
+
}
|
|
226
|
+
state.batchOpened = [];
|
|
227
|
+
state.batchClosed = [];
|
|
228
|
+
state.batchActor = null;
|
|
229
|
+
state.batchPair = null;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Two or more modal sheets of one stack closing in the same update with no
|
|
234
|
+
* sheet of that stack left open and nothing opening: "Close all". Each one
|
|
235
|
+
* would return focus on its own, at depth 0, to an opener that is either
|
|
236
|
+
* inside a dialog that is closing with it or on a page that is still inert
|
|
237
|
+
* behind the other modals — so none of them can. The group returns focus
|
|
238
|
+
* once instead, from its bottom-most sheet, after the last of its dialogs
|
|
239
|
+
* has left the top layer. A single sheet closing, a non-modal sheet, and a
|
|
240
|
+
* batch that also opens a sheet are not groups: their own close handles
|
|
241
|
+
* focus (the replace and the pop have their own rules).
|
|
242
|
+
*/
|
|
243
|
+
function sealClosingGroups() {
|
|
244
|
+
if (state.batchOpened.length > 0) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const closingModal = state.batchClosed.filter(entry => entry.isModal && entry.replace == null && entry.replacing == null);
|
|
248
|
+
if (closingModal.length < 2) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const byDoc = new Map();
|
|
252
|
+
for (const entry of closingModal) {
|
|
253
|
+
const list = byDoc.get(entry.doc) ?? [];
|
|
254
|
+
list.push(entry);
|
|
255
|
+
byDoc.set(entry.doc, list);
|
|
256
|
+
}
|
|
257
|
+
for (const [doc, closing] of byDoc) {
|
|
258
|
+
if (closing.length < 2) {
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
const stack = sortedEntries(doc, true);
|
|
262
|
+
if (stack.some(entry => entry.isOpen)) {
|
|
263
|
+
// Sheets remain open beneath: the pops return into the revealed sheet
|
|
264
|
+
// through the ordinary close path.
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
const members = stack.filter(entry => closing.includes(entry));
|
|
268
|
+
const bottom = members[0];
|
|
269
|
+
const group = {
|
|
270
|
+
members,
|
|
271
|
+
pending: new Set(members),
|
|
272
|
+
returnTarget: bottom.getReturnTarget?.() ?? null
|
|
273
|
+
};
|
|
274
|
+
for (const entry of members) {
|
|
275
|
+
state.closingTogether.set(entry, group);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Whether `key` closes as part of a "Close all" group. Its host then leaves
|
|
282
|
+
* the top layer without returning focus itself; the registry returns it
|
|
283
|
+
* once the whole group has left.
|
|
284
|
+
*/
|
|
285
|
+
export function isClosingTogether(key) {
|
|
286
|
+
const entry = state.entries.find(candidate => candidate.key === key);
|
|
287
|
+
return entry != null && state.closingTogether.has(entry);
|
|
288
|
+
}
|
|
289
|
+
function completeClosingGroup(group) {
|
|
290
|
+
if (group.pending.size > 0 || group.reported) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
group.reported = true;
|
|
294
|
+
const target = group.returnTarget;
|
|
295
|
+
if (target != null && target.isConnected) {
|
|
296
|
+
focusForLastInput(target);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* A member of a "Close all" group has left the top layer. When it is the
|
|
302
|
+
* last one, focus returns to the group's target: the bottom-most sheet's
|
|
303
|
+
* final-focus target or opener, now that no modal dialog stands in the way.
|
|
304
|
+
*/
|
|
305
|
+
export function reportClosedTogether(key) {
|
|
306
|
+
const entry = state.entries.find(candidate => candidate.key === key);
|
|
307
|
+
const group = entry == null ? undefined : state.closingTogether.get(entry);
|
|
308
|
+
if (entry == null || group == null) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
leaveClosingGroup(entry, group);
|
|
312
|
+
}
|
|
313
|
+
function leaveClosingGroup(entry, group) {
|
|
314
|
+
state.closingTogether.delete(entry);
|
|
315
|
+
group.pending.delete(entry);
|
|
316
|
+
completeClosingGroup(group);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Remember the last press in a document: a pointer down, a click (pointer,
|
|
321
|
+
* touch, keyboard activation or code: it is what runs the handler that flips
|
|
322
|
+
* the sheets) or a key down. One capture-phase listener per event per
|
|
323
|
+
* document, installed with the first sheet registered in it; the target is
|
|
324
|
+
* kept weakly.
|
|
325
|
+
*/
|
|
326
|
+
function listenForPresses(doc) {
|
|
327
|
+
if (state.listenedDocuments.has(doc)) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
const record = event => {
|
|
331
|
+
if (event.target instanceof Element) {
|
|
332
|
+
state.lastPress.set(doc, {
|
|
333
|
+
target: new WeakRef(event.target),
|
|
334
|
+
at: Date.now()
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
doc.addEventListener('pointerdown', record, true);
|
|
339
|
+
doc.addEventListener('click', record, true);
|
|
340
|
+
doc.addEventListener('keydown', record, true);
|
|
341
|
+
state.listenedDocuments.set(doc, () => {
|
|
342
|
+
doc.removeEventListener('pointerdown', record, true);
|
|
343
|
+
doc.removeEventListener('click', record, true);
|
|
344
|
+
doc.removeEventListener('keydown', record, true);
|
|
345
|
+
state.lastPress.delete(doc);
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* The document's last registered sheet left: nothing feeds the press record
|
|
351
|
+
* any more, so the listeners go (`architecture:react-component-runtime`
|
|
352
|
+
* INV5: a subscription lives as long as what reads it). The next sheet to
|
|
353
|
+
* register in the document installs them again.
|
|
354
|
+
*/
|
|
355
|
+
function stopListeningForPresses(doc) {
|
|
356
|
+
if (state.entries.some(entry => entry.doc === doc)) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const remove = state.listenedDocuments.get(doc);
|
|
360
|
+
if (remove != null) {
|
|
361
|
+
state.listenedDocuments.delete(doc);
|
|
362
|
+
remove();
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* A press is the actor of a flip only while it is fresh: the handler a press
|
|
368
|
+
* runs flips the sheets within the same task, milliseconds later. A flip
|
|
369
|
+
* that arrives later (a step change after a save resolves) is not that
|
|
370
|
+
* press's doing, and reads focus instead. A press is also spent once the
|
|
371
|
+
* sheet it landed in has been replaced or has closed — its target sits in
|
|
372
|
+
* an inert or no longer presented dialog — or once a replace has consumed
|
|
373
|
+
* it: a step advanced by code shortly after a Submit press (a "Processing"
|
|
374
|
+
* interstitial) is then judged by where focus is, inside the step, and
|
|
375
|
+
* hands off as DEP-0015 promises.
|
|
376
|
+
*/
|
|
377
|
+
const PRESS_ACTOR_WINDOW_MS = 1000;
|
|
378
|
+
function actorFor(doc, flipping) {
|
|
379
|
+
const press = state.lastPress.get(doc);
|
|
380
|
+
const target = press?.target.deref();
|
|
381
|
+
if (press != null && target != null && target.isConnected && Date.now() - press.at <= PRESS_ACTOR_WINDOW_MS && !isPressSpent(target, flipping)) {
|
|
382
|
+
return target;
|
|
383
|
+
}
|
|
384
|
+
return doc.activeElement;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* A press is spent when the sheet it landed in was already closed or
|
|
389
|
+
* replaced before this flip — not the sheet this very flip is closing, which
|
|
390
|
+
* is the ordinary replace made from inside it.
|
|
391
|
+
*/
|
|
392
|
+
function isPressSpent(target, flipping) {
|
|
393
|
+
const owning = state.entries.find(entry => {
|
|
394
|
+
const element = entry.getElement();
|
|
395
|
+
return entry.doc === flipping.doc && element != null && element.contains(target);
|
|
396
|
+
});
|
|
397
|
+
if (owning == null || owning === flipping) {
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
return !owning.isOpen || owning.replace != null;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/** Record a flip of the current batch, and who acted if it is the first. */
|
|
404
|
+
function recordFlip(entry, flip) {
|
|
405
|
+
if (state.batchOpened.length === 0 && state.batchClosed.length === 0) {
|
|
406
|
+
state.batchActor = actorFor(entry.doc, entry);
|
|
407
|
+
}
|
|
408
|
+
(flip === 'opened' ? state.batchOpened : state.batchClosed).push(entry);
|
|
409
|
+
}
|
|
410
|
+
function compareRank(a, b) {
|
|
411
|
+
if (a.rank.batch !== b.rank.batch) {
|
|
412
|
+
return a.rank.batch - b.rank.batch;
|
|
413
|
+
}
|
|
414
|
+
// One commit: tree order. A dialog that precedes another in document order
|
|
415
|
+
// (including one that contains the other) is below it.
|
|
416
|
+
const aEl = a.getTreeAnchor?.() ?? a.getElement();
|
|
417
|
+
const bEl = b.getTreeAnchor?.() ?? b.getElement();
|
|
418
|
+
if (aEl != null && bEl != null && aEl !== bEl) {
|
|
419
|
+
const position = aEl.compareDocumentPosition(bEl);
|
|
420
|
+
if (position & Node.DOCUMENT_POSITION_FOLLOWING) {
|
|
421
|
+
return -1;
|
|
422
|
+
}
|
|
423
|
+
if (position & Node.DOCUMENT_POSITION_PRECEDING) {
|
|
424
|
+
return 1;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return a.rank.seq - b.rank.seq;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/** The sheets of one group (document + modality), bottom to top. */
|
|
431
|
+
function sortedEntries(doc, isModal) {
|
|
432
|
+
return state.entries.filter(entry => entry.doc === doc && entry.isModal === isModal).sort(compareRank);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/** The sheets `entry` shares a group with, bottom to top. */
|
|
436
|
+
function groupOf(entry) {
|
|
437
|
+
return sortedEntries(entry.doc, entry.isModal);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/** The shown order of one group, read from or written to the shared state. */
|
|
441
|
+
function getShownOrder(doc, isModal) {
|
|
442
|
+
const orders = state.shownOrder.get(doc);
|
|
443
|
+
if (orders == null) {
|
|
444
|
+
return [];
|
|
445
|
+
}
|
|
446
|
+
return isModal ? orders.modal : orders.nonModal;
|
|
447
|
+
}
|
|
448
|
+
function setShownOrder(doc, isModal, order) {
|
|
449
|
+
const orders = state.shownOrder.get(doc) ?? {
|
|
450
|
+
modal: [],
|
|
451
|
+
nonModal: []
|
|
452
|
+
};
|
|
453
|
+
if (isModal) {
|
|
454
|
+
orders.modal = order;
|
|
455
|
+
} else {
|
|
456
|
+
orders.nonModal = order;
|
|
457
|
+
}
|
|
458
|
+
state.shownOrder.set(doc, orders);
|
|
459
|
+
}
|
|
460
|
+
function isSameView(a, b) {
|
|
461
|
+
return a.isStacked === b.isStacked && a.isTop === b.isTop && a.isCovered === b.isCovered && a.depth === b.depth && a.ownsScrim === b.ownsScrim && a.isScrimHandoff === b.isScrimHandoff && a.isBlocked === b.isBlocked && a.isScrimTakenOver === b.isScrimTakenOver && a.replacedMotion === b.replacedMotion && a.alignmentOffset === b.alignmentOffset && a.isReplacing === b.isReplacing;
|
|
462
|
+
}
|
|
463
|
+
function computeViews(order, isModal) {
|
|
464
|
+
const changed = [];
|
|
465
|
+
if (!isModal) {
|
|
466
|
+
// Non-modal sheets never stack: no recede, no scrim, no top-layer order
|
|
467
|
+
// against modal sheets. Their view carries only the replace a pair of
|
|
468
|
+
// them may be running.
|
|
469
|
+
for (const entry of order) {
|
|
470
|
+
const view = {
|
|
471
|
+
...BOTTOM_SHEET_STACK_DEFAULT_VIEW,
|
|
472
|
+
replacedMotion: entry.replace?.motion ?? null,
|
|
473
|
+
alignmentOffset: entry.replace?.alignmentOffset ?? 0,
|
|
474
|
+
isReplacing: entry.replacing != null
|
|
475
|
+
};
|
|
476
|
+
if (!isSameView(entry.view, view)) {
|
|
477
|
+
entry.view = view;
|
|
478
|
+
changed.push(entry);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
for (const entry of changed) {
|
|
482
|
+
entry.onView(entry.view);
|
|
483
|
+
}
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
const openEntries = order.filter(entry => entry.isOpen);
|
|
487
|
+
const top = openEntries[openEntries.length - 1] ?? null;
|
|
488
|
+
// A replaced sheet keeps the scrim while it is retained; the sheet replacing
|
|
489
|
+
// it takes the scrim over, instantly, when the replaced sheet leaves.
|
|
490
|
+
const owner = order.find(entry => entry.isOpen || entry.replace != null) ?? null;
|
|
491
|
+
order.forEach((entry, index) => {
|
|
492
|
+
let depth = 0;
|
|
493
|
+
for (let above = index + 1; above < order.length; above += 1) {
|
|
494
|
+
// The sheet replacing this one stands in its place, not above it.
|
|
495
|
+
if (order[above].isOpen && order[above] !== entry.replace?.replacer) {
|
|
496
|
+
depth += 1;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
const isCovered = entry.isOpen && depth > 0;
|
|
500
|
+
const element = entry.getElement();
|
|
501
|
+
const containsPresented = element != null && order.some(other => {
|
|
502
|
+
const otherElement = other.getElement();
|
|
503
|
+
return other !== entry && otherElement != null && element.contains(otherElement);
|
|
504
|
+
});
|
|
505
|
+
const ownsScrim = entry === owner;
|
|
506
|
+
const isScrimHandoff = ownsScrim && entry.isOpen && order.slice(0, index).some(below => !below.isOpen);
|
|
507
|
+
const view = {
|
|
508
|
+
isStacked: order.length > 1,
|
|
509
|
+
isTop: entry === top,
|
|
510
|
+
isCovered,
|
|
511
|
+
depth,
|
|
512
|
+
ownsScrim,
|
|
513
|
+
isScrimHandoff,
|
|
514
|
+
isBlocked: isCovered && !containsPresented,
|
|
515
|
+
isScrimTakenOver: !entry.isOpen && owner != null && owner !== entry,
|
|
516
|
+
replacedMotion: entry.replace?.motion ?? null,
|
|
517
|
+
alignmentOffset: entry.replace?.alignmentOffset ?? 0,
|
|
518
|
+
isReplacing: entry.replacing != null
|
|
519
|
+
};
|
|
520
|
+
if (!isSameView(entry.view, view)) {
|
|
521
|
+
entry.view = view;
|
|
522
|
+
changed.push(entry);
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
for (const entry of changed) {
|
|
526
|
+
entry.onView(entry.view);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Present a switcher step's dialog modally with no focus to return to.
|
|
532
|
+
* showModal() records the element focused at that moment and close() moves
|
|
533
|
+
* focus back to it, in Chromium and WebKit even when focus is in another
|
|
534
|
+
* dialog by then. For a step that element is the control that advanced the
|
|
535
|
+
* flow (Continue, Back) or the flow's opener, and the dialog closes a fade
|
|
536
|
+
* later, while the user is in the next step: the return would pull focus
|
|
537
|
+
* off the active panel with no user action. Blurring first leaves nothing
|
|
538
|
+
* recorded, so the close moves nothing; the entering step then focuses its
|
|
539
|
+
* own panel, and the switcher returns focus to the opener itself when the
|
|
540
|
+
* flow ends. The blur happens in the same task as the present, so no frame
|
|
541
|
+
* paints without focus.
|
|
542
|
+
*/
|
|
543
|
+
export function presentStepModally(dialog) {
|
|
544
|
+
const doc = dialog.ownerDocument;
|
|
545
|
+
const focused = doc.activeElement;
|
|
546
|
+
if (focused instanceof HTMLElement && focused !== doc.body) {
|
|
547
|
+
// Focus in another open dialog that does not contain this step — a sheet
|
|
548
|
+
// stacked over the flow, with the person typing in it while code moves
|
|
549
|
+
// the flow on — is theirs to keep: the registry puts that sheet back on
|
|
550
|
+
// top right after this and lands focus on this element again.
|
|
551
|
+
const focusedDialog = focused.closest('dialog');
|
|
552
|
+
if (focusedDialog != null && focusedDialog !== dialog && !focusedDialog.contains(dialog) && !dialog.contains(focusedDialog) && isSheetHostPresented(focusedDialog)) {
|
|
553
|
+
state.focusToKeep.set(doc, focused);
|
|
554
|
+
}
|
|
555
|
+
focused.blur();
|
|
556
|
+
}
|
|
557
|
+
dialog.showModal();
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Present a switcher dialog whose backdrop must start, and stay, clear: the
|
|
562
|
+
* opacity is set with the scrim transition pinned to 0s, the dialog is
|
|
563
|
+
* presented, and the pin is lifted only after a style flush has applied the
|
|
564
|
+
* clear backdrop, so no frame eases from a previous backdrop value.
|
|
565
|
+
*/
|
|
566
|
+
export function presentWithClearScrim(dialog) {
|
|
567
|
+
dialog.style.setProperty('--_sheet-scrim-duration', '0s');
|
|
568
|
+
dialog.style.setProperty('--_sheet-scrim-opacity', '0');
|
|
569
|
+
presentStepModally(dialog);
|
|
570
|
+
if (typeof getComputedStyle === 'function') {
|
|
571
|
+
void getComputedStyle(dialog, '::backdrop').opacity;
|
|
572
|
+
}
|
|
573
|
+
dialog.style.removeProperty('--_sheet-scrim-duration');
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/** The pending restore of a dialog's scrim transition; one per host. */
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Write a dialog's scrim opacity. A change while the dialog is up is a change
|
|
580
|
+
* of scrim owner, and lands with no transition at all: a `0.01s` duration
|
|
581
|
+
* still paints one frame at the old value, which reads as a dip or, with the
|
|
582
|
+
* old owner's backdrop still fading, as a doubled dim. The pin
|
|
583
|
+
* (`--_sheet-scrim-duration: 0s`, inline) stays for two animation frames
|
|
584
|
+
* (two nested `requestAnimationFrame`s: the first frame paints the new value,
|
|
585
|
+
* the second confirms it is on screen before the stylesheet's duration
|
|
586
|
+
* returns). Hosts cancel the pending restore with `cancelScrimWrite` when
|
|
587
|
+
* they unmount.
|
|
588
|
+
*/
|
|
589
|
+
export function writeScrimOpacity(dialog, opacity, pending) {
|
|
590
|
+
if (dialog == null) {
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
if (!dialog.open || dialog.style.getPropertyValue('--_sheet-scrim-opacity') === opacity) {
|
|
594
|
+
dialog.style.setProperty('--_sheet-scrim-opacity', opacity);
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
dialog.style.setProperty('--_sheet-scrim-duration', '0s');
|
|
598
|
+
dialog.style.setProperty('--_sheet-scrim-opacity', opacity);
|
|
599
|
+
cancelScrimWrite(pending);
|
|
600
|
+
pending.frame = requestAnimationFrame(() => {
|
|
601
|
+
pending.frame = requestAnimationFrame(() => {
|
|
602
|
+
pending.frame = null;
|
|
603
|
+
dialog.style.removeProperty('--_sheet-scrim-duration');
|
|
604
|
+
});
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
export function cancelScrimWrite(pending) {
|
|
608
|
+
if (pending.frame != null) {
|
|
609
|
+
cancelAnimationFrame(pending.frame);
|
|
610
|
+
pending.frame = null;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Whether a sheet's dialog is presented by the browser: as a modal dialog
|
|
616
|
+
* (`open`), as a manual popover (a non-modal sheet in the top layer), or by
|
|
617
|
+
* `show()` (a non-modal sheet below the Popover API support floor).
|
|
618
|
+
*/
|
|
619
|
+
export function isSheetHostPresented(dialog) {
|
|
620
|
+
if (dialog == null) {
|
|
621
|
+
return false;
|
|
622
|
+
}
|
|
623
|
+
if (dialog.open) {
|
|
624
|
+
return true;
|
|
625
|
+
}
|
|
626
|
+
try {
|
|
627
|
+
return dialog.matches(':popover-open');
|
|
628
|
+
} catch {
|
|
629
|
+
// jsdom and pre-Popover browsers do not recognise :popover-open.
|
|
630
|
+
return false;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Whether a modal dialog that does not contain `element` is open in its
|
|
636
|
+
* document. Such a modal makes everything outside it inert, so a non-modal
|
|
637
|
+
* sheet outside it must not paint above it.
|
|
638
|
+
*/
|
|
639
|
+
export function hasOpenModalOutside(element) {
|
|
640
|
+
return openModalsOutside(element).length > 0;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/** The open modal dialogs that do not contain `element`; see above. */
|
|
644
|
+
function openModalsOutside(element) {
|
|
645
|
+
const blockers = [];
|
|
646
|
+
for (const dialog of Array.from(element.ownerDocument.querySelectorAll('dialog[open]'))) {
|
|
647
|
+
if (dialog === element || dialog.contains(element)) {
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
// A modal on its way out (a closing sheet keeps its dialog modal through
|
|
651
|
+
// the exit animation, inert and hidden) blocks nothing any more: it is
|
|
652
|
+
// gone for this decision.
|
|
653
|
+
if (dialog.hasAttribute('inert') || dialog.getAttribute('aria-hidden') === 'true') {
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
if (isModalDialog(dialog)) {
|
|
657
|
+
blockers.push(dialog);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
return blockers;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* Non-modal sheets presented by `show()` because a modal outside them was
|
|
665
|
+
* open, waiting to enter the top layer once it has gone. Per document, with
|
|
666
|
+
* one `close` listener per document: any dialog closing is a reason to look
|
|
667
|
+
* again, and so is every sheet-stack update (a closing modal sheet turns
|
|
668
|
+
* inert before its dialog closes). An open modal removed from the document
|
|
669
|
+
* fires no `close` — a `{isEditing && <Dialog isOpen>}` that unmounts while
|
|
670
|
+
* open — so while anything waits, a mutation observer watches removals too
|
|
671
|
+
* and looks again when a removed subtree held one of the dialogs that caused
|
|
672
|
+
* a deferral. Those blockers are recorded at defer time: `:modal` is the
|
|
673
|
+
* mark that matters, and it leaves with the dialog's top-layer membership,
|
|
674
|
+
* so it cannot be read off a removed node (nor can `aria-modal`, which a
|
|
675
|
+
* `showModal()` host such as MobileNav or Lightbox does not set).
|
|
676
|
+
*/
|
|
677
|
+
|
|
678
|
+
function deferTopLayerEntry(dialog, panel, blockers) {
|
|
679
|
+
const doc = dialog.ownerDocument;
|
|
680
|
+
let entry = state.deferredTopLayer.get(doc);
|
|
681
|
+
if (entry == null) {
|
|
682
|
+
entry = {
|
|
683
|
+
deferred: new Map(),
|
|
684
|
+
blockers: new WeakSet(),
|
|
685
|
+
removals: null,
|
|
686
|
+
stopListeningForClose: null
|
|
687
|
+
};
|
|
688
|
+
state.deferredTopLayer.set(doc, entry);
|
|
689
|
+
}
|
|
690
|
+
if (entry.stopListeningForClose == null) {
|
|
691
|
+
// Installed with the first deferral and removed with the last promotion
|
|
692
|
+
// (`architecture:react-component-runtime` INV5), like the observer below.
|
|
693
|
+
const onClose = () => {
|
|
694
|
+
promoteDeferredNonModal(doc);
|
|
695
|
+
};
|
|
696
|
+
doc.addEventListener('close', onClose, true);
|
|
697
|
+
entry.stopListeningForClose = () => {
|
|
698
|
+
doc.removeEventListener('close', onClose, true);
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
entry.deferred.set(dialog, panel);
|
|
702
|
+
for (const blocker of blockers) {
|
|
703
|
+
entry.blockers.add(blocker);
|
|
704
|
+
}
|
|
705
|
+
if (entry.removals == null && typeof MutationObserver === 'function') {
|
|
706
|
+
const {
|
|
707
|
+
blockers: known
|
|
708
|
+
} = entry;
|
|
709
|
+
const observer = new MutationObserver(records => {
|
|
710
|
+
if (records.some(record => removedABlocker(record, known))) {
|
|
711
|
+
promoteDeferredNonModal(doc);
|
|
712
|
+
}
|
|
713
|
+
});
|
|
714
|
+
observer.observe(doc, {
|
|
715
|
+
childList: true,
|
|
716
|
+
subtree: true
|
|
717
|
+
});
|
|
718
|
+
entry.removals = observer;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
function removedABlocker(record, blockers) {
|
|
722
|
+
for (const node of Array.from(record.removedNodes)) {
|
|
723
|
+
if (!(node instanceof Element)) {
|
|
724
|
+
continue;
|
|
725
|
+
}
|
|
726
|
+
const dialogs = node instanceof HTMLDialogElement ? [node] : Array.from(node.querySelectorAll('dialog'));
|
|
727
|
+
if (dialogs.some(dialog => blockers.has(dialog))) {
|
|
728
|
+
return true;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
return false;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* Move every waiting non-modal sheet whose outside modals have all gone into
|
|
736
|
+
* the top layer: it leaves `show()` and is presented again as a manual
|
|
737
|
+
* popover, announcing its entry so global hosts re-enter above it. Focus
|
|
738
|
+
* stays where it was.
|
|
739
|
+
*/
|
|
740
|
+
export function promoteDeferredNonModal(doc) {
|
|
741
|
+
const entry = state.deferredTopLayer.get(doc);
|
|
742
|
+
if (entry == null || entry.deferred.size === 0) {
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
const {
|
|
746
|
+
deferred
|
|
747
|
+
} = entry;
|
|
748
|
+
for (const [dialog, panel] of Array.from(deferred)) {
|
|
749
|
+
if (!dialog.isConnected || !dialog.open) {
|
|
750
|
+
deferred.delete(dialog);
|
|
751
|
+
continue;
|
|
752
|
+
}
|
|
753
|
+
const blockers = openModalsOutside(dialog);
|
|
754
|
+
if (blockers.length > 0) {
|
|
755
|
+
// Still waiting, maybe on a modal that was not there at defer time (one
|
|
756
|
+
// Dialog replaced by another in a single update): it is a blocker now,
|
|
757
|
+
// and its removal must be a reason to look again too.
|
|
758
|
+
for (const blocker of blockers) {
|
|
759
|
+
entry.blockers.add(blocker);
|
|
760
|
+
}
|
|
761
|
+
continue;
|
|
762
|
+
}
|
|
763
|
+
deferred.delete(dialog);
|
|
764
|
+
const activeElement = doc.activeElement;
|
|
765
|
+
dialog.close();
|
|
766
|
+
showSheetHostNonModal(dialog, panel);
|
|
767
|
+
if (doc.activeElement !== activeElement && activeElement instanceof HTMLElement && activeElement.isConnected) {
|
|
768
|
+
activeElement.focus({
|
|
769
|
+
preventScroll: true
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
if (deferred.size === 0) {
|
|
774
|
+
releaseDeferralWatchers(entry);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
function releaseDeferralWatchers(entry) {
|
|
778
|
+
entry.removals?.disconnect();
|
|
779
|
+
entry.removals = null;
|
|
780
|
+
entry.stopListeningForClose?.();
|
|
781
|
+
entry.stopListeningForClose = null;
|
|
782
|
+
}
|
|
783
|
+
function isModalDialog(dialog) {
|
|
784
|
+
// `:modal` is the browser's own answer (every browser with the Popover
|
|
785
|
+
// API has it). jsdom answers false to it for every dialog, so the modal
|
|
786
|
+
// hosts' own `aria-modal="true"` (Dialog, a scrim BottomSheet) decides
|
|
787
|
+
// there.
|
|
788
|
+
try {
|
|
789
|
+
if (dialog.matches(':modal')) {
|
|
790
|
+
return true;
|
|
791
|
+
}
|
|
792
|
+
} catch {
|
|
793
|
+
// An engine without :modal; fall through to the attribute.
|
|
794
|
+
}
|
|
795
|
+
return dialog.getAttribute('aria-modal') === 'true';
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* Present a non-modal sheet: in the top layer as a manual popover where the
|
|
800
|
+
* Popover API exists (Tier 2 and up), so later sheets order above earlier
|
|
801
|
+
* ones and no page-level z-index is needed; by `show()` below that floor.
|
|
802
|
+
* A manual popover has no light dismiss, no backdrop and no focus or scroll
|
|
803
|
+
* side effects, so the non-modal contract is otherwise unchanged.
|
|
804
|
+
*
|
|
805
|
+
* A non-modal sheet shown while a modal dialog that does not contain it is
|
|
806
|
+
* open (a modal sheet, a Dialog) is inert under that modal, so it is not put
|
|
807
|
+
* in the top layer — there it would paint above the modal while a press on
|
|
808
|
+
* it lands on the modal beneath — and is presented by `show()` instead, in
|
|
809
|
+
* page stacking order beneath the modal, with no page-level z-index; once
|
|
810
|
+
* the last such modal has gone it is presented again in the top layer
|
|
811
|
+
* (`promoteDeferredNonModal`). A non-modal sheet rendered inside the modal's
|
|
812
|
+
* content is not inert and enters the top layer at once.
|
|
813
|
+
*/
|
|
814
|
+
export function showSheetHostNonModal(dialog, panel) {
|
|
815
|
+
if (typeof dialog.showPopover !== 'function') {
|
|
816
|
+
dialog.show();
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
const blockers = openModalsOutside(dialog);
|
|
820
|
+
if (blockers.length > 0) {
|
|
821
|
+
dialog.show();
|
|
822
|
+
deferTopLayerEntry(dialog, panel, blockers);
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
// Both native paths run the engine's focusing steps, and engines differ.
|
|
826
|
+
// Chromium's `show()` lands on the first focusable area in tree order,
|
|
827
|
+
// the panel (`tabindex="-1"`, no focus ring), while its `showPopover()`
|
|
828
|
+
// skips such containers and lands on the first control; Firefox lands on
|
|
829
|
+
// the first control on both paths. The sheet must focus as it did with
|
|
830
|
+
// `show()` in the engine at hand, and no control may be focused and
|
|
831
|
+
// blurred on the way (a field validating on blur would show its error on
|
|
832
|
+
// open). So the popover's focusing steps are pointed at the panel first:
|
|
833
|
+
// `autofocus` on the panel for the duration of `showPopover()` makes it
|
|
834
|
+
// the steps' autofocus delegate in every engine, and the panel is where a
|
|
835
|
+
// container-focusing engine's `show()` lands. Where the engine's `show()`
|
|
836
|
+
// skips the container and lands on the first control, focus moves from
|
|
837
|
+
// the panel to that control once — a focus on the control, no blur.
|
|
838
|
+
const doc = dialog.ownerDocument;
|
|
839
|
+
const hadAutofocus = panel?.hasAttribute('autofocus') ?? false;
|
|
840
|
+
if (panel != null && !hadAutofocus) {
|
|
841
|
+
panel.setAttribute('autofocus', '');
|
|
842
|
+
}
|
|
843
|
+
try {
|
|
844
|
+
dialog.showPopover();
|
|
845
|
+
} finally {
|
|
846
|
+
if (panel != null && !hadAutofocus) {
|
|
847
|
+
panel.removeAttribute('autofocus');
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
// The sheet is now the newest top-layer entry: global hosts shown before
|
|
851
|
+
// it (the toast viewport) re-enter above it, as they do above a modal.
|
|
852
|
+
notifyTopLayerEntry();
|
|
853
|
+
if (panel != null && doc.activeElement === panel && !nativeShowFocusesContainer(dialog)) {
|
|
854
|
+
firstSequentiallyFocusable(panel)?.focus({
|
|
855
|
+
preventScroll: true
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/**
|
|
861
|
+
* The first element inside `root`, in tree order, that sequential focus
|
|
862
|
+
* navigation reaches: focusable, with a tab index of zero or more, not
|
|
863
|
+
* disabled, not inert, and rendered. This is where an engine whose
|
|
864
|
+
* `show()` skips programmatically focusable containers lands.
|
|
865
|
+
*/
|
|
866
|
+
function firstSequentiallyFocusable(root) {
|
|
867
|
+
const candidates = root.querySelectorAll('a[href], area[href], button, input, select, textarea, iframe, summary, [contenteditable]:not([contenteditable="false"]), [tabindex]');
|
|
868
|
+
for (const candidate of Array.from(candidates)) {
|
|
869
|
+
if (candidate === root || candidate.tabIndex < 0) {
|
|
870
|
+
continue;
|
|
871
|
+
}
|
|
872
|
+
if (candidate.disabled === true || candidate.closest('[inert]') != null || candidate.getAttribute('type') === 'hidden' || candidate.hidden || typeof candidate.checkVisibility === 'function' && !candidate.checkVisibility()) {
|
|
873
|
+
continue;
|
|
874
|
+
}
|
|
875
|
+
return candidate;
|
|
876
|
+
}
|
|
877
|
+
return null;
|
|
878
|
+
}
|
|
879
|
+
let showFocusesContainer = null;
|
|
880
|
+
|
|
881
|
+
/**
|
|
882
|
+
* Whether this engine's `dialog.show()` focusing steps land on a
|
|
883
|
+
* programmatically focusable container (`tabindex="-1"`) that comes before
|
|
884
|
+
* the first control — Chromium does, Firefox skips it. Measured once, on a
|
|
885
|
+
* throwaway non-modal dialog that is shown, read and removed inside one
|
|
886
|
+
* task (no paint); focus is put back where it was. The probe lives inside
|
|
887
|
+
* `host`, the sheet's own dialog, which is in the top layer and interactive
|
|
888
|
+
* at this point (a lone sheet, or one inside the open modal's content): a
|
|
889
|
+
* probe appended to the body could sit under an open modal's inertness,
|
|
890
|
+
* where `show()` moves no focus, and cache the wrong answer for the page.
|
|
891
|
+
*/
|
|
892
|
+
function nativeShowFocusesContainer(host) {
|
|
893
|
+
if (showFocusesContainer != null) {
|
|
894
|
+
return showFocusesContainer;
|
|
895
|
+
}
|
|
896
|
+
const doc = host.ownerDocument;
|
|
897
|
+
const activeBefore = doc.activeElement;
|
|
898
|
+
const probe = doc.createElement('dialog');
|
|
899
|
+
probe.setAttribute('aria-hidden', 'true');
|
|
900
|
+
probe.style.cssText = 'position:fixed;inset:auto;opacity:0;pointer-events:none';
|
|
901
|
+
const container = doc.createElement('div');
|
|
902
|
+
container.tabIndex = -1;
|
|
903
|
+
const control = doc.createElement('button');
|
|
904
|
+
control.type = 'button';
|
|
905
|
+
container.appendChild(control);
|
|
906
|
+
probe.appendChild(container);
|
|
907
|
+
host.appendChild(probe);
|
|
908
|
+
try {
|
|
909
|
+
probe.show();
|
|
910
|
+
showFocusesContainer = doc.activeElement === container;
|
|
911
|
+
probe.close();
|
|
912
|
+
} catch {
|
|
913
|
+
showFocusesContainer = true;
|
|
914
|
+
} finally {
|
|
915
|
+
probe.remove();
|
|
916
|
+
if (doc.activeElement !== activeBefore && activeBefore instanceof HTMLElement && activeBefore.isConnected) {
|
|
917
|
+
activeBefore.focus({
|
|
918
|
+
preventScroll: true
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
return showFocusesContainer;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
/** Leave the native host, whichever one presents the sheet. */
|
|
926
|
+
export function hideSheetHost(dialog) {
|
|
927
|
+
const entry = state.deferredTopLayer.get(dialog.ownerDocument);
|
|
928
|
+
if (entry != null && entry.deferred.delete(dialog)) {
|
|
929
|
+
if (entry.deferred.size === 0) {
|
|
930
|
+
releaseDeferralWatchers(entry);
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
if (dialog.hasAttribute('popover') && typeof dialog.hidePopover === 'function') {
|
|
934
|
+
try {
|
|
935
|
+
dialog.hidePopover();
|
|
936
|
+
} catch {
|
|
937
|
+
// Not showing as a popover (the show() fallback); nothing to hide here.
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
if (dialog.open) {
|
|
941
|
+
dialog.close();
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
/**
|
|
946
|
+
* Close a dialog without letting the browser move focus. `close()` returns
|
|
947
|
+
* focus to the element that was focused when `showModal()` ran; with another
|
|
948
|
+
* sheet still open on top, that would pull focus out of the sheet the person
|
|
949
|
+
* is using (a re-show that repairs the top-layer order, a covered sheet
|
|
950
|
+
* closing from code, a replaced sheet leaving). Focus stays where it is; the
|
|
951
|
+
* caller decides where it goes next, if anywhere.
|
|
952
|
+
*/
|
|
953
|
+
export function closeDialogKeepingFocus(dialog) {
|
|
954
|
+
if (dialog == null || !isSheetHostPresented(dialog)) {
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
const doc = dialog.ownerDocument;
|
|
958
|
+
const activeElement = doc.activeElement;
|
|
959
|
+
hideSheetHost(dialog);
|
|
960
|
+
if (doc.activeElement !== activeElement && activeElement instanceof HTMLElement && activeElement.isConnected) {
|
|
961
|
+
activeElement.focus({
|
|
962
|
+
preventScroll: true
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
/**
|
|
968
|
+
* Pair the one close and the one open of this batch as a replace. A replace
|
|
969
|
+
* is a move the person makes from one sheet to the next, and that is what
|
|
970
|
+
* the conditions check:
|
|
971
|
+
*
|
|
972
|
+
* - exactly one sheet closed and exactly one opened in this update;
|
|
973
|
+
* - both are standalone sheets of one group — one document, both modal or
|
|
974
|
+
* both non-modal (a switcher flow replaces its own steps and is never
|
|
975
|
+
* paired);
|
|
976
|
+
* - the closing sheet was the top sheet of its stack once the opening sheet
|
|
977
|
+
* is set aside — a covered sheet closing while a sheet opens is a pop
|
|
978
|
+
* beneath a push;
|
|
979
|
+
* - neither dialog contains the other — a sheet in another's content closes
|
|
980
|
+
* with it, it does not replace it;
|
|
981
|
+
* - the interaction came from inside the closing sheet: the control the
|
|
982
|
+
* person last pressed (or, with no recent press, the element focused when
|
|
983
|
+
* this update's first flip arrived, before any dialog of the update was
|
|
984
|
+
* shown) is in its dialog, or nowhere (a control that left with the step).
|
|
985
|
+
* A close and an open driven from a control outside the closing sheet are
|
|
986
|
+
* two unrelated owners acting in one update, so the sheets keep their own
|
|
987
|
+
* openers and their own push and pop motion.
|
|
988
|
+
*
|
|
989
|
+
* The opening sheet then adopts the closing sheet's opener: its own opener
|
|
990
|
+
* is inside the sheet that is leaving, and the flow began where the closing
|
|
991
|
+
* sheet's did.
|
|
992
|
+
*/
|
|
993
|
+
function detectReplace() {
|
|
994
|
+
if (state.batchOpened.length !== 1 || state.batchClosed.length !== 1) {
|
|
995
|
+
// A pair made earlier in this commit, before a third flip arrived, is
|
|
996
|
+
// undone: the commit's final tally decides, and several sheets changing
|
|
997
|
+
// in one update keep their own push and pop motion.
|
|
998
|
+
undoBatchPair();
|
|
999
|
+
return;
|
|
1000
|
+
}
|
|
1001
|
+
const [replacer] = state.batchOpened;
|
|
1002
|
+
const [replaced] = state.batchClosed;
|
|
1003
|
+
if (state.batchPair != null && state.batchPair.replaced === replaced) {
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
if (replaced === replacer || replaced.replaceable == null || replacer.replaceable == null || replaced.doc !== replacer.doc || replaced.isModal !== replacer.isModal || replaced.replace?.replacer === replacer || !replacer.isOpen || replaced.isOpen) {
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
const replacedElement = replaced.getElement();
|
|
1010
|
+
const replacerElement = replacer.getElement();
|
|
1011
|
+
if (replacedElement == null || replacerElement == null || replacedElement.contains(replacerElement) || replacerElement.contains(replacedElement)) {
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
const wasTop = !groupOf(replaced).some(other => other !== replacer && other !== replaced && other.isOpen && compareRank(other, replaced) > 0);
|
|
1015
|
+
if (!wasTop) {
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
const actor = state.batchActor;
|
|
1019
|
+
const actedFromReplaced = actor == null || actor === replaced.doc.body || replacedElement.contains(actor);
|
|
1020
|
+
if (!actedFromReplaced) {
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
1023
|
+
// A third sheet the closing sheet was itself replacing is dropped at once,
|
|
1024
|
+
// the way a switcher drops a step that is left mid-handoff. The sheet it
|
|
1025
|
+
// was replacing may instead be the one coming back now (a reversal): then
|
|
1026
|
+
// the roles swap and nothing is dropped.
|
|
1027
|
+
if (replaced.replacing != null && replaced.replacing !== replacer) {
|
|
1028
|
+
finishReplace(replaced.replacing);
|
|
1029
|
+
}
|
|
1030
|
+
if (replaced.replace != null) {
|
|
1031
|
+
replaced.replace.replacer.replacing = null;
|
|
1032
|
+
}
|
|
1033
|
+
if (replacer.replace != null) {
|
|
1034
|
+
replacer.replace = null;
|
|
1035
|
+
}
|
|
1036
|
+
replaced.replace = {
|
|
1037
|
+
replacer,
|
|
1038
|
+
motion: 'covered',
|
|
1039
|
+
alignmentOffset: 0,
|
|
1040
|
+
isAlignmentComplete: false,
|
|
1041
|
+
isEnteringComplete: false
|
|
1042
|
+
};
|
|
1043
|
+
replacer.replacing = replaced;
|
|
1044
|
+
state.batchPair = {
|
|
1045
|
+
replaced,
|
|
1046
|
+
replacer,
|
|
1047
|
+
replacerOpener: replacer.replaceable.getOpener()
|
|
1048
|
+
};
|
|
1049
|
+
// The press that made this replace is consumed by it.
|
|
1050
|
+
state.lastPress.delete(replacer.doc);
|
|
1051
|
+
replacer.replaceable.setOpener(replaced.replaceable.getOpener());
|
|
1052
|
+
state.pendingReplaceFocus = replacer;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
/**
|
|
1056
|
+
* Undo the pair this batch made: the replaced sheet is an ordinary closing
|
|
1057
|
+
* sheet again, the replacer an ordinary opening one with its own opener, and
|
|
1058
|
+
* no focus is owed. Called when a later flip of the same commit changes the
|
|
1059
|
+
* tally, before the commit paints.
|
|
1060
|
+
*/
|
|
1061
|
+
function undoBatchPair() {
|
|
1062
|
+
const pair = state.batchPair;
|
|
1063
|
+
if (pair == null) {
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
state.batchPair = null;
|
|
1067
|
+
const {
|
|
1068
|
+
replaced,
|
|
1069
|
+
replacer,
|
|
1070
|
+
replacerOpener
|
|
1071
|
+
} = pair;
|
|
1072
|
+
if (replaced.replace?.replacer === replacer) {
|
|
1073
|
+
replaced.replace = null;
|
|
1074
|
+
}
|
|
1075
|
+
if (replacer.replacing === replaced) {
|
|
1076
|
+
replacer.replacing = null;
|
|
1077
|
+
}
|
|
1078
|
+
replacer.replaceable?.setOpener(replacerOpener);
|
|
1079
|
+
if (state.pendingReplaceFocus === replacer) {
|
|
1080
|
+
state.pendingReplaceFocus = null;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
/**
|
|
1085
|
+
* The person moves from the replaced step to the entering one, so focus
|
|
1086
|
+
* does too, and it lands on the step's `data-autofocus` descendant when it
|
|
1087
|
+
* has one. The dialog is shown by now, so the native focusing steps have
|
|
1088
|
+
* run: a modal replacer's `showModal()` put focus on the entering panel, a
|
|
1089
|
+
* non-modal replacer's `showPopover()` may have left it where it was (Safari;
|
|
1090
|
+
* the replaced step is inert from this frame). Either way the placement runs
|
|
1091
|
+
* after them — focus sitting on the panel, or anywhere that is not the
|
|
1092
|
+
* `data-autofocus` descendant, is the native steps' doing, not the
|
|
1093
|
+
* person's, so it does not count as placed. Only focus the person already
|
|
1094
|
+
* moved to a control inside the entering step is left alone.
|
|
1095
|
+
*/
|
|
1096
|
+
function focusPendingReplacer() {
|
|
1097
|
+
const replacer = state.pendingReplaceFocus;
|
|
1098
|
+
if (replacer == null) {
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
const element = replacer.getElement();
|
|
1102
|
+
if (element == null || !isSheetHostPresented(element)) {
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
state.pendingReplaceFocus = null;
|
|
1106
|
+
replacer.focus('replace');
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
/**
|
|
1110
|
+
* End a replace: the replaced sheet is hidden and its dialog closed; the
|
|
1111
|
+
* views computed next hand the scrim to the sheet that replaced it, which
|
|
1112
|
+
* takes it in the same frame. Focus stays where it is.
|
|
1113
|
+
*/
|
|
1114
|
+
function finishReplace(replaced) {
|
|
1115
|
+
const replace = replaced.replace;
|
|
1116
|
+
if (replace == null) {
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
const {
|
|
1120
|
+
replacer
|
|
1121
|
+
} = replace;
|
|
1122
|
+
replaced.replace = null;
|
|
1123
|
+
if (replacer.replacing === replaced) {
|
|
1124
|
+
replacer.replacing = null;
|
|
1125
|
+
}
|
|
1126
|
+
replaced.hide();
|
|
1127
|
+
replaced.replaceable?.finish();
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
/**
|
|
1131
|
+
* The viewport top a panel rests at: its box without the translation it is
|
|
1132
|
+
* still travelling under (an entering panel rises from below the viewport).
|
|
1133
|
+
* Read from the panel itself, never from the element around it: a standalone
|
|
1134
|
+
* sheet's panel sits directly in its dialog, which spans the viewport, so a
|
|
1135
|
+
* parent's top is not where the panel rests.
|
|
1136
|
+
*/
|
|
1137
|
+
function restingTopOfPanel(panel) {
|
|
1138
|
+
const top = panel.getBoundingClientRect().top;
|
|
1139
|
+
const view = panel.ownerDocument.defaultView;
|
|
1140
|
+
if (view == null || typeof view.DOMMatrixReadOnly !== 'function') {
|
|
1141
|
+
return top;
|
|
1142
|
+
}
|
|
1143
|
+
const {
|
|
1144
|
+
transform
|
|
1145
|
+
} = view.getComputedStyle(panel);
|
|
1146
|
+
if (transform === '' || transform === 'none') {
|
|
1147
|
+
return top;
|
|
1148
|
+
}
|
|
1149
|
+
// A sheet panel only ever translates on the block axis (entrance, drag,
|
|
1150
|
+
// alignment); its computed matrix's `m42` is that translation.
|
|
1151
|
+
return top - new view.DOMMatrixReadOnly(transform).m42;
|
|
1152
|
+
}
|
|
1153
|
+
function alignmentOffsetForPanels(enteringPanel, retainedPanel) {
|
|
1154
|
+
if (enteringPanel == null || retainedPanel == null) {
|
|
1155
|
+
return 0;
|
|
1156
|
+
}
|
|
1157
|
+
// The retained panel is at rest; the entering panel is measured where its
|
|
1158
|
+
// entrance will leave it.
|
|
1159
|
+
return Math.max(0, restingTopOfPanel(enteringPanel) - retainedPanel.getBoundingClientRect().top);
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
/**
|
|
1163
|
+
* A host's panel reports the start and end of its motions here. During a
|
|
1164
|
+
* replace they drive the handoff: when the entering sheet starts to rise, a
|
|
1165
|
+
* taller retained sheet starts moving down to its top edge; the retained
|
|
1166
|
+
* sheet fades once both have finished; when the fade ends the replace is over.
|
|
1167
|
+
*/
|
|
1168
|
+
export function reportBottomSheetMotion(key, event, motion) {
|
|
1169
|
+
const entry = state.entries.find(candidate => candidate.key === key);
|
|
1170
|
+
if (entry == null) {
|
|
1171
|
+
return;
|
|
1172
|
+
}
|
|
1173
|
+
if (motion === 'entering') {
|
|
1174
|
+
const replaced = entry.replacing;
|
|
1175
|
+
const replace = replaced?.replace ?? null;
|
|
1176
|
+
if (replaced == null || replace == null || replace.replacer !== entry) {
|
|
1177
|
+
return;
|
|
1178
|
+
}
|
|
1179
|
+
if (event === 'start') {
|
|
1180
|
+
if (replace.motion !== 'covered') {
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
const alignmentOffset = alignmentOffsetForPanels(entry.replaceable?.getPanel() ?? null, replaced.replaceable?.getPanel() ?? null);
|
|
1184
|
+
if (alignmentOffset > ALIGNMENT_THRESHOLD_PX) {
|
|
1185
|
+
replace.motion = 'aligning';
|
|
1186
|
+
replace.alignmentOffset = alignmentOffset;
|
|
1187
|
+
replace.isAlignmentComplete = false;
|
|
1188
|
+
}
|
|
1189
|
+
// A sheet that came back while fading is already where it belongs:
|
|
1190
|
+
// its entrance is over as soon as it starts, like a switcher step's.
|
|
1191
|
+
if (entry.isEnteringInPlace) {
|
|
1192
|
+
entry.isEnteringInPlace = false;
|
|
1193
|
+
replace.isEnteringComplete = true;
|
|
1194
|
+
if (replace.motion !== 'aligning') {
|
|
1195
|
+
replace.motion = 'fading';
|
|
1196
|
+
}
|
|
1197
|
+
} else if (replace.motion === 'covered') {
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
} else {
|
|
1201
|
+
replace.isEnteringComplete = true;
|
|
1202
|
+
if (replace.motion === 'aligning' && !replace.isAlignmentComplete) {
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1205
|
+
replace.motion = 'fading';
|
|
1206
|
+
}
|
|
1207
|
+
update(entry.doc, entry.isModal);
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
const replace = entry.replace;
|
|
1211
|
+
if (replace == null || event !== 'complete') {
|
|
1212
|
+
return;
|
|
1213
|
+
}
|
|
1214
|
+
if (motion === 'aligning' && replace.motion === 'aligning') {
|
|
1215
|
+
if (replace.isEnteringComplete) {
|
|
1216
|
+
replace.motion = 'fading';
|
|
1217
|
+
} else {
|
|
1218
|
+
replace.isAlignmentComplete = true;
|
|
1219
|
+
}
|
|
1220
|
+
update(entry.doc, entry.isModal);
|
|
1221
|
+
} else if (motion === 'fading' && replace.motion === 'fading') {
|
|
1222
|
+
finishReplace(entry);
|
|
1223
|
+
update(entry.doc, entry.isModal);
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
/**
|
|
1228
|
+
* Make a document's top layer match the ranked order. The top layer is
|
|
1229
|
+
* append-only, so a dialog can only move up, by closing and showing it again:
|
|
1230
|
+
* the longest prefix of the wanted order that the shown order already
|
|
1231
|
+
* contains, in order, is kept, and every dialog after it is re-shown in
|
|
1232
|
+
* order (closing them from the top first). A deep link whose inner sheet
|
|
1233
|
+
* registered first therefore re-shows inner and outer; a sheet that must
|
|
1234
|
+
* rise above the sheets shown after it re-shows only itself, so a retained
|
|
1235
|
+
* sheet beneath it keeps its backdrop and any motion in flight. Runs inside
|
|
1236
|
+
* the commit, so a wrong intermediate order never paints. Focus moves into
|
|
1237
|
+
* the top open sheet when anything was shown — unless the person was already
|
|
1238
|
+
* in a sheet that is only re-shown to keep its place (a switcher flow it
|
|
1239
|
+
* covers changed step from code, say): the element they were in, a field
|
|
1240
|
+
* being typed in, is the dialog's focus delegate for that `showModal()` and
|
|
1241
|
+
* is focused again right after it, so the keystrokes that follow still land
|
|
1242
|
+
* there. The top layer is append-only, so the re-show has to close the dialog
|
|
1243
|
+
* first and the field sees one blur and one focus; nothing shorter exists.
|
|
1244
|
+
*/
|
|
1245
|
+
function reconcileTopLayer(doc, isModal, order) {
|
|
1246
|
+
const shownOrder = getShownOrder(doc, isModal);
|
|
1247
|
+
const presentable = order.filter(entry => {
|
|
1248
|
+
const element = entry.getElement();
|
|
1249
|
+
return element != null && element.isConnected;
|
|
1250
|
+
});
|
|
1251
|
+
let kept = 0;
|
|
1252
|
+
let cursor = 0;
|
|
1253
|
+
while (kept < presentable.length) {
|
|
1254
|
+
const next = shownOrder.indexOf(presentable[kept], cursor);
|
|
1255
|
+
if (next === -1) {
|
|
1256
|
+
break;
|
|
1257
|
+
}
|
|
1258
|
+
cursor = next + 1;
|
|
1259
|
+
kept += 1;
|
|
1260
|
+
}
|
|
1261
|
+
const keptEntries = new Set(presentable.slice(0, kept));
|
|
1262
|
+
const reshown = presentable.slice(kept);
|
|
1263
|
+
const keptActive = activeElementToKeep(doc, reshown);
|
|
1264
|
+
let didShow = false;
|
|
1265
|
+
for (let index = shownOrder.length - 1; index >= 0; index -= 1) {
|
|
1266
|
+
if (!keptEntries.has(shownOrder[index])) {
|
|
1267
|
+
shownOrder[index].hide();
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
for (const entry of reshown) {
|
|
1271
|
+
const delegate = keptActive != null && entry.getElement()?.contains(keptActive) ? keptActive : null;
|
|
1272
|
+
// The native focusing steps follow an `autofocus` delegate, so the
|
|
1273
|
+
// re-shown dialog lands on the element the person was in instead of its
|
|
1274
|
+
// first focusable area; the attribute is the dialog's for this call only.
|
|
1275
|
+
const hadAutofocus = delegate?.hasAttribute('autofocus') ?? false;
|
|
1276
|
+
if (delegate != null && !hadAutofocus) {
|
|
1277
|
+
delegate.setAttribute('autofocus', '');
|
|
1278
|
+
}
|
|
1279
|
+
entry.show();
|
|
1280
|
+
if (delegate != null && !hadAutofocus) {
|
|
1281
|
+
delegate.removeAttribute('autofocus');
|
|
1282
|
+
}
|
|
1283
|
+
didShow = true;
|
|
1284
|
+
}
|
|
1285
|
+
setShownOrder(doc, isModal, presentable);
|
|
1286
|
+
if (!didShow) {
|
|
1287
|
+
return;
|
|
1288
|
+
}
|
|
1289
|
+
// A dialog shown again (closed and re-shown to keep its place) went back
|
|
1290
|
+
// into the top layer above everything shown before, a global host's
|
|
1291
|
+
// popover hosted inside it included: that popover still reads as showing
|
|
1292
|
+
// but paints beneath the dialog now. Hosts re-enter the top layer in
|
|
1293
|
+
// place, as they do above a non-modal surface that just entered, so toasts
|
|
1294
|
+
// raised in the re-shown sheet still paint above it. A first show needs
|
|
1295
|
+
// none of this: the outlet it supplies moves the hosts.
|
|
1296
|
+
if (reshown.some(entry => shownOrder.includes(entry))) {
|
|
1297
|
+
notifyTopLayerEntry();
|
|
1298
|
+
}
|
|
1299
|
+
if (keptActive != null && keptActive.isConnected) {
|
|
1300
|
+
if (doc.activeElement !== keptActive) {
|
|
1301
|
+
keptActive.focus({
|
|
1302
|
+
preventScroll: true
|
|
1303
|
+
});
|
|
1304
|
+
}
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
const openEntries = presentable.filter(entry => entry.isOpen);
|
|
1308
|
+
openEntries[openEntries.length - 1]?.focus('open');
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
/**
|
|
1312
|
+
* The element to keep focused across a re-show: the document's active
|
|
1313
|
+
* element when it is inside an open sheet that is about to be re-shown.
|
|
1314
|
+
*/
|
|
1315
|
+
function activeElementToKeep(doc, reshown) {
|
|
1316
|
+
const remembered = state.focusToKeep.get(doc);
|
|
1317
|
+
state.focusToKeep.delete(doc);
|
|
1318
|
+
const current = doc.activeElement;
|
|
1319
|
+
const active = remembered != null && remembered.isConnected ? remembered : current instanceof HTMLElement && current !== doc.body ? current : null;
|
|
1320
|
+
if (active == null) {
|
|
1321
|
+
return null;
|
|
1322
|
+
}
|
|
1323
|
+
for (const entry of reshown) {
|
|
1324
|
+
const element = entry.getElement();
|
|
1325
|
+
if (entry.isOpen && element != null && element.contains(active)) {
|
|
1326
|
+
return active;
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
return null;
|
|
1330
|
+
}
|
|
1331
|
+
function update(doc, isModal) {
|
|
1332
|
+
detectReplace();
|
|
1333
|
+
// A drop can land as soon as the batch has both of its flips: the pairing
|
|
1334
|
+
// is decided then, and anything left `replace`d is a retained sheet whose
|
|
1335
|
+
// replacer left the flow.
|
|
1336
|
+
if (state.batchOpened.length > 0 && state.batchClosed.length > 0) {
|
|
1337
|
+
flushPendingDrops();
|
|
1338
|
+
}
|
|
1339
|
+
const order = sortedEntries(doc, isModal);
|
|
1340
|
+
computeViews(order, isModal);
|
|
1341
|
+
reconcileTopLayer(doc, isModal, order);
|
|
1342
|
+
state.focusToKeep.delete(doc);
|
|
1343
|
+
focusPendingReplacer();
|
|
1344
|
+
promoteDeferredNonModal(doc);
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
/**
|
|
1348
|
+
* Join the stack for as long as the sheet is presented. Call from a layout
|
|
1349
|
+
* effect; the returned function leaves the stack (also from its cleanup).
|
|
1350
|
+
* `isOpen` is the controlled open state; a presented sheet whose `isOpen` is
|
|
1351
|
+
* false is sliding out and keeps its place without counting as a layer.
|
|
1352
|
+
*/
|
|
1353
|
+
export function registerBottomSheet(registration, isOpen, isModal = true) {
|
|
1354
|
+
const {
|
|
1355
|
+
key
|
|
1356
|
+
} = registration;
|
|
1357
|
+
const remembered = state.rankByKey.get(key);
|
|
1358
|
+
let rank;
|
|
1359
|
+
if (remembered != null && !remembered.isClosed && (
|
|
1360
|
+
// A StrictMode replay unregisters and registers again in one synchronous
|
|
1361
|
+
// pass, before any seal; the rank is kept so the replay cannot reorder.
|
|
1362
|
+
// A sheet React hid (Suspense, Activity) and later revealed comes back in
|
|
1363
|
+
// a later batch and re-presents on top, like a fresh open.
|
|
1364
|
+
remembered.detachedBatch == null || remembered.detachedBatch === state.currentBatch)) {
|
|
1365
|
+
rank = {
|
|
1366
|
+
batch: remembered.batch,
|
|
1367
|
+
seq: remembered.seq
|
|
1368
|
+
};
|
|
1369
|
+
remembered.detachedBatch = null;
|
|
1370
|
+
} else {
|
|
1371
|
+
rank = takeRank();
|
|
1372
|
+
state.rankByKey.set(key, {
|
|
1373
|
+
...rank,
|
|
1374
|
+
isClosed: !isOpen,
|
|
1375
|
+
detachedBatch: null
|
|
1376
|
+
});
|
|
1377
|
+
}
|
|
1378
|
+
const doc = registration.getElement()?.ownerDocument ?? document;
|
|
1379
|
+
listenForPresses(doc);
|
|
1380
|
+
const entry = {
|
|
1381
|
+
...registration,
|
|
1382
|
+
rank,
|
|
1383
|
+
isOpen,
|
|
1384
|
+
doc,
|
|
1385
|
+
isModal,
|
|
1386
|
+
view: BOTTOM_SHEET_STACK_DEFAULT_VIEW,
|
|
1387
|
+
replace: null,
|
|
1388
|
+
replacing: null,
|
|
1389
|
+
isEnteringInPlace: false
|
|
1390
|
+
};
|
|
1391
|
+
state.entries.push(entry);
|
|
1392
|
+
if (isOpen && rank.batch === state.currentBatch && state.isBatchDirty) {
|
|
1393
|
+
recordFlip(entry, 'opened');
|
|
1394
|
+
}
|
|
1395
|
+
update(entry.doc, entry.isModal);
|
|
1396
|
+
return () => {
|
|
1397
|
+
const index = state.entries.indexOf(entry);
|
|
1398
|
+
if (index === -1) {
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
state.entries.splice(index, 1);
|
|
1402
|
+
stopListeningForPresses(entry.doc);
|
|
1403
|
+
setShownOrder(entry.doc, entry.isModal, getShownOrder(entry.doc, entry.isModal).filter(shown => shown !== entry));
|
|
1404
|
+
state.batchOpened = state.batchOpened.filter(other => other !== entry);
|
|
1405
|
+
state.batchClosed = state.batchClosed.filter(other => other !== entry);
|
|
1406
|
+
state.pendingDrops = state.pendingDrops.filter(other => other !== entry);
|
|
1407
|
+
if (state.pendingReplaceFocus === entry) {
|
|
1408
|
+
state.pendingReplaceFocus = null;
|
|
1409
|
+
}
|
|
1410
|
+
if (state.batchPair?.replaced === entry || state.batchPair?.replacer === entry) {
|
|
1411
|
+
state.batchPair = null;
|
|
1412
|
+
}
|
|
1413
|
+
// A member of a "Close all" group leaving the registry has left for
|
|
1414
|
+
// good: a host that unmounts mid-exit never sends its motion report, so
|
|
1415
|
+
// leaving is its completion. Reported after `hide()` below, once its
|
|
1416
|
+
// dialog is out of the top layer, so the return focus has nothing modal
|
|
1417
|
+
// in its way; a host that still reports afterwards finds nothing to do.
|
|
1418
|
+
const closingGroup = state.closingTogether.get(entry);
|
|
1419
|
+
if (entry.replace != null) {
|
|
1420
|
+
entry.replace.replacer.replacing = null;
|
|
1421
|
+
entry.replace = null;
|
|
1422
|
+
}
|
|
1423
|
+
if (entry.replacing != null) {
|
|
1424
|
+
// The sheet it was replacing is left as an ordinary closing sheet.
|
|
1425
|
+
entry.replacing.replace = null;
|
|
1426
|
+
entry.replacing = null;
|
|
1427
|
+
}
|
|
1428
|
+
// A sheet that leaves while its dialog is still open was hidden by React
|
|
1429
|
+
// (Suspense, Activity) or is unmounting: a modal dialog with no box would
|
|
1430
|
+
// keep blocking the page, so it leaves the top layer here.
|
|
1431
|
+
entry.hide();
|
|
1432
|
+
if (closingGroup != null) {
|
|
1433
|
+
leaveClosingGroup(entry, closingGroup);
|
|
1434
|
+
}
|
|
1435
|
+
const current = state.rankByKey.get(key);
|
|
1436
|
+
if (current != null) {
|
|
1437
|
+
current.detachedBatch = state.currentBatch;
|
|
1438
|
+
state.isBatchDirty = true;
|
|
1439
|
+
scheduleSeal();
|
|
1440
|
+
}
|
|
1441
|
+
entry.view = BOTTOM_SHEET_STACK_DEFAULT_VIEW;
|
|
1442
|
+
entry.onView(entry.view);
|
|
1443
|
+
update(entry.doc, entry.isModal);
|
|
1444
|
+
};
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
/**
|
|
1448
|
+
* The dialog that realizes a level changed: a switcher flow handed off to a
|
|
1449
|
+
* step with its own dialog, which its host presented on top of the top layer
|
|
1450
|
+
* in this same commit. The level keeps its rank; the registry records the new
|
|
1451
|
+
* dialog as the most recently shown and puts the sheets that belong above it
|
|
1452
|
+
* back on top before paint. A level on its own, or already on top, is left
|
|
1453
|
+
* alone.
|
|
1454
|
+
*/
|
|
1455
|
+
export function notifyBottomSheetHostChanged(key) {
|
|
1456
|
+
const entry = state.entries.find(candidate => candidate.key === key);
|
|
1457
|
+
if (entry == null || !entry.isModal) {
|
|
1458
|
+
return;
|
|
1459
|
+
}
|
|
1460
|
+
const element = entry.getElement();
|
|
1461
|
+
if (element == null || !isSheetHostPresented(element)) {
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
const shown = getShownOrder(entry.doc, true).filter(shownEntry => shownEntry !== entry);
|
|
1465
|
+
shown.push(entry);
|
|
1466
|
+
setShownOrder(entry.doc, true, shown);
|
|
1467
|
+
update(entry.doc, true);
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
/**
|
|
1471
|
+
* Report the controlled open state of a registered sheet. Closing keeps the
|
|
1472
|
+
* sheet's place while it slides out; reopening takes a new rank, so the sheet
|
|
1473
|
+
* returns on top.
|
|
1474
|
+
*/
|
|
1475
|
+
export function setBottomSheetOpen(key, isOpen) {
|
|
1476
|
+
const entry = state.entries.find(candidate => candidate.key === key);
|
|
1477
|
+
if (entry == null || entry.isOpen === isOpen) {
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1480
|
+
entry.isOpen = isOpen;
|
|
1481
|
+
const remembered = state.rankByKey.get(key);
|
|
1482
|
+
if (isOpen) {
|
|
1483
|
+
if (state.closingTogether.has(entry)) {
|
|
1484
|
+
const group = state.closingTogether.get(entry);
|
|
1485
|
+
state.closingTogether.delete(entry);
|
|
1486
|
+
group?.pending.delete(entry);
|
|
1487
|
+
}
|
|
1488
|
+
entry.rank = takeRank();
|
|
1489
|
+
state.rankByKey.set(key, {
|
|
1490
|
+
...entry.rank,
|
|
1491
|
+
isClosed: false,
|
|
1492
|
+
detachedBatch: null
|
|
1493
|
+
});
|
|
1494
|
+
recordFlip(entry, 'opened');
|
|
1495
|
+
// Reopened while being replaced: it is a sheet again, not a fading one.
|
|
1496
|
+
if (entry.replace != null) {
|
|
1497
|
+
entry.replace.replacer.replacing = null;
|
|
1498
|
+
entry.replace = null;
|
|
1499
|
+
entry.isEnteringInPlace = true;
|
|
1500
|
+
}
|
|
1501
|
+
} else {
|
|
1502
|
+
if (remembered != null) {
|
|
1503
|
+
remembered.isClosed = true;
|
|
1504
|
+
}
|
|
1505
|
+
state.isBatchDirty = true;
|
|
1506
|
+
scheduleSeal();
|
|
1507
|
+
recordFlip(entry, 'closed');
|
|
1508
|
+
entry.isEnteringInPlace = false;
|
|
1509
|
+
// A replacing sheet that closes drops the sheet it was replacing, the way
|
|
1510
|
+
// a switcher hides a retained step when the flow is left — unless that
|
|
1511
|
+
// sheet is the one coming back in this same update (Continue pressed
|
|
1512
|
+
// during a Back handoff), which `detectReplace` pairs as the reverse
|
|
1513
|
+
// replace. The other flips of this commit may not have arrived yet, so
|
|
1514
|
+
// the drop waits for the seal; a paired sheet is no longer `replace`d by
|
|
1515
|
+
// then and is left alone.
|
|
1516
|
+
if (entry.replacing != null) {
|
|
1517
|
+
state.pendingDrops.push(entry.replacing);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
update(entry.doc, entry.isModal);
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
/**
|
|
1524
|
+
* Drop the retained sheets whose replacer closed in this batch and that no
|
|
1525
|
+
* replace of this batch took back. Runs when the batch is sealed and, before
|
|
1526
|
+
* that, whenever a pairing has been decided, so the drop lands in the same
|
|
1527
|
+
* commit as the close that caused it.
|
|
1528
|
+
*/
|
|
1529
|
+
function flushPendingDrops() {
|
|
1530
|
+
if (state.pendingDrops.length === 0) {
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1533
|
+
const drops = state.pendingDrops;
|
|
1534
|
+
state.pendingDrops = [];
|
|
1535
|
+
for (const replaced of drops) {
|
|
1536
|
+
const replacer = replaced.replace?.replacer;
|
|
1537
|
+
if (replacer != null && !replacer.isOpen) {
|
|
1538
|
+
finishReplace(replaced);
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
/**
|
|
1544
|
+
* The open sheet directly beneath `key`: its dialog, and a way to move focus
|
|
1545
|
+
* into it. Null when nothing open is beneath. A closing top sheet asks this
|
|
1546
|
+
* to return focus into the sheet it reveals.
|
|
1547
|
+
*/
|
|
1548
|
+
export function getBottomSheetBeneath(key) {
|
|
1549
|
+
const own = state.entries.find(entry => entry.key === key);
|
|
1550
|
+
if (own == null) {
|
|
1551
|
+
return null;
|
|
1552
|
+
}
|
|
1553
|
+
const order = groupOf(own);
|
|
1554
|
+
for (let below = order.indexOf(own) - 1; below >= 0; below -= 1) {
|
|
1555
|
+
const entry = order[below];
|
|
1556
|
+
const element = entry.getElement();
|
|
1557
|
+
if (entry.isOpen && element != null) {
|
|
1558
|
+
return {
|
|
1559
|
+
element,
|
|
1560
|
+
focus: () => entry.focus('reveal')
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
return null;
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
// preventScroll on both: presenting a sheet must not scroll the page to reveal
|
|
1568
|
+
// what it just focused. For an autofocused field that reveal is the mobile
|
|
1569
|
+
// keyboard's, and it moves the document under a fixed sheet; useMobileKeyboard
|
|
1570
|
+
// brings the field into view within the sheet instead.
|
|
1571
|
+
/**
|
|
1572
|
+
* Move focus into a sheet panel. Focus already inside the sheet stays put:
|
|
1573
|
+
* on `open` the native host has normally run its own focusing steps, which
|
|
1574
|
+
* land on the panel (`tabindex="-1"`), so a `data-autofocus` descendant is
|
|
1575
|
+
* reached only where the host moved nothing (a stubbed host, or a browser
|
|
1576
|
+
* whose focusing steps skipped the sheet) — the released behavior. On
|
|
1577
|
+
* `replace` the entering step takes focus whatever its modality and whatever
|
|
1578
|
+
* the host did, a `data-autofocus` descendant first; on `reveal` the panel
|
|
1579
|
+
* itself takes focus, with a ring only after keyboard input.
|
|
1580
|
+
*/
|
|
1581
|
+
export function focusSheetPanel(panel, reason, isModal) {
|
|
1582
|
+
if (panel == null) {
|
|
1583
|
+
return;
|
|
1584
|
+
}
|
|
1585
|
+
// A covered sheet takes no focus: its dialog is inert (the stack marks it
|
|
1586
|
+
// so, and a modal above it blocks it in the browser anyway), so a step that
|
|
1587
|
+
// enters beneath a sheet on top — a covered switcher flow changing step from
|
|
1588
|
+
// code — leaves the person where they are, in the sheet on top.
|
|
1589
|
+
const host = panel.closest('dialog');
|
|
1590
|
+
if (host != null && (host.hasAttribute('inert') || host.getAttribute('aria-hidden') === 'true')) {
|
|
1591
|
+
return;
|
|
1592
|
+
}
|
|
1593
|
+
// Focus already inside this sheet's own content stays put. Focus inside a
|
|
1594
|
+
// sheet rendered within this content (a closing nested sheet) does not
|
|
1595
|
+
// count: that sheet's dialog is leaving, and this one is being revealed.
|
|
1596
|
+
// On a replace entry the native focusing steps have just run and may have
|
|
1597
|
+
// put focus on the panel itself; that is not a placement, so only focus on
|
|
1598
|
+
// a control inside the panel (the person's, or the field this placement
|
|
1599
|
+
// chose) stays put.
|
|
1600
|
+
const activeElement = panel.ownerDocument.activeElement;
|
|
1601
|
+
const isInsidePanel = activeElement != null && panel.contains(activeElement) && activeElement.closest('dialog') === panel.closest('dialog');
|
|
1602
|
+
if (isInsidePanel && (reason !== 'replace' || activeElement !== panel)) {
|
|
1603
|
+
return;
|
|
1604
|
+
}
|
|
1605
|
+
if (reason === 'reveal') {
|
|
1606
|
+
focusForLastInput(panel);
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1609
|
+
const autofocus = panel.querySelector('[data-autofocus]');
|
|
1610
|
+
if (autofocus != null) {
|
|
1611
|
+
autofocus.focus({
|
|
1612
|
+
preventScroll: true
|
|
1613
|
+
});
|
|
1614
|
+
} else if (!isInsidePanel && (isModal || reason === 'replace')) {
|
|
1615
|
+
// A lone non-modal sheet takes no focus on open; the step replacing the
|
|
1616
|
+
// one the person was in does, so the keyboard stays in the flow. Focus
|
|
1617
|
+
// the native steps already put on the panel needs no second focus().
|
|
1618
|
+
panel.focus({
|
|
1619
|
+
preventScroll: true
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
/** Test-only: the presented sheets' dialogs, bottom to top, with their views. */
|
|
1625
|
+
export function getBottomSheetStackForTests(doc = document, isModal = true) {
|
|
1626
|
+
return sortedEntries(doc, isModal).map(entry => ({
|
|
1627
|
+
element: entry.getElement(),
|
|
1628
|
+
isOpen: entry.isOpen,
|
|
1629
|
+
view: entry.view
|
|
1630
|
+
}));
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
/** Test-only: forget every sheet and start the batch counter over. */
|
|
1634
|
+
export function resetBottomSheetStackForTests() {
|
|
1635
|
+
showFocusesContainer = null;
|
|
1636
|
+
// The press listeners go with the entries they served.
|
|
1637
|
+
for (const entry of state.entries) {
|
|
1638
|
+
const remove = state.listenedDocuments.get(entry.doc);
|
|
1639
|
+
if (remove != null) {
|
|
1640
|
+
state.listenedDocuments.delete(entry.doc);
|
|
1641
|
+
remove();
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
state.entries.length = 0;
|
|
1645
|
+
state.shownOrder = new WeakMap();
|
|
1646
|
+
state.batchOpened = [];
|
|
1647
|
+
state.batchClosed = [];
|
|
1648
|
+
state.batchActor = null;
|
|
1649
|
+
state.pendingDrops = [];
|
|
1650
|
+
state.pendingReplaceFocus = null;
|
|
1651
|
+
state.closingTogether = new Map();
|
|
1652
|
+
state.currentBatch = 0;
|
|
1653
|
+
state.nextSeq = 0;
|
|
1654
|
+
state.isBatchDirty = false;
|
|
1655
|
+
state.isSealScheduled = false;
|
|
1656
|
+
state.focusToKeep = new WeakMap();
|
|
1657
|
+
// Deferral watchers on the test document go with the deferrals they
|
|
1658
|
+
// served (a WeakMap cannot be walked; the test document is the one place
|
|
1659
|
+
// a test can have deferred into).
|
|
1660
|
+
const deferral = state.deferredTopLayer.get(document);
|
|
1661
|
+
if (deferral != null) {
|
|
1662
|
+
releaseDeferralWatchers(deferral);
|
|
1663
|
+
}
|
|
1664
|
+
state.deferredTopLayer = new WeakMap();
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
/** Test-only: the shared state's key, shape and protocol version, for the two-copies tests. */
|
|
1668
|
+
export const BOTTOM_SHEET_STACK_STATE_FOR_TESTS = {
|
|
1669
|
+
key: STACK_STATE_KEY,
|
|
1670
|
+
shape: STACK_STATE_SHAPE,
|
|
1671
|
+
protocolVersion: STACK_PROTOCOL_VERSION,
|
|
1672
|
+
keyFor: version => Symbol.for(`astryx.bottomSheetStack.v${version}`),
|
|
1673
|
+
/** Whether the document has the press listeners installed. */
|
|
1674
|
+
isListeningForPresses: doc => state.listenedDocuments.has(doc)
|
|
1675
|
+
};
|