@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,2203 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file animator.ts
|
|
5
|
+
* @input An element, target values, and a spring, momentum curves (coast.ts)
|
|
6
|
+
* or any closed-form curve
|
|
7
|
+
* @output Exports `createAnimator`: a compositor animator for translate,
|
|
8
|
+
* rotate, scale and opacity, with presentation values and velocities read
|
|
9
|
+
* from the analytic curves, velocity-preserving retargeting, freeze-in-place
|
|
10
|
+
* `stop`, completion promises, a shared clock, and UIKit's Reduced Motion
|
|
11
|
+
* substitution; plus its option, handle and transition types
|
|
12
|
+
* @position Motion foundation (DOM). Internal to core: components use it for
|
|
13
|
+
* every touch-presentation animation; not exported from the package entry
|
|
14
|
+
*
|
|
15
|
+
* HOW IT RUNS
|
|
16
|
+
*
|
|
17
|
+
* Each animated value (x, y, rotate, scaleX, scaleY, opacity) follows a
|
|
18
|
+
* closed-form curve: a spring (spring.ts), momentum curves its caller built (a
|
|
19
|
+
* UIScrollView coast with an edge bounce, coast.ts), a curve handed to `play` (a spring chasing a moving
|
|
20
|
+
* target, a fitted UIKit curve), or a Reduced Motion cross-fade. The curves are baked into
|
|
21
|
+
* Web Animations keyframes on the individual transform properties (`translate`,
|
|
22
|
+
* `rotate`, `scale`) and `opacity`, sampled adaptively until linear
|
|
23
|
+
* interpolation between keyframes is within a fraction of a pixel, and played
|
|
24
|
+
* with `easing: linear`. The browser then runs them on its compositor: they
|
|
25
|
+
* keep advancing every frame while JavaScript is busy (`spec:AST-094` FR5).
|
|
26
|
+
*
|
|
27
|
+
* Why keyframes and not one `linear()` easing: measured in Chromium, both
|
|
28
|
+
* forms are composited and equally precise for the same samples, but WebKit's
|
|
29
|
+
* Core Animation path refuses to accelerate an effect whose easing is
|
|
30
|
+
* `linear()` with points (KeyframeEffect::canBeAccelerated) unless its newer
|
|
31
|
+
* threaded animations are on, so on older iOS Safari a `linear()` spring would
|
|
32
|
+
* run on the main thread. Linear segments between keyframes map directly onto
|
|
33
|
+
* a Core Animation keyframe animation. Why the individual properties and not
|
|
34
|
+
* `transform`: each can be animated, retargeted or driven by a finger on its
|
|
35
|
+
* own (a drag writes `translate` while `scale` keeps springing), with no
|
|
36
|
+
* additive composition, which neither engine composites.
|
|
37
|
+
*
|
|
38
|
+
* The page never reads the DOM to know where something is: `get` and
|
|
39
|
+
* `getVelocity` evaluate the curves at a time on the document timeline's
|
|
40
|
+
* clock (`performance.now()` by default), so a finger can grab anything
|
|
41
|
+
* mid-flight from the value on screen, and a new target starts from the
|
|
42
|
+
* current value with the current velocity (`spec:AST-094` FR6). A gesture's release velocity
|
|
43
|
+
* can be handed in explicitly instead.
|
|
44
|
+
*
|
|
45
|
+
* End state without a flicker frame: the element's inline style always holds
|
|
46
|
+
* the values the animations END on, and the keyframes hold that end state for
|
|
47
|
+
* a while after the curve ends (END_HOLD_MS). The main thread drops a finished
|
|
48
|
+
* animation as soon as it runs after the curve's end; the inline style already
|
|
49
|
+
* shows the same values, so nothing moves. The hold matters when the curve
|
|
50
|
+
* ends during a main-thread block: WebKit's compositor ends an animation by
|
|
51
|
+
* falling back to the value its main thread last committed, which is
|
|
52
|
+
* mid-flight.
|
|
53
|
+
*
|
|
54
|
+
* Start time: a call that starts from rest (nothing on its channels moving,
|
|
55
|
+
* no `options.at`) gets no start time, so the browser starts it when it
|
|
56
|
+
* commits it, as Core Animation starts an animation at its transaction's
|
|
57
|
+
* commit: a main-thread block before that frame delays the motion instead of
|
|
58
|
+
* skipping into it. Everything the call starts, and every
|
|
59
|
+
* call from rest in the same task, starts at that one time, which
|
|
60
|
+
* `handle.started` reports. A call that continues motion on
|
|
61
|
+
* screen (a retarget, a catch) keeps an explicit start at the call time, or
|
|
62
|
+
* `options.at` to line several animators up, so the new curve continues
|
|
63
|
+
* from the frame on screen. Keyframes are sampled from the call's start, so
|
|
64
|
+
* a value that keeps moving through a rebuild stays on its curve to the
|
|
65
|
+
* frame.
|
|
66
|
+
*
|
|
67
|
+
* WebKit starts an animation created just before a block at its last frame
|
|
68
|
+
* time before the block, so there the motion still skips into its curve;
|
|
69
|
+
* Chromium starts it after the block.
|
|
70
|
+
*
|
|
71
|
+
* Promotion: Mobile Safari runs an animation whose start time is set from
|
|
72
|
+
* script on its compositor at its first commit only if the element already
|
|
73
|
+
* has a compositing layer; without one, a block right after that commit
|
|
74
|
+
* freezes it (iOS 26.2 simulator: 2 of 7 runs kept moving). Every animation
|
|
75
|
+
* the animator creates, and every `set`, therefore promotes its channels in
|
|
76
|
+
* the same task: inline `will-change` on the element, merged with the
|
|
77
|
+
* element's own. That is enough (5 of 5, and 4 of 4 through the animator);
|
|
78
|
+
* promotion ends PROMOTION_LINGER_MS after the last motion.
|
|
79
|
+
*
|
|
80
|
+
* Known limits, iOS Safari (measured on the iOS 26.2 simulator):
|
|
81
|
+
* - a block in the task that starts a motion delays it: it starts when the
|
|
82
|
+
* browser commits it, after the block;
|
|
83
|
+
* - an animation created in the task that first displays a dialog stays on
|
|
84
|
+
* the main thread: start it in the next animation frame;
|
|
85
|
+
* - after a long block, an animation frame's timestamp is when the frame was
|
|
86
|
+
* due, so passing it as `at` starts the curve in the past: keep the default
|
|
87
|
+
* clock;
|
|
88
|
+
* - WebKit does not promote the siblings a moving layer will cross, so a
|
|
89
|
+
* moving layer below content can paint over it: give it `z-index: -1`
|
|
90
|
+
* inside a parent with `isolation: isolate`;
|
|
91
|
+
* - a tap-driven start can show its first moving frame two to four frames
|
|
92
|
+
* into the curve (simulator, not yet confirmed on a device).
|
|
93
|
+
*
|
|
94
|
+
* Reduced Motion (design:reduced-motion): with `prefers-reduced-motion:
|
|
95
|
+
* reduce`, every `animate` and `play` (travel or opacity, on a spring or any
|
|
96
|
+
* curve) is replaced, and nothing moves, scales or springs. The planner
|
|
97
|
+
* (reducedMotionPlan.ts) decides what each call becomes; the animator
|
|
98
|
+
* applies the plan and runs its landings:
|
|
99
|
+
* - on a coarse primary pointer, or for a call started by a touch drag's
|
|
100
|
+
* release on any pointer (`touchRelease`), it becomes (DR2) an
|
|
101
|
+
* opacity-only cross-fade of `timing.reducedMotionCrossFadeMs` (200 ms)
|
|
102
|
+
* on `timing.curves.move`:
|
|
103
|
+
* - entering (opacity rises from 0 in the same call, or `crossfadeAs:
|
|
104
|
+
* 'entrance'`): lands in its new place at once and fades in;
|
|
105
|
+
* - leaving (opacity falls to 0, or `crossfadeAs: 'exit'`): fades out in
|
|
106
|
+
* place, then lands;
|
|
107
|
+
* - an opacity change alone: fades to its value;
|
|
108
|
+
* - otherwise: dips (fades out, lands, fades back in);
|
|
109
|
+
* travel lands with a plain style write while the element is transparent,
|
|
110
|
+
* never as an animated transform; a busy main thread only keeps it
|
|
111
|
+
* transparent longer, and a dip fades back in from the landing; a later
|
|
112
|
+
* call that lands too carries a landing still waiting (two calls in one
|
|
113
|
+
* frame land together), and a later opacity change lands it at once;
|
|
114
|
+
* - on any other primary pointer (DR3) it completes at once.
|
|
115
|
+
* No call option and no theme value lengthens the fade or keeps travel (DR4):
|
|
116
|
+
* the fade's length is not a theme role.
|
|
117
|
+
*
|
|
118
|
+
* Momentum under Reduce Motion (`design:reduced-motion` DR2, DEC-2): a
|
|
119
|
+
* decay's momentum continues the velocity a finger handed it on release
|
|
120
|
+
* (UIScrollView's coast after a flick, with its bounce at an edge), so it
|
|
121
|
+
* keeps its motion, as native momentum does: it is direct movement, not a
|
|
122
|
+
* transition. A curve marked `isTransition` (coast()'s spring back from past
|
|
123
|
+
* an edge), like a spring a component starts on release toward a target it
|
|
124
|
+
* picks (a detent, a page), is a transition: it plays through the Reduce
|
|
125
|
+
* Motion policy above, a cross-fade on a coarse pointer or after a touch
|
|
126
|
+
* release (`touchRelease`) and at once elsewhere (DR2, DR3).
|
|
127
|
+
*
|
|
128
|
+
* SSR-safe: nothing touches `window` or `document` at module scope, and an
|
|
129
|
+
* animator created without an element only keeps state until one is attached.
|
|
130
|
+
*
|
|
131
|
+
* SYNC: When modified, update:
|
|
132
|
+
* - /packages/core/src/motion/animator.test.ts
|
|
133
|
+
* - /packages/core/src/motion/reducedMotionPlan.ts (the Reduce Motion plan)
|
|
134
|
+
* - /packages/core/src/motion/README.md
|
|
135
|
+
*/
|
|
136
|
+
|
|
137
|
+
import { cubicBezier } from "./bezier.js";
|
|
138
|
+
import { solveSpring, toSpring } from "./spring.js";
|
|
139
|
+
import { reducedMotionCrossFade } from "./uikit/timing.js";
|
|
140
|
+
import { planReducedMotion } from "./reducedMotionPlan.js";
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* A value the animator can drive.
|
|
144
|
+
*/
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Values by property; `scale` sets `scaleX` and `scaleY` together.
|
|
148
|
+
*/
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* A spring and the velocity it starts with.
|
|
152
|
+
*/
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Options of `animate` and `play`: the spring, a release velocity, the Reduced
|
|
156
|
+
* Motion handling.
|
|
157
|
+
*/
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Any closed-form curve to play on the compositor (`Animator.play`, and a
|
|
161
|
+
* release's coast through `Animator.decay`): a spring chasing a moving
|
|
162
|
+
* target, a piecewise curve, a fitted UIKit curve. Times are seconds from
|
|
163
|
+
* the curve's start; the value at `duration` is the end state. Start it at
|
|
164
|
+
* the property's presentation value (`get`) to continue without a jump.
|
|
165
|
+
*/
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Options of `createAnimator`.
|
|
169
|
+
*/
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* One `animate` / `decay` call's animations.
|
|
173
|
+
*/
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* A compositor animator bound to one element (see the file header).
|
|
177
|
+
*/
|
|
178
|
+
|
|
179
|
+
// ---------------------------------------------------------------------------
|
|
180
|
+
// Curves
|
|
181
|
+
// ---------------------------------------------------------------------------
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* A call started from rest. Its animations are created without a start time,
|
|
185
|
+
* so the browser starts them when it commits them, as Core Animation starts
|
|
186
|
+
* an animation at its transaction's commit: a main-thread block before that
|
|
187
|
+
* frame delays the motion instead of skipping into it. Until then the curves
|
|
188
|
+
* read from the call time; once the browser's start is known they move to it.
|
|
189
|
+
*/
|
|
190
|
+
|
|
191
|
+
/** An atRest() call waiting for its curves to end. */
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Reduced Motion travel that lands with a plain style write while the
|
|
195
|
+
* element is fully transparent (never an animated transform), then, in a
|
|
196
|
+
* dip, the fade back in.
|
|
197
|
+
*/
|
|
198
|
+
|
|
199
|
+
/** What Reduced Motion makes of a call: its segments, and any landing. */
|
|
200
|
+
|
|
201
|
+
const PROPERTIES = ['x', 'y', 'rotate', 'scaleX', 'scaleY', 'opacity'];
|
|
202
|
+
|
|
203
|
+
/** The axes momentum moves: `decay` takes only these (DR2, DEC-2). */
|
|
204
|
+
const DECAY_PROPERTIES = ['x', 'y'];
|
|
205
|
+
const IDENTITY = {
|
|
206
|
+
x: 0,
|
|
207
|
+
y: 0,
|
|
208
|
+
rotate: 0,
|
|
209
|
+
scaleX: 1,
|
|
210
|
+
scaleY: 1,
|
|
211
|
+
opacity: 1
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Within this of its target a curve is over (in the property's own unit). A
|
|
216
|
+
* curve built outside the animator (a progress curve) settles where a call
|
|
217
|
+
* would with this as its epsilon.
|
|
218
|
+
*/
|
|
219
|
+
export const SETTLE_THRESHOLD = {
|
|
220
|
+
x: 0.05,
|
|
221
|
+
y: 0.05,
|
|
222
|
+
rotate: 0.01,
|
|
223
|
+
scaleX: 0.0001,
|
|
224
|
+
scaleY: 0.0001,
|
|
225
|
+
opacity: 0.001
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
/** Keyframes are added until linear interpolation is within this. */
|
|
229
|
+
const SAMPLE_TOLERANCE = {
|
|
230
|
+
x: 0.05,
|
|
231
|
+
y: 0.05,
|
|
232
|
+
rotate: 0.02,
|
|
233
|
+
scaleX: 0.0002,
|
|
234
|
+
scaleY: 0.0002,
|
|
235
|
+
opacity: 0.002
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
/** A curve that has not settled by then (an undamped spring) snaps. */
|
|
239
|
+
const MAX_DURATION_MS = 30_000;
|
|
240
|
+
/** Above this many keyframes the tolerance is relaxed. */
|
|
241
|
+
const MAX_KEYFRAMES = 1200;
|
|
242
|
+
/**
|
|
243
|
+
* The keyframes hold the end state this long after a curve ends. An engine
|
|
244
|
+
* whose compositor ends an animation while the main thread is blocked falls
|
|
245
|
+
* back to the value its main thread last committed, mid-flight (WebKit); the
|
|
246
|
+
* hold keeps the end state on screen through such a block. The main thread
|
|
247
|
+
* drops the animation as soon as it runs after the curve's end.
|
|
248
|
+
*/
|
|
249
|
+
export const END_HOLD_MS = 5000;
|
|
250
|
+
|
|
251
|
+
/** How often a paused animation is looked at again for retiring, ms. */
|
|
252
|
+
const RETIRE_RECHECK_MS = 100;
|
|
253
|
+
|
|
254
|
+
/** How long an element stays promoted after its last motion, ms. */
|
|
255
|
+
export const PROMOTION_LINGER_MS = 500;
|
|
256
|
+
const CHANNELS = {
|
|
257
|
+
translate: ['x', 'y'],
|
|
258
|
+
rotate: ['rotate'],
|
|
259
|
+
scale: ['scaleX', 'scaleY'],
|
|
260
|
+
opacity: ['opacity']
|
|
261
|
+
};
|
|
262
|
+
const CHANNEL_OF = {
|
|
263
|
+
x: 'translate',
|
|
264
|
+
y: 'translate',
|
|
265
|
+
rotate: 'rotate',
|
|
266
|
+
scaleX: 'scale',
|
|
267
|
+
scaleY: 'scale',
|
|
268
|
+
opacity: 'opacity'
|
|
269
|
+
};
|
|
270
|
+
function round(value, digits) {
|
|
271
|
+
return Number(value.toFixed(digits));
|
|
272
|
+
}
|
|
273
|
+
function format(channel, value) {
|
|
274
|
+
switch (channel) {
|
|
275
|
+
case 'translate':
|
|
276
|
+
return `${round(value('x'), 3)}px ${round(value('y'), 3)}px`;
|
|
277
|
+
case 'rotate':
|
|
278
|
+
return `${round(value('rotate'), 4)}deg`;
|
|
279
|
+
case 'scale':
|
|
280
|
+
return `${round(value('scaleX'), 5)} ${round(value('scaleY'), 5)}`;
|
|
281
|
+
case 'opacity':
|
|
282
|
+
return `${round(Math.min(1, Math.max(0, value('opacity'))), 4)}`;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
const shared = new WeakMap();
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* The one animator for `element`, created on
|
|
289
|
+
* first use. Code that animates one element from several places (a swipe on
|
|
290
|
+
* x, a removal on y) shares it, so their curves compose in one `translate`
|
|
291
|
+
* track and retargeting one axis leaves the other on its curve; two
|
|
292
|
+
* animators on one element would overwrite each other. `options` apply
|
|
293
|
+
* when it is created; later calls get the same animator. Never `attach` it
|
|
294
|
+
* elsewhere or `dispose` it: it lives as long as the element.
|
|
295
|
+
*/
|
|
296
|
+
export function animatorFor(element, options = {}) {
|
|
297
|
+
let animator = shared.get(element);
|
|
298
|
+
if (animator == null) {
|
|
299
|
+
animator = createAnimator(element, options);
|
|
300
|
+
shared.set(element, animator);
|
|
301
|
+
}
|
|
302
|
+
return animator;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* The inline text for a channel: `format`, except that an identity transform
|
|
307
|
+
* is written `none`. Any other value, `translate: 0px 0px` included, makes
|
|
308
|
+
* the element the containing block of its `position: fixed` descendants and
|
|
309
|
+
* a stacking context, so a motion back to identity would leave a fixed child
|
|
310
|
+
* moved with its parent; Mobile Safari also lets no finger scroll a list
|
|
311
|
+
* inside an element left at `scale: 1`. At rest the identity leaves the
|
|
312
|
+
* element entirely when its stylesheet agrees (`releaseIdentity`).
|
|
313
|
+
*/
|
|
314
|
+
function inlineText(channel, value) {
|
|
315
|
+
const at = (property, identity, digits) => round(value(property), digits) === identity;
|
|
316
|
+
switch (channel) {
|
|
317
|
+
case 'translate':
|
|
318
|
+
return at('x', 0, 3) && at('y', 0, 3) ? 'none' : format(channel, value);
|
|
319
|
+
case 'rotate':
|
|
320
|
+
return at('rotate', 0, 4) ? 'none' : format(channel, value);
|
|
321
|
+
case 'scale':
|
|
322
|
+
return at('scaleX', 1, 5) && at('scaleY', 1, 5) ? 'none' : format(channel, value);
|
|
323
|
+
case 'opacity':
|
|
324
|
+
return format(channel, value);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/** What `inlineText` writes for each channel at identity. */
|
|
329
|
+
const IDENTITY_TEXT = {
|
|
330
|
+
translate: 'none',
|
|
331
|
+
rotate: 'none',
|
|
332
|
+
scale: 'none',
|
|
333
|
+
opacity: '1'
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
/** Whether a computed value (or an unset one) is the channel's identity. */
|
|
337
|
+
function isIdentityText(channel, text) {
|
|
338
|
+
const value = text.trim();
|
|
339
|
+
if (value === '' || value === 'none') {
|
|
340
|
+
return true;
|
|
341
|
+
}
|
|
342
|
+
const numbers = value.split(/\s+/).map(part => Number.parseFloat(part));
|
|
343
|
+
if (numbers.some(number => !Number.isFinite(number))) {
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
const identity = channel === 'scale' || channel === 'opacity' ? 1 : 0;
|
|
347
|
+
return numbers.every(number => Math.abs(number - identity) < 1e-6);
|
|
348
|
+
}
|
|
349
|
+
function expand(values) {
|
|
350
|
+
const out = {};
|
|
351
|
+
for (const [key, value] of Object.entries(values)) {
|
|
352
|
+
if (value == null || !Number.isFinite(value)) {
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
if (key === 'scale') {
|
|
356
|
+
out.scaleX ??= value;
|
|
357
|
+
out.scaleY ??= value;
|
|
358
|
+
} else if (PROPERTIES.includes(key)) {
|
|
359
|
+
out[key] = value;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return out;
|
|
363
|
+
}
|
|
364
|
+
function defaultNow() {
|
|
365
|
+
return typeof performance === 'undefined' ? Date.now() : performance.now();
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Whether the user asked for reduced motion (`prefers-reduced-motion:
|
|
370
|
+
* reduce`), read at the moment of the call: the preference the animator uses
|
|
371
|
+
* by default for its Reduce Motion substitution. A component never reads it
|
|
372
|
+
* to pick a curve of its own: its motion settings do not override the
|
|
373
|
+
* policy (design:reduced-motion DR4); `animator.reducedMotion()` or
|
|
374
|
+
* `reducedMotionFade()` gives the policy's answer. False without a window
|
|
375
|
+
* (server rendering).
|
|
376
|
+
*/
|
|
377
|
+
export function defaultPrefersReducedMotion() {
|
|
378
|
+
return typeof window !== 'undefined' && typeof window.matchMedia === 'function' && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/** Whether the primary pointer is coarse: `(pointer: coarse)`. */
|
|
382
|
+
export function defaultIsCoarsePointer() {
|
|
383
|
+
return typeof window !== 'undefined' && typeof window.matchMedia === 'function' && window.matchMedia('(pointer: coarse)').matches;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* The transition `decay` hands a Reduce Motion transition curve to: under
|
|
388
|
+
* Reduce Motion `animate` plays no spring (a cross-fade, or at once), so the
|
|
389
|
+
* spring itself is never solved.
|
|
390
|
+
*/
|
|
391
|
+
const REDUCED_TRANSITION = {
|
|
392
|
+
spring: {
|
|
393
|
+
duration: 0.5,
|
|
394
|
+
bounce: 0
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Creates an animator for `element` (or for an element attached later).
|
|
400
|
+
*
|
|
401
|
+
* @example
|
|
402
|
+
* ```
|
|
403
|
+
* const animator = createAnimator(sheet);
|
|
404
|
+
* // finger lifts at 1200 px/s heading for the detent at 0:
|
|
405
|
+
* animator.animate({y: 0}, {spring: springs.sheetDetent, velocity: {y: 1200}});
|
|
406
|
+
* // the finger lands again: where is it right now?
|
|
407
|
+
* const y = animator.get('y', event.timeStamp);
|
|
408
|
+
* animator.stop(['y']);
|
|
409
|
+
* ```
|
|
410
|
+
*/
|
|
411
|
+
export function createAnimator(element, options = {}) {
|
|
412
|
+
const now = options.now ?? defaultNow;
|
|
413
|
+
const requestFrame = options.requestFrame ?? (typeof requestAnimationFrame === 'function' ? callback => {
|
|
414
|
+
requestAnimationFrame(() => callback());
|
|
415
|
+
} : null);
|
|
416
|
+
// Reduced Motion reads the person's settings and nothing else: no option
|
|
417
|
+
// overrides them (design:reduced-motion DR4).
|
|
418
|
+
const prefersReducedMotion = defaultPrefersReducedMotion;
|
|
419
|
+
const isCoarsePointer = defaultIsCoarsePointer;
|
|
420
|
+
const resting = {
|
|
421
|
+
...IDENTITY,
|
|
422
|
+
...expand(options.initial ?? {})
|
|
423
|
+
};
|
|
424
|
+
const segments = new Map();
|
|
425
|
+
const running = new Map();
|
|
426
|
+
/** Drops a finished, still-filling animation once the main thread runs. */
|
|
427
|
+
const retireTimers = new Map();
|
|
428
|
+
const touched = new Set();
|
|
429
|
+
/**
|
|
430
|
+
* The stylesheet's opacity a Reduce Motion dip borrowed because no call
|
|
431
|
+
* owned opacity; the dip runs from and back to it, then hands it back.
|
|
432
|
+
*/
|
|
433
|
+
let borrowedOpacity = null;
|
|
434
|
+
let target = null;
|
|
435
|
+
/** Calls from rest whose start still waits for the browser's commit. */
|
|
436
|
+
const pendingGroups = new Set();
|
|
437
|
+
/** atRest() calls still waiting; dispose settles them. */
|
|
438
|
+
const restWaits = new Set();
|
|
439
|
+
/** A detach that settles as an unmount at the end of the task, unless an attach follows. */
|
|
440
|
+
let unmountQueued = false;
|
|
441
|
+
/**
|
|
442
|
+
* The node the latest attach(null) detached, while its unmount is pending:
|
|
443
|
+
* a waiting Reduce Motion move lands there, so that node is left visible
|
|
444
|
+
* where it was going.
|
|
445
|
+
*/
|
|
446
|
+
let lastDetached = null;
|
|
447
|
+
/** Runs `write` with the pending unmount's node as the target. */
|
|
448
|
+
const onLastDetached = write => {
|
|
449
|
+
const current = target;
|
|
450
|
+
target = target ?? lastDetached;
|
|
451
|
+
try {
|
|
452
|
+
write();
|
|
453
|
+
} finally {
|
|
454
|
+
target = current;
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
/** The call being committed, when it starts from rest. */
|
|
458
|
+
let callGroup = null;
|
|
459
|
+
/** Channels promoted to the element's compositing layer (will-change). */
|
|
460
|
+
const promoted = new Set();
|
|
461
|
+
/** The element's own inline will-change, restored when promotion ends. */
|
|
462
|
+
let ownWillChange = null;
|
|
463
|
+
/** The stylesheet's will-change when there is no inline value. */
|
|
464
|
+
let sheetWillChange = '';
|
|
465
|
+
let demoteTimer = null;
|
|
466
|
+
/** Channels whose identity the last node held inline (see `attach`). */
|
|
467
|
+
let heldIdentity = new Set();
|
|
468
|
+
|
|
469
|
+
// -- reading the curves ---------------------------------------------------
|
|
470
|
+
|
|
471
|
+
const segmentAt = (property, time) => {
|
|
472
|
+
const segment = segments.get(property);
|
|
473
|
+
if (segment == null) {
|
|
474
|
+
return null;
|
|
475
|
+
}
|
|
476
|
+
if (time >= segment.start + segment.duration) {
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
return segment;
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* A curve whose call the browser has not started yet: still at its start
|
|
484
|
+
* on screen (valueAt), already moving in the model (velocityAt).
|
|
485
|
+
*/
|
|
486
|
+
const isHeld = segment => segment.group != null && !segment.group.resolved;
|
|
487
|
+
|
|
488
|
+
/** The curve itself at `time`: what the keyframes play. */
|
|
489
|
+
const curveAt = (property, time) => {
|
|
490
|
+
const segment = segments.get(property);
|
|
491
|
+
if (segment == null) {
|
|
492
|
+
return resting[property];
|
|
493
|
+
}
|
|
494
|
+
if (time >= segment.start + segment.duration) {
|
|
495
|
+
return segment.target;
|
|
496
|
+
}
|
|
497
|
+
return segment.at(Math.max(0, time - segment.start));
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* The value on screen at `time`. A curve the browser has not started is at
|
|
502
|
+
* its start up to now; a later time reads the curve from its provisional
|
|
503
|
+
* start, the model's best guess until the browser's start moves it (a
|
|
504
|
+
* companion that samples a curve ahead follows its shape).
|
|
505
|
+
*/
|
|
506
|
+
const valueAt = (property, time) => {
|
|
507
|
+
const segment = segments.get(property);
|
|
508
|
+
if (segment != null && isHeld(segment) && time <= now()) {
|
|
509
|
+
// Nothing has moved on screen until the browser starts the call.
|
|
510
|
+
return segment.at(0);
|
|
511
|
+
}
|
|
512
|
+
return curveAt(property, time);
|
|
513
|
+
};
|
|
514
|
+
const velocityAt = (property, time) => {
|
|
515
|
+
const held = segments.get(property);
|
|
516
|
+
if (held != null && isHeld(held)) {
|
|
517
|
+
// Still at its start on screen, but moving in the model: a retarget
|
|
518
|
+
// carries the velocity the curve has reached by now, so a flick whose
|
|
519
|
+
// moves all land before the browser starts any keeps its speed.
|
|
520
|
+
return held.speed(Math.max(0, time - held.start));
|
|
521
|
+
}
|
|
522
|
+
const segment = segmentAt(property, time);
|
|
523
|
+
if (segment == null || time < segment.start) {
|
|
524
|
+
return 0;
|
|
525
|
+
}
|
|
526
|
+
return segment.speed(time - segment.start);
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
// The curve just before a jump at absolute time `time`.
|
|
530
|
+
const valueBefore = (property, time) => curveAt(property, time - 1e-6);
|
|
531
|
+
|
|
532
|
+
// -- writing --------------------------------------------------------------
|
|
533
|
+
|
|
534
|
+
const finalValue = property => segments.get(property)?.target ?? resting[property];
|
|
535
|
+
const writeInline = (channel, value) => {
|
|
536
|
+
if (target == null) {
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
touched.add(channel);
|
|
540
|
+
const text = inlineText(channel, value);
|
|
541
|
+
if (text === IDENTITY_TEXT[channel]) {
|
|
542
|
+
// Identity leaves the element when nothing else would move it.
|
|
543
|
+
target.style.setProperty(channel, text);
|
|
544
|
+
releaseIdentity(channel);
|
|
545
|
+
} else {
|
|
546
|
+
target.style.setProperty(channel, text);
|
|
547
|
+
}
|
|
548
|
+
};
|
|
549
|
+
const sampleTimes = (property, from, to, tolerance) => {
|
|
550
|
+
const segment = segments.get(property);
|
|
551
|
+
if (segment == null) {
|
|
552
|
+
return [];
|
|
553
|
+
}
|
|
554
|
+
const begin = Math.max(from, segment.start);
|
|
555
|
+
const end = Math.min(to, segment.start + segment.duration);
|
|
556
|
+
if (end <= begin) {
|
|
557
|
+
return [];
|
|
558
|
+
}
|
|
559
|
+
const times = [begin];
|
|
560
|
+
const value = t => curveAt(property, t);
|
|
561
|
+
const breaks = segment.jumps.map(j => segment.start + j).filter(t => t > begin && t < end);
|
|
562
|
+
const edges = [begin, ...breaks, end];
|
|
563
|
+
for (let e = 0; e + 1 < edges.length; e++) {
|
|
564
|
+
const a = edges[e];
|
|
565
|
+
const b = edges[e + 1];
|
|
566
|
+
// A 120 Hz grid, each interval split until the chord is within the
|
|
567
|
+
// tolerance at its quarter points.
|
|
568
|
+
const steps = Math.max(1, Math.ceil((b - a) / (1000 / 120)));
|
|
569
|
+
const split = (t0, t1, depth) => {
|
|
570
|
+
const v0 = value(t0);
|
|
571
|
+
const v1 = t1 === b && breaks.includes(b) ? valueBefore(property, b) : value(t1);
|
|
572
|
+
let worst = 0;
|
|
573
|
+
for (const f of [0.25, 0.5, 0.75]) {
|
|
574
|
+
const t = t0 + (t1 - t0) * f;
|
|
575
|
+
worst = Math.max(worst, Math.abs(value(t) - (v0 + (v1 - v0) * f)));
|
|
576
|
+
}
|
|
577
|
+
if (worst > tolerance && depth < 10 && t1 - t0 > 0.25) {
|
|
578
|
+
const mid = (t0 + t1) / 2;
|
|
579
|
+
split(t0, mid, depth + 1);
|
|
580
|
+
split(mid, t1, depth + 1);
|
|
581
|
+
} else {
|
|
582
|
+
times.push(t1);
|
|
583
|
+
}
|
|
584
|
+
};
|
|
585
|
+
for (let i = 0; i < steps; i++) {
|
|
586
|
+
split(a + (b - a) * i / steps, a + (b - a) * (i + 1) / steps, 0);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
return times;
|
|
590
|
+
};
|
|
591
|
+
const rebuild = (channel, time) => {
|
|
592
|
+
const previous = running.get(channel);
|
|
593
|
+
running.delete(channel);
|
|
594
|
+
clearTimeout(retireTimers.get(channel));
|
|
595
|
+
retireTimers.delete(channel);
|
|
596
|
+
// The element's inline style holds the end state, so its old animation
|
|
597
|
+
// can go first.
|
|
598
|
+
previous?.cancel();
|
|
599
|
+
const properties = CHANNELS[channel];
|
|
600
|
+
// Settled curves become resting values.
|
|
601
|
+
for (const property of properties) {
|
|
602
|
+
const segment = segments.get(property);
|
|
603
|
+
if (segment != null && time >= segment.start + segment.duration) {
|
|
604
|
+
resting[property] = segment.target;
|
|
605
|
+
segments.delete(property);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
writeInline(channel, finalValue);
|
|
609
|
+
const moving = properties.filter(p => segments.has(p));
|
|
610
|
+
if (target == null || typeof target.animate !== 'function') {
|
|
611
|
+
previous?.cancel();
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
if (moving.length === 0) {
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
// A channel rebuilt by an explicit call (set, rebase, a retarget of
|
|
618
|
+
// another axis) while one of its curves still waits for the browser's
|
|
619
|
+
// start keeps that start: no explicit start for this animation.
|
|
620
|
+
const waiting = callGroup ?? moving.map(p => segments.get(p).group).find(group => group != null && !group.resolved) ?? null;
|
|
621
|
+
// The keyframes start where the animation will: at the waiting call's
|
|
622
|
+
// own time, which its start moves to the browser's (resolveGroup), so a
|
|
623
|
+
// write later in the task keeps the screen on the model's clock.
|
|
624
|
+
const from = waiting?.provisional ?? time;
|
|
625
|
+
const end = Math.min(from + MAX_DURATION_MS, Math.max(...moving.map(p => segments.get(p).start + segments.get(p).duration)));
|
|
626
|
+
if (end <= from) {
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
if (callGroup != null) {
|
|
630
|
+
// A curve of an earlier call still waiting for its first frame is
|
|
631
|
+
// rebaked here: it starts with this call's commit.
|
|
632
|
+
for (const property of moving) {
|
|
633
|
+
const segment = segments.get(property);
|
|
634
|
+
const group = segment.group;
|
|
635
|
+
if (group != null && group !== callGroup && !group.resolved) {
|
|
636
|
+
const index = group.segments.indexOf(segment);
|
|
637
|
+
if (index >= 0) {
|
|
638
|
+
group.segments.splice(index, 1);
|
|
639
|
+
}
|
|
640
|
+
group.adoptedBy = callGroup;
|
|
641
|
+
segment.group = callGroup;
|
|
642
|
+
// Re-dated to this call's time: it starts at the commit too, not
|
|
643
|
+
// the work between the two calls into its curve.
|
|
644
|
+
segment.start += callGroup.provisional - group.provisional;
|
|
645
|
+
callGroup.segments.push(segment);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
const collect = scale => {
|
|
650
|
+
const times = new Set([from, end]);
|
|
651
|
+
for (const property of moving) {
|
|
652
|
+
for (const t of sampleTimes(property, from, end, SAMPLE_TOLERANCE[property] * scale)) {
|
|
653
|
+
times.add(t);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
return times;
|
|
657
|
+
};
|
|
658
|
+
let scale = 1;
|
|
659
|
+
let all = collect(scale);
|
|
660
|
+
while (all.size > MAX_KEYFRAMES && scale < 64) {
|
|
661
|
+
scale *= 2;
|
|
662
|
+
all = collect(scale);
|
|
663
|
+
}
|
|
664
|
+
// Jumps become two keyframes at one offset: the value just before, then
|
|
665
|
+
// the value after. A jump on the last frame (fade out, then move) too.
|
|
666
|
+
const jumpTimes = new Set();
|
|
667
|
+
for (const property of moving) {
|
|
668
|
+
const segment = segments.get(property);
|
|
669
|
+
for (const j of segment.jumps) {
|
|
670
|
+
const t = segment.start + j;
|
|
671
|
+
if (t > from && t <= end) {
|
|
672
|
+
jumpTimes.add(t);
|
|
673
|
+
all.add(t);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
const times = [...all].sort((a, b) => a - b);
|
|
678
|
+
const span = end - from + END_HOLD_MS;
|
|
679
|
+
const keyframes = [];
|
|
680
|
+
for (const t of times) {
|
|
681
|
+
const offset = (t - from) / span;
|
|
682
|
+
if (jumpTimes.has(t)) {
|
|
683
|
+
keyframes.push({
|
|
684
|
+
offset,
|
|
685
|
+
[channel]: format(channel, p => valueBefore(p, t))
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
keyframes.push({
|
|
689
|
+
offset,
|
|
690
|
+
[channel]: format(channel, p => t >= end ? finalValue(p) : curveAt(p, t))
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
keyframes.push({
|
|
694
|
+
offset: 1,
|
|
695
|
+
[channel]: format(channel, finalValue)
|
|
696
|
+
});
|
|
697
|
+
promote(channel);
|
|
698
|
+
const animation = target.animate(keyframes, {
|
|
699
|
+
duration: span,
|
|
700
|
+
// The last keyframes hold the end state (END_HOLD_MS); filling both
|
|
701
|
+
// ways also keeps it where an engine honors the fill on its compositor.
|
|
702
|
+
// The main thread drops the animation once it runs after the end; the
|
|
703
|
+
// inline style already holds the same values, so that changes no pixel.
|
|
704
|
+
fill: 'both',
|
|
705
|
+
easing: 'linear'
|
|
706
|
+
});
|
|
707
|
+
if (waiting != null) {
|
|
708
|
+
// From rest: no start time, so the browser starts the animation when
|
|
709
|
+
// it commits it (StartGroup); the keyframes are sampled from `from`,
|
|
710
|
+
// the call's own start.
|
|
711
|
+
waiting.animations.push(animation);
|
|
712
|
+
waiting.channels.add(channel);
|
|
713
|
+
// A cancel before the first frame rejects its ready promise.
|
|
714
|
+
animation.ready?.catch(() => {});
|
|
715
|
+
} else {
|
|
716
|
+
// One clock: the keyframes were sampled from `time`.
|
|
717
|
+
animation.startTime = time;
|
|
718
|
+
}
|
|
719
|
+
running.set(channel, animation);
|
|
720
|
+
previous?.cancel();
|
|
721
|
+
scheduleRetire(channel, animation, end);
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* Once the main thread runs after the curve's end, the animation leaves the
|
|
726
|
+
* running state: the element's inline style already shows the end values.
|
|
727
|
+
*/
|
|
728
|
+
const scheduleRetire = (channel, animation, end) => {
|
|
729
|
+
clearTimeout(retireTimers.get(channel));
|
|
730
|
+
const retire = () => {
|
|
731
|
+
retireTimers.delete(channel);
|
|
732
|
+
if (running.get(channel) !== animation) {
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
// Paused or sought (a sampler holding it): retire when its own time
|
|
736
|
+
// reaches the curve's end, never from the wall clock.
|
|
737
|
+
const remaining = remainingCurve(animation);
|
|
738
|
+
if (remaining != null && remaining > 0) {
|
|
739
|
+
retireTimers.set(channel, setTimeout(retire, animation.playState === 'paused' ? RETIRE_RECHECK_MS : remaining));
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
running.delete(channel);
|
|
743
|
+
animation.cancel();
|
|
744
|
+
releaseIdentity(channel);
|
|
745
|
+
flushReleases();
|
|
746
|
+
};
|
|
747
|
+
retireTimers.set(channel, setTimeout(retire, Math.max(0, end - now())));
|
|
748
|
+
};
|
|
749
|
+
|
|
750
|
+
/** The element's own (stylesheet) opacity, read once when a dip starts. */
|
|
751
|
+
const ownOpacity = () => {
|
|
752
|
+
const view = target?.ownerDocument.defaultView;
|
|
753
|
+
const value = target == null ? NaN : Number.parseFloat(view?.getComputedStyle(target).getPropertyValue('opacity') ?? '');
|
|
754
|
+
return Number.isFinite(value) ? Math.min(1, Math.max(0, value)) : 1;
|
|
755
|
+
};
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* Whether the animator holds an opacity of its own on the element: an inline
|
|
759
|
+
* value it wrote (a `set` over a stylesheet that hides or dims it). A dip
|
|
760
|
+
* returns to that value.
|
|
761
|
+
*/
|
|
762
|
+
const writesOwnOpacity = () => (target?.style.getPropertyValue('opacity') ?? '') !== '';
|
|
763
|
+
|
|
764
|
+
/** Gives the element's opacity back to its stylesheet: its inline value goes. */
|
|
765
|
+
const releaseOwnOpacity = () => {
|
|
766
|
+
target?.style.removeProperty('opacity');
|
|
767
|
+
};
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* Lands every Reduce Motion move still waiting, at once, on the current
|
|
771
|
+
* element: its travel, and the opacity its fade was heading to (the
|
|
772
|
+
* stylesheet's again for a borrowed one). Its calls settle `finished`.
|
|
773
|
+
*/
|
|
774
|
+
const landPending = finished => {
|
|
775
|
+
if (landings.size === 0) {
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
const changed = new Set();
|
|
779
|
+
let handedBack = false;
|
|
780
|
+
for (const landing of [...landings]) {
|
|
781
|
+
endLanding(landing);
|
|
782
|
+
for (const [property, value] of landing.values) {
|
|
783
|
+
segments.delete(property);
|
|
784
|
+
resting[property] = value;
|
|
785
|
+
changed.add(property);
|
|
786
|
+
}
|
|
787
|
+
if (landing.fadeIn != null || landing.restore != null) {
|
|
788
|
+
const borrowed = landing.fadeIn?.isBorrowed === true || landing.restoreIsBorrowed === true;
|
|
789
|
+
segments.delete('opacity');
|
|
790
|
+
resting.opacity = borrowed ? IDENTITY.opacity : landing.fadeIn?.to ?? landing.restore ?? IDENTITY.opacity;
|
|
791
|
+
changed.add('opacity');
|
|
792
|
+
if (borrowed) {
|
|
793
|
+
borrowedOpacity = null;
|
|
794
|
+
handedBack = true;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
for (const owner of [landing.owner, ...landing.carried]) {
|
|
798
|
+
carriedOwners.delete(owner);
|
|
799
|
+
if (!owner.settled) {
|
|
800
|
+
settle(owner, finished);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
commit(changed, now());
|
|
805
|
+
if (handedBack) {
|
|
806
|
+
// The stylesheet owns its opacity again: no value stays inline.
|
|
807
|
+
releaseOwnOpacity();
|
|
808
|
+
}
|
|
809
|
+
};
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* Ends a borrowed dip on the element it ran on: the stylesheet owns its
|
|
813
|
+
* opacity again (an unmount or a new element never keeps the borrow).
|
|
814
|
+
*/
|
|
815
|
+
const handBackBorrow = () => {
|
|
816
|
+
if (borrowedOpacity == null) {
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
borrowedOpacity = null;
|
|
820
|
+
segments.delete('opacity');
|
|
821
|
+
resting.opacity = IDENTITY.opacity;
|
|
822
|
+
releaseOwnOpacity();
|
|
823
|
+
};
|
|
824
|
+
|
|
825
|
+
/**
|
|
826
|
+
* A channel at identity with no animation of its own leaves the element:
|
|
827
|
+
* its inline value is removed, unless the stylesheet would then move the
|
|
828
|
+
* element, in which case the identity stays inline. Runs when such a value
|
|
829
|
+
* is written and again when a motion ends.
|
|
830
|
+
*/
|
|
831
|
+
const releaseIdentity = channel => {
|
|
832
|
+
if (target == null || running.has(channel)) {
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
if (channel === 'opacity' && borrowedOpacity != null) {
|
|
836
|
+
if ([...landings].some(landing => landing.fadeIn?.isBorrowed === true)) {
|
|
837
|
+
// At the bottom of the dip, before it lands and fades back in.
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
// A dip on the stylesheet's opacity ended: the stylesheet owns it again.
|
|
841
|
+
borrowedOpacity = null;
|
|
842
|
+
segments.delete('opacity');
|
|
843
|
+
resting.opacity = IDENTITY.opacity;
|
|
844
|
+
releaseOwnOpacity();
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
if (target.style.getPropertyValue(channel) !== IDENTITY_TEXT[channel]) {
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
// Read with every other channel the same commit leaves at identity: one
|
|
851
|
+
// style read for the element, not one per channel (flushReleases).
|
|
852
|
+
releasing.add(channel);
|
|
853
|
+
if (!releaseQueued) {
|
|
854
|
+
releaseQueued = true;
|
|
855
|
+
queueMicrotask(flushReleases);
|
|
856
|
+
}
|
|
857
|
+
};
|
|
858
|
+
|
|
859
|
+
/** Channels left at identity whose stylesheet value is still to be read. */
|
|
860
|
+
const releasing = new Set();
|
|
861
|
+
let releaseQueued = false;
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* Removes the identity values written since the last flush, reads the
|
|
865
|
+
* stylesheet once for all of them, and puts back the ones it would move:
|
|
866
|
+
* a single forced style read per element and commit, where a read per
|
|
867
|
+
* channel used to force one each.
|
|
868
|
+
*/
|
|
869
|
+
const flushReleases = () => {
|
|
870
|
+
releaseQueued = false;
|
|
871
|
+
const element = target;
|
|
872
|
+
const channels = [...releasing].filter(channel => !running.has(channel) && element?.style.getPropertyValue(channel) === IDENTITY_TEXT[channel]);
|
|
873
|
+
releasing.clear();
|
|
874
|
+
if (element == null || channels.length === 0) {
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
for (const channel of channels) {
|
|
878
|
+
element.style.removeProperty(channel);
|
|
879
|
+
}
|
|
880
|
+
const style = element.ownerDocument.defaultView?.getComputedStyle(element);
|
|
881
|
+
for (const channel of channels) {
|
|
882
|
+
const underneath = style?.getPropertyValue(channel);
|
|
883
|
+
if (underneath != null && !isIdentityText(channel, underneath)) {
|
|
884
|
+
element.style.setProperty(channel, IDENTITY_TEXT[channel]);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
};
|
|
888
|
+
|
|
889
|
+
/**
|
|
890
|
+
* Curve time an animation still has to play on its own clock (its keyframes
|
|
891
|
+
* hold the end for END_HOLD_MS after the curve), or null where the engine
|
|
892
|
+
* does not report it; a paused animation always has some left.
|
|
893
|
+
*/
|
|
894
|
+
const remainingCurve = animation => {
|
|
895
|
+
const timing = animation.effect?.getTiming?.();
|
|
896
|
+
const current = animation.currentTime;
|
|
897
|
+
if (timing == null || current == null) {
|
|
898
|
+
return animation.playState === 'paused' ? Infinity : null;
|
|
899
|
+
}
|
|
900
|
+
const left = Number(timing.duration) - END_HOLD_MS - Number(current);
|
|
901
|
+
return animation.playState === 'paused' ? Math.max(left, 1) : left;
|
|
902
|
+
};
|
|
903
|
+
|
|
904
|
+
// -- promotion -------------------------------------------------------------
|
|
905
|
+
|
|
906
|
+
/** Writes the promoted channels as will-change. */
|
|
907
|
+
const writePromotion = element => {
|
|
908
|
+
const list = [...promoted].join(', ');
|
|
909
|
+
if (list === '') {
|
|
910
|
+
element.style.willChange = ownWillChange ?? '';
|
|
911
|
+
ownWillChange = null;
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
if (ownWillChange == null) {
|
|
915
|
+
ownWillChange = element.style.willChange;
|
|
916
|
+
// Without an inline value, the stylesheet's reasons stay alongside the
|
|
917
|
+
// promotion's (read once, as the promotion starts).
|
|
918
|
+
sheetWillChange = ownWillChange === '' ? element.ownerDocument.defaultView?.getComputedStyle(element).getPropertyValue('will-change') ?? '' : '';
|
|
919
|
+
}
|
|
920
|
+
const base = ownWillChange !== '' ? ownWillChange : sheetWillChange;
|
|
921
|
+
const own = base === 'auto' ? '' : base;
|
|
922
|
+
const reasons = own.split(',').map(reason => reason.trim()).filter(reason => reason !== '' && !promoted.has(reason));
|
|
923
|
+
element.style.willChange = [...reasons, ...promoted].join(', ');
|
|
924
|
+
};
|
|
925
|
+
|
|
926
|
+
/**
|
|
927
|
+
* Gives the element a compositing layer for
|
|
928
|
+
* `channel` from the task that starts the motion: Mobile Safari runs an
|
|
929
|
+
* animation whose start time is set from script on its compositor at its
|
|
930
|
+
* first commit only if the element already has a layer.
|
|
931
|
+
*/
|
|
932
|
+
const promote = channel => {
|
|
933
|
+
if (target == null) {
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
if (!promoted.has(channel)) {
|
|
937
|
+
promoted.add(channel);
|
|
938
|
+
writePromotion(target);
|
|
939
|
+
}
|
|
940
|
+
scheduleDemote();
|
|
941
|
+
};
|
|
942
|
+
|
|
943
|
+
/** Ends promotion PROMOTION_LINGER_MS after the last motion ends. */
|
|
944
|
+
const scheduleDemote = () => {
|
|
945
|
+
if (demoteTimer != null) {
|
|
946
|
+
clearTimeout(demoteTimer);
|
|
947
|
+
}
|
|
948
|
+
let end = now();
|
|
949
|
+
for (const segment of segments.values()) {
|
|
950
|
+
end = Math.max(end, segment.start + segment.duration);
|
|
951
|
+
}
|
|
952
|
+
demoteTimer = setTimeout(() => {
|
|
953
|
+
demoteTimer = null;
|
|
954
|
+
const time = now();
|
|
955
|
+
const moving = [...segments.values()].some(segment => time < segment.start + segment.duration);
|
|
956
|
+
if (moving || pendingGroups.size > 0) {
|
|
957
|
+
scheduleDemote();
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
960
|
+
demote();
|
|
961
|
+
}, Math.max(0, end - now()) + PROMOTION_LINGER_MS);
|
|
962
|
+
};
|
|
963
|
+
const demote = () => {
|
|
964
|
+
if (demoteTimer != null) {
|
|
965
|
+
clearTimeout(demoteTimer);
|
|
966
|
+
demoteTimer = null;
|
|
967
|
+
}
|
|
968
|
+
if (promoted.size === 0) {
|
|
969
|
+
return;
|
|
970
|
+
}
|
|
971
|
+
promoted.clear();
|
|
972
|
+
if (target != null) {
|
|
973
|
+
writePromotion(target);
|
|
974
|
+
}
|
|
975
|
+
};
|
|
976
|
+
|
|
977
|
+
// -- starting from rest ---------------------------------------------------
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* A call starts from rest when nothing on the channels it drives is moving
|
|
981
|
+
* (a curve still waiting for its first frame counts as rest) and nothing
|
|
982
|
+
* pins its clock (`at`). Returns its start group, or null for an explicit
|
|
983
|
+
* start.
|
|
984
|
+
*/
|
|
985
|
+
const beginGroup = (time, properties, at) => {
|
|
986
|
+
if (target == null || typeof target.animate !== 'function') {
|
|
987
|
+
return null;
|
|
988
|
+
}
|
|
989
|
+
const channels = new Set();
|
|
990
|
+
for (const property of properties) {
|
|
991
|
+
channels.add(CHANNEL_OF[property]);
|
|
992
|
+
}
|
|
993
|
+
if (at != null) {
|
|
994
|
+
// An explicit clock is the caller's, except a clock no later than a
|
|
995
|
+
// call from rest on these channels still waiting for the browser's
|
|
996
|
+
// start (an event timestamp, such as a drop's, for the reorder that
|
|
997
|
+
// rebases it): the new call joins that call, and the browser's start
|
|
998
|
+
// moves both. A later clock is a later call, often a frame or more
|
|
999
|
+
// later while the browser has not started the first: it keeps its own
|
|
1000
|
+
// clock, since joining would sample its curve from the earlier call's
|
|
1001
|
+
// time and hold its first value for the gap.
|
|
1002
|
+
for (const channel of channels) {
|
|
1003
|
+
for (const property of CHANNELS[channel]) {
|
|
1004
|
+
const group = segments.get(property)?.group;
|
|
1005
|
+
if (group != null && !group.resolved && at <= group.provisional) {
|
|
1006
|
+
return group;
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
return null;
|
|
1011
|
+
}
|
|
1012
|
+
for (const channel of channels) {
|
|
1013
|
+
for (const property of CHANNELS[channel]) {
|
|
1014
|
+
const segment = segments.get(property);
|
|
1015
|
+
if (segment != null && time < segment.start + segment.duration && (segment.group == null || segment.group.resolved)) {
|
|
1016
|
+
return null;
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
let resolve = () => {};
|
|
1021
|
+
const started = new Promise(r => {
|
|
1022
|
+
resolve = r;
|
|
1023
|
+
});
|
|
1024
|
+
return {
|
|
1025
|
+
provisional: time,
|
|
1026
|
+
segments: [],
|
|
1027
|
+
animations: [],
|
|
1028
|
+
channels: new Set(),
|
|
1029
|
+
resolved: false,
|
|
1030
|
+
resolve,
|
|
1031
|
+
started
|
|
1032
|
+
};
|
|
1033
|
+
};
|
|
1034
|
+
|
|
1035
|
+
/** Commits a call's changes, from rest when it has a start group. */
|
|
1036
|
+
const commitCall = (group, owner, changed, time) => {
|
|
1037
|
+
if (group != null) {
|
|
1038
|
+
for (const property of changed) {
|
|
1039
|
+
const segment = segments.get(property);
|
|
1040
|
+
if (segment != null && segment.owner === owner) {
|
|
1041
|
+
segment.group = group;
|
|
1042
|
+
group.segments.push(segment);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
callGroup = group;
|
|
1047
|
+
try {
|
|
1048
|
+
commit(changed, time);
|
|
1049
|
+
} finally {
|
|
1050
|
+
callGroup = null;
|
|
1051
|
+
}
|
|
1052
|
+
if (group == null) {
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
if (group.animations.length === 0) {
|
|
1056
|
+
resolveGroup(group, group.provisional); // nothing to start
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
pendingGroups.add(group);
|
|
1060
|
+
for (const animation of group.animations) {
|
|
1061
|
+
// A cancel before the first frame rejects each one's ready promise.
|
|
1062
|
+
animation.ready?.catch(() => {});
|
|
1063
|
+
}
|
|
1064
|
+
const ready = group.animations[0].ready;
|
|
1065
|
+
if (ready == null || typeof ready.then !== 'function') {
|
|
1066
|
+
resolveGroup(group, group.provisional); // no pending start to wait for
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
// The browser sets the start: at the frame that commits the animations,
|
|
1070
|
+
// or when its compositor starts them (Chromium). Read it in that first
|
|
1071
|
+
// frame, ahead of anything that might pause or seek the animations, and
|
|
1072
|
+
// when `ready` settles.
|
|
1073
|
+
requestFrame?.(() => syncGroup(group));
|
|
1074
|
+
ready.then(() => syncGroup(group), () => settleCancelled(group));
|
|
1075
|
+
};
|
|
1076
|
+
|
|
1077
|
+
/**
|
|
1078
|
+
* A call whose first animation was cancelled before its first frame: if a
|
|
1079
|
+
* later call in the same task took over its curves, it starts with that
|
|
1080
|
+
* call; otherwise it never started.
|
|
1081
|
+
*/
|
|
1082
|
+
const settleCancelled = group => {
|
|
1083
|
+
if (group.resolved) {
|
|
1084
|
+
return;
|
|
1085
|
+
}
|
|
1086
|
+
let adopter = group.adoptedBy;
|
|
1087
|
+
while (adopter?.adoptedBy != null) {
|
|
1088
|
+
adopter = adopter.adoptedBy;
|
|
1089
|
+
}
|
|
1090
|
+
if (adopter != null && adopter !== group) {
|
|
1091
|
+
void adopter.started.then(start => resolveGroup(group, start));
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
// A same-task write replaced the animation the call waited on: the call
|
|
1095
|
+
// still starts with the browser's start of the ones that remain.
|
|
1096
|
+
if (awaitStart(group)) {
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
resolveGroup(group, group.provisional);
|
|
1100
|
+
};
|
|
1101
|
+
|
|
1102
|
+
/**
|
|
1103
|
+
* Waits for the browser's start of a call through one of its animations
|
|
1104
|
+
* still playing; false when none is left to wait on.
|
|
1105
|
+
*/
|
|
1106
|
+
const awaitStart = group => {
|
|
1107
|
+
const live = group.animations.find(animation => animation.playState !== 'idle');
|
|
1108
|
+
const ready = live?.ready;
|
|
1109
|
+
if (ready == null || typeof ready.then !== 'function') {
|
|
1110
|
+
return false;
|
|
1111
|
+
}
|
|
1112
|
+
ready.then(() => syncGroup(group), () => settleCancelled(group));
|
|
1113
|
+
return true;
|
|
1114
|
+
};
|
|
1115
|
+
const syncGroup = group => {
|
|
1116
|
+
if (group.resolved) {
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
for (const animation of group.animations) {
|
|
1120
|
+
if (animation.startTime != null) {
|
|
1121
|
+
resolveGroup(group, Number(animation.startTime));
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
// Finished from outside before the browser started it (a sampler's
|
|
1126
|
+
// finish() on a held animation): nothing is left to wait on, so the call
|
|
1127
|
+
// keeps its own clock, and a later call on an explicit clock does not
|
|
1128
|
+
// join it and wait for a start that never comes.
|
|
1129
|
+
if (group.animations.length > 0 && group.animations.every(animation => animation.playState === 'finished' || animation.playState === 'idle') && group.animations.some(animation => animation.playState === 'finished')) {
|
|
1130
|
+
resolveGroup(group, group.provisional);
|
|
1131
|
+
return;
|
|
1132
|
+
}
|
|
1133
|
+
// No start yet (paused before its first frame by a harness or a page,
|
|
1134
|
+
// or resumed with play() and still pending): neither `ready` nor the
|
|
1135
|
+
// first frame will look again, so keep looking each frame until the call
|
|
1136
|
+
// has a start, or its animations finish (its own clock) or are cancelled.
|
|
1137
|
+
if (group.animations.some(animation => animation.startTime == null && animation.playState !== 'idle')) {
|
|
1138
|
+
requestFrame?.(() => syncGroup(group));
|
|
1139
|
+
}
|
|
1140
|
+
};
|
|
1141
|
+
|
|
1142
|
+
/** Reads the start of every call whose first frame has been committed. */
|
|
1143
|
+
const syncPending = () => {
|
|
1144
|
+
for (const group of pendingGroups) {
|
|
1145
|
+
syncGroup(group);
|
|
1146
|
+
}
|
|
1147
|
+
syncFinished();
|
|
1148
|
+
};
|
|
1149
|
+
|
|
1150
|
+
/**
|
|
1151
|
+
* An animation finished from outside (a test utility's or devtools'
|
|
1152
|
+
* `finish()`) shows its end state while the model still reads its curve:
|
|
1153
|
+
* the curve ends now in the model too, so the next call starts from what
|
|
1154
|
+
* is on screen.
|
|
1155
|
+
*/
|
|
1156
|
+
const syncFinished = () => {
|
|
1157
|
+
for (const [channel, animation] of running) {
|
|
1158
|
+
if (animation.playState !== 'finished') {
|
|
1159
|
+
continue;
|
|
1160
|
+
}
|
|
1161
|
+
const time = now();
|
|
1162
|
+
for (const property of CHANNELS[channel]) {
|
|
1163
|
+
const segment = segments.get(property);
|
|
1164
|
+
if (segment == null || time >= segment.start + segment.duration) {
|
|
1165
|
+
continue;
|
|
1166
|
+
}
|
|
1167
|
+
segments.delete(property);
|
|
1168
|
+
resting[property] = segment.target;
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
};
|
|
1172
|
+
|
|
1173
|
+
/** Moves a call from rest onto the time the browser started it. */
|
|
1174
|
+
const resolveGroup = (group, start) => {
|
|
1175
|
+
if (group.resolved) {
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
group.resolved = true;
|
|
1179
|
+
pendingGroups.delete(group);
|
|
1180
|
+
for (const animation of group.animations) {
|
|
1181
|
+
// One clock for the whole call; a cancelled one stays cancelled (a
|
|
1182
|
+
// start time would play it again).
|
|
1183
|
+
if (animation.startTime == null && animation.playState !== 'idle') {
|
|
1184
|
+
animation.startTime = start;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
const delta = start - group.provisional;
|
|
1188
|
+
if (delta !== 0) {
|
|
1189
|
+
for (const segment of group.segments) {
|
|
1190
|
+
segment.start += delta;
|
|
1191
|
+
}
|
|
1192
|
+
for (const channel of group.channels) {
|
|
1193
|
+
const animation = running.get(channel);
|
|
1194
|
+
if (animation == null || !group.animations.includes(animation)) {
|
|
1195
|
+
continue;
|
|
1196
|
+
}
|
|
1197
|
+
const ends = CHANNELS[channel].flatMap(p => {
|
|
1198
|
+
const segment = segments.get(p);
|
|
1199
|
+
return segment == null ? [] : [segment.start + segment.duration];
|
|
1200
|
+
});
|
|
1201
|
+
if (ends.length > 0) {
|
|
1202
|
+
scheduleRetire(channel, animation, Math.max(...ends));
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
for (const owner of new Set(group.segments.map(s => s.owner))) {
|
|
1206
|
+
if (owner.settled || owner.timer == null) {
|
|
1207
|
+
continue;
|
|
1208
|
+
}
|
|
1209
|
+
clearTimeout(owner.timer);
|
|
1210
|
+
const ends = group.segments.filter(segment => segment.owner === owner).map(segment => segment.start + segment.duration);
|
|
1211
|
+
owner.timer = setTimeout(() => {
|
|
1212
|
+
owner.timer = null;
|
|
1213
|
+
settle(owner, true);
|
|
1214
|
+
}, Math.max(0, Math.max(start, ...ends) - now()));
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
group.resolve(start);
|
|
1218
|
+
};
|
|
1219
|
+
|
|
1220
|
+
// -- handles --------------------------------------------------------------
|
|
1221
|
+
|
|
1222
|
+
const lastSpring = new WeakMap();
|
|
1223
|
+
const interrupt = (property, time) => {
|
|
1224
|
+
cancelLanding(property);
|
|
1225
|
+
const segment = segments.get(property);
|
|
1226
|
+
if (segment == null) {
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
if (time < segment.start + segment.duration && !carriedOwners.has(segment.owner)) {
|
|
1230
|
+
settle(segment.owner, false);
|
|
1231
|
+
}
|
|
1232
|
+
};
|
|
1233
|
+
const settle = (owner, finished) => {
|
|
1234
|
+
if (owner.settled) {
|
|
1235
|
+
return;
|
|
1236
|
+
}
|
|
1237
|
+
owner.settled = true;
|
|
1238
|
+
if (owner.timer != null) {
|
|
1239
|
+
clearTimeout(owner.timer);
|
|
1240
|
+
owner.timer = null;
|
|
1241
|
+
}
|
|
1242
|
+
owner.resolve(finished);
|
|
1243
|
+
};
|
|
1244
|
+
const makeHandle = (owner, properties, finished, time, group = null) => {
|
|
1245
|
+
const ends = properties.map(p => {
|
|
1246
|
+
const segment = segments.get(p);
|
|
1247
|
+
return segment != null && segment.owner === owner ? segment.start + segment.duration : time;
|
|
1248
|
+
});
|
|
1249
|
+
const end = Math.max(time, ...ends);
|
|
1250
|
+
if (properties.length === 0 || end <= time) {
|
|
1251
|
+
settle(owner, true);
|
|
1252
|
+
} else {
|
|
1253
|
+
// Interruptions settle false as they happen (interrupt); reaching the
|
|
1254
|
+
// end untouched is completion.
|
|
1255
|
+
owner.timer = setTimeout(() => {
|
|
1256
|
+
owner.timer = null;
|
|
1257
|
+
settle(owner, true);
|
|
1258
|
+
}, Math.max(0, end - now()));
|
|
1259
|
+
}
|
|
1260
|
+
const handle = {
|
|
1261
|
+
properties,
|
|
1262
|
+
finished,
|
|
1263
|
+
started: group?.started ?? Promise.resolve(time),
|
|
1264
|
+
value: (property, at = now()) => {
|
|
1265
|
+
syncPending();
|
|
1266
|
+
return valueAt(property, at);
|
|
1267
|
+
},
|
|
1268
|
+
velocity: (property, at = now()) => {
|
|
1269
|
+
syncPending();
|
|
1270
|
+
return velocityAt(property, at);
|
|
1271
|
+
},
|
|
1272
|
+
retarget: (to, transition, retargetOptions) => {
|
|
1273
|
+
const spring = transition?.spring ?? lastSpring.get(owner);
|
|
1274
|
+
if (spring == null) {
|
|
1275
|
+
throw new Error('animator: retarget needs a spring');
|
|
1276
|
+
}
|
|
1277
|
+
return animator.animate(to, {
|
|
1278
|
+
...transition,
|
|
1279
|
+
spring
|
|
1280
|
+
}, retargetOptions);
|
|
1281
|
+
},
|
|
1282
|
+
// The call's own curves, and its travel still waiting to land under
|
|
1283
|
+
// Reduce Motion: a catch freezes both where they are on screen
|
|
1284
|
+
// (AST-094 FR6), travel first so the fade's landing no longer carries
|
|
1285
|
+
// it, as `animator.stop()` does.
|
|
1286
|
+
stop: () => animator.stop(PROPERTIES.filter(p => properties.includes(p) && (segments.get(p)?.owner === owner || [...landings].some(landing => landing.values.has(p) && (landing.owners.get(p) ?? landing.owner) === owner))))
|
|
1287
|
+
};
|
|
1288
|
+
return handle;
|
|
1289
|
+
};
|
|
1290
|
+
const newOwner = () => {
|
|
1291
|
+
let resolve = () => {};
|
|
1292
|
+
const finished = new Promise(r => {
|
|
1293
|
+
resolve = r;
|
|
1294
|
+
});
|
|
1295
|
+
return {
|
|
1296
|
+
owner: {
|
|
1297
|
+
resolve,
|
|
1298
|
+
settled: false,
|
|
1299
|
+
timer: null
|
|
1300
|
+
},
|
|
1301
|
+
finished
|
|
1302
|
+
};
|
|
1303
|
+
};
|
|
1304
|
+
|
|
1305
|
+
// -- segment factories ------------------------------------------------------
|
|
1306
|
+
|
|
1307
|
+
const springSegment = (owner, property, start, to, spring, velocity) => {
|
|
1308
|
+
const from = valueAt(property, start);
|
|
1309
|
+
if (from === to && velocity === 0) {
|
|
1310
|
+
return null;
|
|
1311
|
+
}
|
|
1312
|
+
const curve = solveSpring(toSpring(spring), {
|
|
1313
|
+
from,
|
|
1314
|
+
to,
|
|
1315
|
+
velocity
|
|
1316
|
+
});
|
|
1317
|
+
const duration = Math.min(MAX_DURATION_MS, curve.settleTime(SETTLE_THRESHOLD[property]) * 1000);
|
|
1318
|
+
return {
|
|
1319
|
+
start,
|
|
1320
|
+
duration,
|
|
1321
|
+
target: to,
|
|
1322
|
+
at: t => curve.value(t / 1000),
|
|
1323
|
+
speed: t => curve.velocity(t / 1000),
|
|
1324
|
+
jumps: [],
|
|
1325
|
+
owner,
|
|
1326
|
+
spring
|
|
1327
|
+
};
|
|
1328
|
+
};
|
|
1329
|
+
const fadeSegment = (owner, start, stops, jumps = []) => {
|
|
1330
|
+
const ease = cubicBezier(reducedMotionCrossFade.curve);
|
|
1331
|
+
const duration = stops[stops.length - 1].at;
|
|
1332
|
+
const at = t => {
|
|
1333
|
+
for (let i = 0; i + 1 < stops.length; i++) {
|
|
1334
|
+
const a = stops[i];
|
|
1335
|
+
const b = stops[i + 1];
|
|
1336
|
+
if (t < b.at || i + 2 === stops.length) {
|
|
1337
|
+
if (b.at === a.at) {
|
|
1338
|
+
return b.value;
|
|
1339
|
+
}
|
|
1340
|
+
const p = Math.min(1, Math.max(0, (t - a.at) / (b.at - a.at)));
|
|
1341
|
+
return a.value + (b.value - a.value) * ease(p);
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
return stops[stops.length - 1].value;
|
|
1345
|
+
};
|
|
1346
|
+
return {
|
|
1347
|
+
start,
|
|
1348
|
+
duration,
|
|
1349
|
+
target: stops[stops.length - 1].value,
|
|
1350
|
+
at,
|
|
1351
|
+
speed: t => (at(t + 0.5) - at(Math.max(0, t - 0.5))) / (t >= 0.5 ? 1 : 0.5 + t) * 1000,
|
|
1352
|
+
jumps,
|
|
1353
|
+
owner,
|
|
1354
|
+
isFade: true
|
|
1355
|
+
};
|
|
1356
|
+
};
|
|
1357
|
+
|
|
1358
|
+
// -- the animator -----------------------------------------------------------
|
|
1359
|
+
|
|
1360
|
+
const commit = (changed, time) => {
|
|
1361
|
+
const channels = new Set();
|
|
1362
|
+
for (const property of changed) {
|
|
1363
|
+
channels.add(CHANNEL_OF[property]);
|
|
1364
|
+
}
|
|
1365
|
+
if (channels.has('opacity')) {
|
|
1366
|
+
borrowedOpacity = null;
|
|
1367
|
+
}
|
|
1368
|
+
for (const channel of channels) {
|
|
1369
|
+
rebuild(channel, time);
|
|
1370
|
+
}
|
|
1371
|
+
flushReleases();
|
|
1372
|
+
};
|
|
1373
|
+
|
|
1374
|
+
/** Reduced Motion landings still waiting for the element to be transparent. */
|
|
1375
|
+
const landings = new Set();
|
|
1376
|
+
/** Calls a later call's landing carries: an interrupt does not settle them. */
|
|
1377
|
+
const carriedOwners = new Set();
|
|
1378
|
+
|
|
1379
|
+
/**
|
|
1380
|
+
* A later write to `property` takes it from any landing. A write to opacity
|
|
1381
|
+
* decides what shows, so a landing still waiting writes its travel at once
|
|
1382
|
+
* rather than never, and its calls settle as interrupted.
|
|
1383
|
+
*/
|
|
1384
|
+
const cancelLanding = property => {
|
|
1385
|
+
for (const landing of [...landings]) {
|
|
1386
|
+
if (property === 'opacity') {
|
|
1387
|
+
endLanding(landing);
|
|
1388
|
+
landNow(landing);
|
|
1389
|
+
} else if (landing.values.delete(property)) {
|
|
1390
|
+
// Set or stopped before it landed: the call whose move it was did
|
|
1391
|
+
// not finish (a carried value is an earlier call's).
|
|
1392
|
+
const moved = landing.owners.get(property) ?? landing.owner;
|
|
1393
|
+
landing.owners.delete(property);
|
|
1394
|
+
if (!moved.settled) {
|
|
1395
|
+
settle(moved, false);
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
};
|
|
1400
|
+
const landNow = landing => {
|
|
1401
|
+
const changed = [];
|
|
1402
|
+
for (const [property, value] of landing.values) {
|
|
1403
|
+
segments.delete(property);
|
|
1404
|
+
resting[property] = value;
|
|
1405
|
+
changed.push(property);
|
|
1406
|
+
}
|
|
1407
|
+
if (changed.length > 0) {
|
|
1408
|
+
commit(changed, now());
|
|
1409
|
+
}
|
|
1410
|
+
for (const owner of [landing.owner, ...landing.carried]) {
|
|
1411
|
+
carriedOwners.delete(owner);
|
|
1412
|
+
if (!owner.settled) {
|
|
1413
|
+
settle(owner, false);
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
};
|
|
1417
|
+
|
|
1418
|
+
/**
|
|
1419
|
+
* A later Reduced Motion call that lands too carries the landings still
|
|
1420
|
+
* waiting: their travel lands with its own, which wins where both move a
|
|
1421
|
+
* property, and their calls settle when it lands. So two calls in one
|
|
1422
|
+
* frame (a drag cancel's position and scale) both land.
|
|
1423
|
+
*/
|
|
1424
|
+
const adoptLandings = (plan, properties) => {
|
|
1425
|
+
const carried = [];
|
|
1426
|
+
const owners = new Map();
|
|
1427
|
+
for (const landing of [...landings]) {
|
|
1428
|
+
for (const [property, value] of landing.values) {
|
|
1429
|
+
const moved = landing.owners.get(property) ?? landing.owner;
|
|
1430
|
+
if (!properties.includes(property) && !plan.values.has(property)) {
|
|
1431
|
+
plan.values.set(property, value);
|
|
1432
|
+
owners.set(property, moved);
|
|
1433
|
+
} else if (!moved.settled) {
|
|
1434
|
+
// This call takes the property over: the earlier move never lands.
|
|
1435
|
+
settle(moved, false);
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
carried.push(landing.owner, ...landing.carried);
|
|
1439
|
+
endLanding(landing);
|
|
1440
|
+
}
|
|
1441
|
+
for (const owner of carried) {
|
|
1442
|
+
carriedOwners.add(owner);
|
|
1443
|
+
}
|
|
1444
|
+
return {
|
|
1445
|
+
carried,
|
|
1446
|
+
owners
|
|
1447
|
+
};
|
|
1448
|
+
};
|
|
1449
|
+
|
|
1450
|
+
/** Where opacity is heading: past a waiting landing's fade back in. */
|
|
1451
|
+
const opacityHeading = () => {
|
|
1452
|
+
let to = finalValue('opacity');
|
|
1453
|
+
for (const landing of landings) {
|
|
1454
|
+
if (landing.fadeIn != null) {
|
|
1455
|
+
to = landing.fadeIn.to;
|
|
1456
|
+
} else if (landing.restore != null) {
|
|
1457
|
+
to = landing.restore;
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
return to;
|
|
1461
|
+
};
|
|
1462
|
+
const endLanding = landing => {
|
|
1463
|
+
if (landing.timer != null) {
|
|
1464
|
+
clearTimeout(landing.timer);
|
|
1465
|
+
}
|
|
1466
|
+
landings.delete(landing);
|
|
1467
|
+
};
|
|
1468
|
+
|
|
1469
|
+
/** Arms a landing from the call's start (the browser's, for a call from rest). */
|
|
1470
|
+
const scheduleLanding = (owner, started, plan, adopted) => {
|
|
1471
|
+
const owners = new Map();
|
|
1472
|
+
for (const property of plan.values.keys()) {
|
|
1473
|
+
owners.set(property, adopted?.owners.get(property) ?? owner);
|
|
1474
|
+
}
|
|
1475
|
+
const landing = {
|
|
1476
|
+
...plan,
|
|
1477
|
+
owner,
|
|
1478
|
+
carried: adopted?.carried ?? [],
|
|
1479
|
+
owners,
|
|
1480
|
+
timer: null,
|
|
1481
|
+
due: null
|
|
1482
|
+
};
|
|
1483
|
+
landings.add(landing);
|
|
1484
|
+
// The handle settles when the landing (and its fade back in) is done.
|
|
1485
|
+
if (owner.timer != null) {
|
|
1486
|
+
clearTimeout(owner.timer);
|
|
1487
|
+
owner.timer = null;
|
|
1488
|
+
}
|
|
1489
|
+
void started.then(start => {
|
|
1490
|
+
if (!landings.has(landing)) {
|
|
1491
|
+
return;
|
|
1492
|
+
}
|
|
1493
|
+
landing.due = start + landing.at;
|
|
1494
|
+
landing.timer = setTimeout(() => land(landing), Math.max(0, landing.due - now()));
|
|
1495
|
+
});
|
|
1496
|
+
};
|
|
1497
|
+
|
|
1498
|
+
/**
|
|
1499
|
+
* Writes the travel while the element is transparent: a plain style write,
|
|
1500
|
+
* never an animation (DR2: opacity only), so it is never shown in the wrong
|
|
1501
|
+
* place. In a dip the fade back in starts from here and takes what is left
|
|
1502
|
+
* of the cross-fade, so the dip ends when it was due even when a busy main
|
|
1503
|
+
* thread runs this timer late; past the whole fade back in (over 100 ms
|
|
1504
|
+
* late) the element appears at once where it landed.
|
|
1505
|
+
*/
|
|
1506
|
+
const land = landing => {
|
|
1507
|
+
landings.delete(landing);
|
|
1508
|
+
landing.timer = null;
|
|
1509
|
+
const time = now();
|
|
1510
|
+
const changed = [];
|
|
1511
|
+
for (const [property, value] of landing.values) {
|
|
1512
|
+
segments.delete(property);
|
|
1513
|
+
resting[property] = value;
|
|
1514
|
+
changed.push(property);
|
|
1515
|
+
}
|
|
1516
|
+
let end = time;
|
|
1517
|
+
if (landing.fadeIn != null) {
|
|
1518
|
+
// The fade back in takes what is left of the cross-fade: a timer that
|
|
1519
|
+
// a busy main thread runs late shortens it, so the dip still ends
|
|
1520
|
+
// when it was due (design:reduced-motion DR2, 200 ms or less), and the
|
|
1521
|
+
// element is never shown before it lands.
|
|
1522
|
+
const late = landing.due == null ? 0 : Math.max(0, time - landing.due);
|
|
1523
|
+
const ms = Math.max(1, landing.fadeIn.ms - late);
|
|
1524
|
+
segments.set('opacity', fadeSegment(landing.owner, time, [{
|
|
1525
|
+
at: 0,
|
|
1526
|
+
value: valueAt('opacity', time)
|
|
1527
|
+
}, {
|
|
1528
|
+
at: ms,
|
|
1529
|
+
value: landing.fadeIn.to
|
|
1530
|
+
}]));
|
|
1531
|
+
changed.push('opacity');
|
|
1532
|
+
end = time + ms;
|
|
1533
|
+
} else if (landing.restore != null) {
|
|
1534
|
+
segments.delete('opacity');
|
|
1535
|
+
resting.opacity = landing.restoreIsBorrowed === true ? IDENTITY.opacity : landing.restore;
|
|
1536
|
+
changed.push('opacity');
|
|
1537
|
+
}
|
|
1538
|
+
commit(changed, time);
|
|
1539
|
+
if (landing.restoreIsBorrowed === true) {
|
|
1540
|
+
// The stylesheet owns its opacity again.
|
|
1541
|
+
releaseOwnOpacity();
|
|
1542
|
+
}
|
|
1543
|
+
if (landing.fadeIn?.isBorrowed === true) {
|
|
1544
|
+
// The fade back in returns to the stylesheet's opacity: it owns it
|
|
1545
|
+
// again once the fade ends (releaseIdentity).
|
|
1546
|
+
borrowedOpacity = landing.fadeIn.to;
|
|
1547
|
+
}
|
|
1548
|
+
// The calls it carries are done: their travel has landed.
|
|
1549
|
+
for (const carried of landing.carried) {
|
|
1550
|
+
carriedOwners.delete(carried);
|
|
1551
|
+
if (!carried.settled) {
|
|
1552
|
+
settle(carried, true);
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
const {
|
|
1556
|
+
owner
|
|
1557
|
+
} = landing;
|
|
1558
|
+
if (owner.settled) {
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1561
|
+
if (end <= time) {
|
|
1562
|
+
settle(owner, true);
|
|
1563
|
+
} else {
|
|
1564
|
+
owner.timer = setTimeout(() => {
|
|
1565
|
+
owner.timer = null;
|
|
1566
|
+
settle(owner, true);
|
|
1567
|
+
}, Math.max(0, end - now()));
|
|
1568
|
+
}
|
|
1569
|
+
};
|
|
1570
|
+
|
|
1571
|
+
/**
|
|
1572
|
+
* A call that sets a value still waiting to land takes it over: the earlier
|
|
1573
|
+
* move never lands, and its call settles interrupted. Once nothing waiting
|
|
1574
|
+
* is left, a call that names its own opacity replaces an earlier fade back
|
|
1575
|
+
* in or restore too, so a caught dismissal fades straight back up instead
|
|
1576
|
+
* of dipping. Whatever still waits is carried by the new plan.
|
|
1577
|
+
*/
|
|
1578
|
+
const takeOverLandings = (properties, namesOpacity) => {
|
|
1579
|
+
for (const property of properties) {
|
|
1580
|
+
if (property !== 'opacity') {
|
|
1581
|
+
cancelLanding(property);
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
if (!namesOpacity) {
|
|
1585
|
+
// A dip left with nothing to land (a move taken back) fades straight
|
|
1586
|
+
// back in from where it is, rather than finishing its dip.
|
|
1587
|
+
for (const landing of [...landings]) {
|
|
1588
|
+
if (landing.values.size === 0 && landing.fadeIn != null) {
|
|
1589
|
+
endLanding(landing);
|
|
1590
|
+
land(landing);
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
return;
|
|
1594
|
+
}
|
|
1595
|
+
for (const landing of [...landings]) {
|
|
1596
|
+
// A waiting call that named its own opacity loses it to this call: it
|
|
1597
|
+
// never reaches that opacity, so it settles interrupted even though its
|
|
1598
|
+
// travel still lands.
|
|
1599
|
+
for (const owner of [landing.owner, ...landing.carried]) {
|
|
1600
|
+
if (namedOpacity.has(owner) && !owner.settled) {
|
|
1601
|
+
settle(owner, false);
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
if (landing.values.size > 0) {
|
|
1605
|
+
continue;
|
|
1606
|
+
}
|
|
1607
|
+
endLanding(landing);
|
|
1608
|
+
if (landing.fadeIn?.isBorrowed === true || landing.restoreIsBorrowed === true) {
|
|
1609
|
+
// The call's own opacity owns it now, not the stylesheet's.
|
|
1610
|
+
borrowedOpacity = null;
|
|
1611
|
+
}
|
|
1612
|
+
for (const owner of [landing.owner, ...landing.carried]) {
|
|
1613
|
+
carriedOwners.delete(owner);
|
|
1614
|
+
if (!owner.settled) {
|
|
1615
|
+
settle(owner, false);
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
};
|
|
1620
|
+
|
|
1621
|
+
/** Reduce Motion calls that named their own opacity. */
|
|
1622
|
+
const namedOpacity = new WeakSet();
|
|
1623
|
+
|
|
1624
|
+
/**
|
|
1625
|
+
* Where opacity was heading before a call took the waiting landings over:
|
|
1626
|
+
* an entrance by role that catches an exit fades back to what the exit
|
|
1627
|
+
* would have restored.
|
|
1628
|
+
*/
|
|
1629
|
+
let headingBeforeTakeOver = null;
|
|
1630
|
+
|
|
1631
|
+
/** What the Reduce Motion planner reads of this animator. */
|
|
1632
|
+
const planView = {
|
|
1633
|
+
valueAt,
|
|
1634
|
+
opacityHeading: () => headingBeforeTakeOver ?? opacityHeading(),
|
|
1635
|
+
ownOpacity: () => ownOpacity(),
|
|
1636
|
+
writesOwnOpacity: () => writesOwnOpacity(),
|
|
1637
|
+
get borrowedOpacity() {
|
|
1638
|
+
return borrowedOpacity;
|
|
1639
|
+
},
|
|
1640
|
+
get hasOpacityCurve() {
|
|
1641
|
+
return segments.has('opacity');
|
|
1642
|
+
},
|
|
1643
|
+
get restingOpacity() {
|
|
1644
|
+
return resting.opacity;
|
|
1645
|
+
},
|
|
1646
|
+
get hasPendingLanding() {
|
|
1647
|
+
return landings.size > 0;
|
|
1648
|
+
}
|
|
1649
|
+
};
|
|
1650
|
+
|
|
1651
|
+
/**
|
|
1652
|
+
* Reduced Motion (see the file header): the planner's plan
|
|
1653
|
+
* (reducedMotionPlan.ts) as this call's segments.
|
|
1654
|
+
*/
|
|
1655
|
+
const reducedMotion = (owner, time, targets, properties, fade, role) => {
|
|
1656
|
+
const heading = opacityHeading();
|
|
1657
|
+
// An entrance or an exit by role drives the opacity as a named one does.
|
|
1658
|
+
takeOverLandings(properties, targets.opacity != null || role != null);
|
|
1659
|
+
if (targets.opacity != null) {
|
|
1660
|
+
namedOpacity.add(owner);
|
|
1661
|
+
}
|
|
1662
|
+
headingBeforeTakeOver = heading;
|
|
1663
|
+
let plan;
|
|
1664
|
+
try {
|
|
1665
|
+
plan = planReducedMotion(planView, time, targets, properties, fade, role);
|
|
1666
|
+
} finally {
|
|
1667
|
+
headingBeforeTakeOver = null;
|
|
1668
|
+
}
|
|
1669
|
+
const next = {};
|
|
1670
|
+
for (const property of plan.atOnce) {
|
|
1671
|
+
next[property] = null;
|
|
1672
|
+
}
|
|
1673
|
+
if (plan.opacityFade != null) {
|
|
1674
|
+
next.opacity = fadeSegment(owner, time, plan.opacityFade);
|
|
1675
|
+
}
|
|
1676
|
+
return {
|
|
1677
|
+
next,
|
|
1678
|
+
holds: plan.holds,
|
|
1679
|
+
landing: plan.landing,
|
|
1680
|
+
borrowed: plan.borrowed
|
|
1681
|
+
};
|
|
1682
|
+
};
|
|
1683
|
+
const animator = {
|
|
1684
|
+
get element() {
|
|
1685
|
+
return target;
|
|
1686
|
+
},
|
|
1687
|
+
reducedMotion(reducedOptions = {}) {
|
|
1688
|
+
if (!prefersReducedMotion()) {
|
|
1689
|
+
return 'off';
|
|
1690
|
+
}
|
|
1691
|
+
return isCoarsePointer() || reducedOptions.touchRelease === true ? 'crossfade' : 'instant';
|
|
1692
|
+
},
|
|
1693
|
+
attach(next) {
|
|
1694
|
+
if (next === target) {
|
|
1695
|
+
return;
|
|
1696
|
+
}
|
|
1697
|
+
if (next == null) {
|
|
1698
|
+
// Unmount, unless a node replaces this one in the same task (React's
|
|
1699
|
+
// replacement order: attach(null), then the new node): nothing is
|
|
1700
|
+
// left to animate, so at the end of the task every other handle
|
|
1701
|
+
// settles as interrupted, every atRest() wait settles and no
|
|
1702
|
+
// completion timer outlives the element, as in dispose()
|
|
1703
|
+
// (architecture:react-component-runtime INV5). A replacement keeps
|
|
1704
|
+
// them all: the motion goes on on the new node.
|
|
1705
|
+
lastDetached = target;
|
|
1706
|
+
if (!unmountQueued) {
|
|
1707
|
+
unmountQueued = true;
|
|
1708
|
+
queueMicrotask(() => {
|
|
1709
|
+
if (!unmountQueued) {
|
|
1710
|
+
return;
|
|
1711
|
+
}
|
|
1712
|
+
unmountQueued = false;
|
|
1713
|
+
// A Reduce Motion move still waiting lands where it was going, as
|
|
1714
|
+
// in dispose(), on the node just detached too: a dip's fade out
|
|
1715
|
+
// left its low opacity inline there, so a later attach of that
|
|
1716
|
+
// node (a Suspense or Activity reveal) shows it landed and
|
|
1717
|
+
// visible. Its calls settle as interrupted.
|
|
1718
|
+
onLastDetached(() => {
|
|
1719
|
+
landPending(false);
|
|
1720
|
+
// The landing's commit rebuilt every channel it touched on the
|
|
1721
|
+
// detached node, any axis still moving included: leave that
|
|
1722
|
+
// node with no animation, timer or promotion.
|
|
1723
|
+
for (const animation of running.values()) {
|
|
1724
|
+
animation.cancel();
|
|
1725
|
+
}
|
|
1726
|
+
running.clear();
|
|
1727
|
+
for (const timer of retireTimers.values()) {
|
|
1728
|
+
clearTimeout(timer);
|
|
1729
|
+
}
|
|
1730
|
+
retireTimers.clear();
|
|
1731
|
+
demote();
|
|
1732
|
+
});
|
|
1733
|
+
lastDetached = null;
|
|
1734
|
+
const time = now();
|
|
1735
|
+
for (const property of PROPERTIES) {
|
|
1736
|
+
interrupt(property, time);
|
|
1737
|
+
}
|
|
1738
|
+
for (const wait of [...restWaits]) {
|
|
1739
|
+
wait.settle();
|
|
1740
|
+
}
|
|
1741
|
+
});
|
|
1742
|
+
}
|
|
1743
|
+
} else {
|
|
1744
|
+
// A node in place of the one just detached: no unmount.
|
|
1745
|
+
unmountQueued = false;
|
|
1746
|
+
lastDetached = null;
|
|
1747
|
+
}
|
|
1748
|
+
// After the landing's commit, which can promote the element again.
|
|
1749
|
+
demote();
|
|
1750
|
+
const isOpacityBorrowed = borrowedOpacity != null || [...landings].some(landing => landing.restoreIsBorrowed === true);
|
|
1751
|
+
for (const animation of running.values()) {
|
|
1752
|
+
animation.cancel();
|
|
1753
|
+
}
|
|
1754
|
+
running.clear();
|
|
1755
|
+
for (const timer of retireTimers.values()) {
|
|
1756
|
+
clearTimeout(timer);
|
|
1757
|
+
}
|
|
1758
|
+
retireTimers.clear();
|
|
1759
|
+
if (isOpacityBorrowed) {
|
|
1760
|
+
// A running borrowed dip (or an exit by role returning to the
|
|
1761
|
+
// stylesheet) stays in the model through any attach, so a pending
|
|
1762
|
+
// move still lands while the element is invisible; only the
|
|
1763
|
+
// outgoing element gets its stylesheet's opacity back.
|
|
1764
|
+
releaseOwnOpacity();
|
|
1765
|
+
}
|
|
1766
|
+
// Identities the outgoing node holds inline over a stylesheet that
|
|
1767
|
+
// differs, kept through a detach for the node that comes next.
|
|
1768
|
+
if (target != null) {
|
|
1769
|
+
const outgoing = target;
|
|
1770
|
+
heldIdentity = new Set(Object.keys(CHANNELS).filter(channel => outgoing.style.getPropertyValue(channel) === IDENTITY_TEXT[channel]));
|
|
1771
|
+
}
|
|
1772
|
+
const held = heldIdentity;
|
|
1773
|
+
target = next;
|
|
1774
|
+
if (target == null) {
|
|
1775
|
+
return;
|
|
1776
|
+
}
|
|
1777
|
+
heldIdentity = new Set();
|
|
1778
|
+
const time = now();
|
|
1779
|
+
// A borrowed dip that ended while detached hands the opacity back
|
|
1780
|
+
// instead of writing the borrowed value inline.
|
|
1781
|
+
const dip = segments.get('opacity');
|
|
1782
|
+
if (borrowedOpacity != null && (dip == null || time >= dip.start + dip.duration)) {
|
|
1783
|
+
borrowedOpacity = null;
|
|
1784
|
+
segments.delete('opacity');
|
|
1785
|
+
resting.opacity = IDENTITY.opacity;
|
|
1786
|
+
}
|
|
1787
|
+
for (const channel of Object.keys(CHANNELS)) {
|
|
1788
|
+
const properties = CHANNELS[channel];
|
|
1789
|
+
const isDefault = properties.every(p => !segments.has(p) && resting[p] === IDENTITY[p]);
|
|
1790
|
+
if (!isDefault) {
|
|
1791
|
+
rebuild(channel, time);
|
|
1792
|
+
} else if (held.has(channel)) {
|
|
1793
|
+
// An identity the animator held inline over a stylesheet that
|
|
1794
|
+
// differs goes onto the new node too (writeInline keeps it only
|
|
1795
|
+
// where the new node's stylesheet still differs).
|
|
1796
|
+
writeInline(channel, property => resting[property]);
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
flushReleases();
|
|
1800
|
+
},
|
|
1801
|
+
get(property, time = now()) {
|
|
1802
|
+
syncPending();
|
|
1803
|
+
return valueAt(property === 'scale' ? 'scaleX' : property, time);
|
|
1804
|
+
},
|
|
1805
|
+
getVelocity(property, time = now()) {
|
|
1806
|
+
syncPending();
|
|
1807
|
+
return velocityAt(property === 'scale' ? 'scaleX' : property, time);
|
|
1808
|
+
},
|
|
1809
|
+
set(values) {
|
|
1810
|
+
syncPending();
|
|
1811
|
+
const time = now();
|
|
1812
|
+
const next = expand(values);
|
|
1813
|
+
for (const [property, value] of Object.entries(next)) {
|
|
1814
|
+
interrupt(property, time);
|
|
1815
|
+
segments.delete(property);
|
|
1816
|
+
resting[property] = value;
|
|
1817
|
+
}
|
|
1818
|
+
// Direct manipulation: the layer makes each write a composite only.
|
|
1819
|
+
for (const property of Object.keys(next)) {
|
|
1820
|
+
promote(CHANNEL_OF[property]);
|
|
1821
|
+
}
|
|
1822
|
+
commit(Object.keys(next), time);
|
|
1823
|
+
},
|
|
1824
|
+
animate(to, transition, animateOptions = {}) {
|
|
1825
|
+
syncPending();
|
|
1826
|
+
const time = animateOptions.at ?? now();
|
|
1827
|
+
const targets = expand(to);
|
|
1828
|
+
const velocities = expand(transition.velocity ?? {});
|
|
1829
|
+
const {
|
|
1830
|
+
owner,
|
|
1831
|
+
finished
|
|
1832
|
+
} = newOwner();
|
|
1833
|
+
lastSpring.set(owner, transition.spring);
|
|
1834
|
+
const properties = Object.keys(targets);
|
|
1835
|
+
const next = {};
|
|
1836
|
+
let plan = null;
|
|
1837
|
+
if (prefersReducedMotion()) {
|
|
1838
|
+
plan = reducedMotion(owner, time, targets, properties, isCoarsePointer() || animateOptions.touchRelease === true, animateOptions.crossfadeAs);
|
|
1839
|
+
Object.assign(next, plan.next);
|
|
1840
|
+
} else {
|
|
1841
|
+
for (const property of properties) {
|
|
1842
|
+
const velocity = velocities[property] ?? velocityAt(property, time);
|
|
1843
|
+
next[property] = springSegment(owner, property, time, targets[property], transition.spring, velocity);
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
const group = beginGroup(time, Object.keys(next), animateOptions.at);
|
|
1847
|
+
const adopted = plan?.landing != null ? adoptLandings(plan.landing, Object.keys(next)) : null;
|
|
1848
|
+
const changed = [];
|
|
1849
|
+
for (const [property, segment] of Object.entries(next)) {
|
|
1850
|
+
interrupt(property, time);
|
|
1851
|
+
changed.push(property);
|
|
1852
|
+
if (segment == null) {
|
|
1853
|
+
// Read before the segment goes: valueAt falls back to resting.
|
|
1854
|
+
const value = plan?.holds[property] ?? targets[property] ?? valueAt(property, time);
|
|
1855
|
+
segments.delete(property);
|
|
1856
|
+
resting[property] = value;
|
|
1857
|
+
} else {
|
|
1858
|
+
segments.set(property, segment);
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
commitCall(group, owner, changed, time);
|
|
1862
|
+
if (plan?.borrowed != null) {
|
|
1863
|
+
// The dip's own commit cleared it; the borrow lasts the dip.
|
|
1864
|
+
borrowedOpacity = plan.borrowed;
|
|
1865
|
+
}
|
|
1866
|
+
const handle = makeHandle(owner, changed, finished, time, group);
|
|
1867
|
+
if (plan?.landing != null) {
|
|
1868
|
+
scheduleLanding(owner, handle.started, plan.landing, adopted);
|
|
1869
|
+
}
|
|
1870
|
+
return handle;
|
|
1871
|
+
},
|
|
1872
|
+
play(curves, playOptions = {}) {
|
|
1873
|
+
syncPending();
|
|
1874
|
+
const time = playOptions.at ?? now();
|
|
1875
|
+
const {
|
|
1876
|
+
owner,
|
|
1877
|
+
finished
|
|
1878
|
+
} = newOwner();
|
|
1879
|
+
const byProperty = {};
|
|
1880
|
+
// `scale` first, so an explicit `scaleX` or `scaleY` beside it wins.
|
|
1881
|
+
const entries = Object.entries(curves).sort(([a], [b]) => Number(b === 'scale') - Number(a === 'scale'));
|
|
1882
|
+
for (const [key, curve] of entries) {
|
|
1883
|
+
if (curve == null) {
|
|
1884
|
+
continue;
|
|
1885
|
+
}
|
|
1886
|
+
if (!Number.isFinite(curve.duration) || curve.duration < 0) {
|
|
1887
|
+
throw new RangeError(`animator: a curve needs a finite duration ≥ 0 (got ${curve.duration})`);
|
|
1888
|
+
}
|
|
1889
|
+
for (const property of expandList([key])) {
|
|
1890
|
+
byProperty[property] = curve;
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
const properties = Object.keys(byProperty);
|
|
1894
|
+
const targets = {};
|
|
1895
|
+
for (const property of properties) {
|
|
1896
|
+
const curve = byProperty[property];
|
|
1897
|
+
targets[property] = curve.at(curve.duration);
|
|
1898
|
+
}
|
|
1899
|
+
const plan = prefersReducedMotion() ? reducedMotion(owner, time, targets, properties, isCoarsePointer() || playOptions.touchRelease === true, playOptions.crossfadeAs) : null;
|
|
1900
|
+
const next = {
|
|
1901
|
+
...plan?.next
|
|
1902
|
+
};
|
|
1903
|
+
for (const property of properties) {
|
|
1904
|
+
if (property in next) {
|
|
1905
|
+
continue;
|
|
1906
|
+
}
|
|
1907
|
+
const curve = byProperty[property];
|
|
1908
|
+
next[property] = curve.duration === 0 ? null : {
|
|
1909
|
+
start: time,
|
|
1910
|
+
duration: Math.min(MAX_DURATION_MS, curve.duration * 1000),
|
|
1911
|
+
target: targets[property],
|
|
1912
|
+
at: t => curve.at(t / 1000),
|
|
1913
|
+
speed: t => curve.velocity(t / 1000),
|
|
1914
|
+
jumps: [],
|
|
1915
|
+
owner
|
|
1916
|
+
};
|
|
1917
|
+
}
|
|
1918
|
+
const group = beginGroup(time, Object.keys(next), playOptions.at);
|
|
1919
|
+
const adopted = plan?.landing != null ? adoptLandings(plan.landing, Object.keys(next)) : null;
|
|
1920
|
+
const changed = [];
|
|
1921
|
+
for (const [property, segment] of Object.entries(next)) {
|
|
1922
|
+
interrupt(property, time);
|
|
1923
|
+
changed.push(property);
|
|
1924
|
+
if (segment == null) {
|
|
1925
|
+
// Read before the segment goes: valueAt falls back to resting.
|
|
1926
|
+
const value = plan?.holds[property] ?? targets[property] ?? valueAt(property, time);
|
|
1927
|
+
segments.delete(property);
|
|
1928
|
+
resting[property] = value;
|
|
1929
|
+
} else {
|
|
1930
|
+
segments.set(property, segment);
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
commitCall(group, owner, changed, time);
|
|
1934
|
+
if (plan?.borrowed != null) {
|
|
1935
|
+
// The dip's own commit cleared it; the borrow lasts the dip.
|
|
1936
|
+
borrowedOpacity = plan.borrowed;
|
|
1937
|
+
}
|
|
1938
|
+
const handle = makeHandle(owner, changed, finished, time, group);
|
|
1939
|
+
if (plan?.landing != null) {
|
|
1940
|
+
scheduleLanding(owner, handle.started, plan.landing, adopted);
|
|
1941
|
+
}
|
|
1942
|
+
return handle;
|
|
1943
|
+
},
|
|
1944
|
+
decay(curves, decayOptions = {}) {
|
|
1945
|
+
syncPending();
|
|
1946
|
+
const time = decayOptions.at ?? now();
|
|
1947
|
+
// Under Reduce Motion only momentum keeps moving (DR2, DEC-2): a curve
|
|
1948
|
+
// that is a transition plays as `animate` does, a cross-fade or at once.
|
|
1949
|
+
const reduce = prefersReducedMotion();
|
|
1950
|
+
const transitions = {};
|
|
1951
|
+
for (const property of DECAY_PROPERTIES) {
|
|
1952
|
+
const curve = curves[property];
|
|
1953
|
+
if (reduce && curve?.isTransition === true) {
|
|
1954
|
+
transitions[property] = curve.at(curve.duration);
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
const transitioned = Object.keys(transitions).length > 0 ? animator.animate(transitions, REDUCED_TRANSITION, {
|
|
1958
|
+
at: time,
|
|
1959
|
+
touchRelease: decayOptions.touchRelease
|
|
1960
|
+
}) : null;
|
|
1961
|
+
const {
|
|
1962
|
+
owner,
|
|
1963
|
+
finished
|
|
1964
|
+
} = newOwner();
|
|
1965
|
+
// Momentum moves the translate axes only (DR2's throw exception).
|
|
1966
|
+
const properties = DECAY_PROPERTIES.filter(property => curves[property] != null && transitions[property] === undefined);
|
|
1967
|
+
if (transitioned != null && properties.length === 0) {
|
|
1968
|
+
return transitioned;
|
|
1969
|
+
}
|
|
1970
|
+
const group = beginGroup(time, properties, decayOptions.at);
|
|
1971
|
+
for (const property of properties) {
|
|
1972
|
+
const curve = curves[property];
|
|
1973
|
+
interrupt(property, time);
|
|
1974
|
+
if (!(curve.duration > 0)) {
|
|
1975
|
+
segments.delete(property);
|
|
1976
|
+
resting[property] = curve.at(0);
|
|
1977
|
+
continue;
|
|
1978
|
+
}
|
|
1979
|
+
segments.set(property, {
|
|
1980
|
+
start: time,
|
|
1981
|
+
duration: Math.min(MAX_DURATION_MS, curve.duration * 1000),
|
|
1982
|
+
target: curve.at(curve.duration),
|
|
1983
|
+
at: t => curve.at(t / 1000),
|
|
1984
|
+
speed: t => curve.velocity(t / 1000),
|
|
1985
|
+
jumps: [],
|
|
1986
|
+
owner
|
|
1987
|
+
});
|
|
1988
|
+
}
|
|
1989
|
+
commitCall(group, owner, properties, time);
|
|
1990
|
+
const handle = makeHandle(owner, properties, finished, time, group);
|
|
1991
|
+
if (transitioned == null) {
|
|
1992
|
+
return handle;
|
|
1993
|
+
}
|
|
1994
|
+
// One handle for the whole release: the cross-faded axes too.
|
|
1995
|
+
const faded = transitioned;
|
|
1996
|
+
const owns = property => faded.properties.includes(property);
|
|
1997
|
+
return {
|
|
1998
|
+
...handle,
|
|
1999
|
+
properties: [...handle.properties, ...faded.properties],
|
|
2000
|
+
started: Promise.all([handle.started, faded.started]).then(([coasting]) => coasting),
|
|
2001
|
+
finished: Promise.all([handle.finished, faded.finished]).then(([coasted, done]) => coasted && done),
|
|
2002
|
+
value: (property, at) => owns(property) ? faded.value(property, at) : handle.value(property, at),
|
|
2003
|
+
velocity: (property, at) => owns(property) ? faded.velocity(property, at) : handle.velocity(property, at),
|
|
2004
|
+
stop: () => {
|
|
2005
|
+
handle.stop();
|
|
2006
|
+
faded.stop();
|
|
2007
|
+
}
|
|
2008
|
+
};
|
|
2009
|
+
},
|
|
2010
|
+
stop(properties) {
|
|
2011
|
+
syncPending();
|
|
2012
|
+
const time = now();
|
|
2013
|
+
const list = properties == null ? PROPERTIES : expandList(properties);
|
|
2014
|
+
const changed = [];
|
|
2015
|
+
// A borrowed dip, or an exit by role that returns to the stylesheet.
|
|
2016
|
+
const wasBorrowed = borrowedOpacity != null || [...landings].some(landing => landing.restoreIsBorrowed === true);
|
|
2017
|
+
// Read before the landings go: a dip's fade back in, an exit's restore.
|
|
2018
|
+
const heading = opacityHeading();
|
|
2019
|
+
for (const property of list) {
|
|
2020
|
+
cancelLanding(property);
|
|
2021
|
+
const segment = segments.get(property);
|
|
2022
|
+
if (segment == null) {
|
|
2023
|
+
continue;
|
|
2024
|
+
}
|
|
2025
|
+
// A Reduce Motion fade is never left half done: it finishes, and a
|
|
2026
|
+
// dip on the stylesheet's opacity hands that opacity back.
|
|
2027
|
+
const value = !segment.isFade ? valueAt(property, time) : wasBorrowed ? IDENTITY.opacity : heading;
|
|
2028
|
+
interrupt(property, time);
|
|
2029
|
+
segments.delete(property);
|
|
2030
|
+
resting[property] = value;
|
|
2031
|
+
changed.push(property);
|
|
2032
|
+
}
|
|
2033
|
+
commit(changed, time);
|
|
2034
|
+
if (wasBorrowed && changed.includes('opacity')) {
|
|
2035
|
+
releaseOwnOpacity();
|
|
2036
|
+
}
|
|
2037
|
+
},
|
|
2038
|
+
rebase(deltas, rebaseOptions = {}) {
|
|
2039
|
+
syncPending();
|
|
2040
|
+
const time = rebaseOptions.at ?? now();
|
|
2041
|
+
const changed = [];
|
|
2042
|
+
const owners = new Set();
|
|
2043
|
+
for (const [property, delta] of Object.entries(expand(deltas))) {
|
|
2044
|
+
if (delta === 0) {
|
|
2045
|
+
continue;
|
|
2046
|
+
}
|
|
2047
|
+
changed.push(property);
|
|
2048
|
+
const segment = segmentAt(property, time);
|
|
2049
|
+
if (segment == null) {
|
|
2050
|
+
resting[property] = valueAt(property, time) + delta;
|
|
2051
|
+
segments.delete(property);
|
|
2052
|
+
continue;
|
|
2053
|
+
}
|
|
2054
|
+
owners.add(segment.owner);
|
|
2055
|
+
// A curve still waiting for the browser's start stays in its call:
|
|
2056
|
+
// it starts with that call's commit.
|
|
2057
|
+
const group = segment.group != null && !segment.group.resolved ? segment.group : undefined;
|
|
2058
|
+
const replace = next => {
|
|
2059
|
+
if (group != null) {
|
|
2060
|
+
const index = group.segments.indexOf(segment);
|
|
2061
|
+
if (index >= 0) {
|
|
2062
|
+
group.segments[index] = next;
|
|
2063
|
+
} else {
|
|
2064
|
+
group.segments.push(next);
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
segments.set(property, next);
|
|
2068
|
+
};
|
|
2069
|
+
if (segment.spring != null) {
|
|
2070
|
+
const from = valueAt(property, time) + delta;
|
|
2071
|
+
const velocity = velocityAt(property, time);
|
|
2072
|
+
const curve = solveSpring(toSpring(segment.spring), {
|
|
2073
|
+
from,
|
|
2074
|
+
to: segment.target,
|
|
2075
|
+
velocity
|
|
2076
|
+
});
|
|
2077
|
+
replace({
|
|
2078
|
+
start: group?.provisional ?? time,
|
|
2079
|
+
...(group != null ? {
|
|
2080
|
+
group
|
|
2081
|
+
} : {}),
|
|
2082
|
+
duration: Math.min(MAX_DURATION_MS, curve.settleTime(SETTLE_THRESHOLD[property]) * 1000),
|
|
2083
|
+
target: segment.target,
|
|
2084
|
+
at: t => curve.value(t / 1000),
|
|
2085
|
+
speed: t => curve.velocity(t / 1000),
|
|
2086
|
+
jumps: [],
|
|
2087
|
+
owner: segment.owner,
|
|
2088
|
+
spring: segment.spring
|
|
2089
|
+
});
|
|
2090
|
+
} else {
|
|
2091
|
+
replace({
|
|
2092
|
+
...segment,
|
|
2093
|
+
target: segment.target + delta,
|
|
2094
|
+
at: t => segment.at(t) + delta
|
|
2095
|
+
});
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
commit(changed, time);
|
|
2099
|
+
// The handles that own the rebased curves finish at their new ends.
|
|
2100
|
+
for (const owner of owners) {
|
|
2101
|
+
if (owner.settled || owner.timer == null) {
|
|
2102
|
+
continue;
|
|
2103
|
+
}
|
|
2104
|
+
clearTimeout(owner.timer);
|
|
2105
|
+
let end = time;
|
|
2106
|
+
for (const segment of segments.values()) {
|
|
2107
|
+
if (segment.owner === owner) {
|
|
2108
|
+
end = Math.max(end, segment.start + segment.duration);
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
owner.timer = setTimeout(() => {
|
|
2112
|
+
owner.timer = null;
|
|
2113
|
+
settle(owner, true);
|
|
2114
|
+
}, Math.max(0, end - now()));
|
|
2115
|
+
}
|
|
2116
|
+
},
|
|
2117
|
+
async atRest(properties) {
|
|
2118
|
+
const list = properties == null ? PROPERTIES : expandList(properties);
|
|
2119
|
+
return new Promise(resolve => {
|
|
2120
|
+
const wait = {
|
|
2121
|
+
timer: null,
|
|
2122
|
+
settle: () => {}
|
|
2123
|
+
};
|
|
2124
|
+
const done = () => {
|
|
2125
|
+
if (wait.timer != null) {
|
|
2126
|
+
clearTimeout(wait.timer);
|
|
2127
|
+
}
|
|
2128
|
+
restWaits.delete(wait);
|
|
2129
|
+
resolve();
|
|
2130
|
+
};
|
|
2131
|
+
wait.settle = done;
|
|
2132
|
+
restWaits.add(wait);
|
|
2133
|
+
const check = () => {
|
|
2134
|
+
wait.timer = null;
|
|
2135
|
+
syncPending();
|
|
2136
|
+
const time = now();
|
|
2137
|
+
let end = time;
|
|
2138
|
+
for (const property of list) {
|
|
2139
|
+
const segment = segments.get(property);
|
|
2140
|
+
if (segment != null) {
|
|
2141
|
+
end = Math.max(end, segment.start + segment.duration);
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
if (landings.size > 0) {
|
|
2145
|
+
// A Reduced Motion landing is still to come.
|
|
2146
|
+
wait.timer = setTimeout(check, 16);
|
|
2147
|
+
} else if (end <= time) {
|
|
2148
|
+
done();
|
|
2149
|
+
} else {
|
|
2150
|
+
// A retarget or a later start moves the end: look again then.
|
|
2151
|
+
wait.timer = setTimeout(check, end - time);
|
|
2152
|
+
}
|
|
2153
|
+
};
|
|
2154
|
+
check();
|
|
2155
|
+
});
|
|
2156
|
+
},
|
|
2157
|
+
dispose() {
|
|
2158
|
+
// A Reduce Motion move still waiting to land is written at its end
|
|
2159
|
+
// state, so the element is left visible where it was going (the node
|
|
2160
|
+
// just detached, when an unmount is pending); its call and the calls
|
|
2161
|
+
// it carries settle as interrupted.
|
|
2162
|
+
if (target == null && unmountQueued) {
|
|
2163
|
+
target = lastDetached;
|
|
2164
|
+
}
|
|
2165
|
+
lastDetached = null;
|
|
2166
|
+
unmountQueued = false;
|
|
2167
|
+
landPending(false);
|
|
2168
|
+
// Nothing moves any more: every atRest() wait settles.
|
|
2169
|
+
for (const wait of [...restWaits]) {
|
|
2170
|
+
wait.settle();
|
|
2171
|
+
}
|
|
2172
|
+
const time = now();
|
|
2173
|
+
for (const property of PROPERTIES) {
|
|
2174
|
+
interrupt(property, time);
|
|
2175
|
+
}
|
|
2176
|
+
for (const animation of running.values()) {
|
|
2177
|
+
animation.cancel();
|
|
2178
|
+
}
|
|
2179
|
+
running.clear();
|
|
2180
|
+
for (const timer of retireTimers.values()) {
|
|
2181
|
+
clearTimeout(timer);
|
|
2182
|
+
}
|
|
2183
|
+
retireTimers.clear();
|
|
2184
|
+
handBackBorrow();
|
|
2185
|
+
demote();
|
|
2186
|
+
target = null;
|
|
2187
|
+
}
|
|
2188
|
+
};
|
|
2189
|
+
animator.attach(element);
|
|
2190
|
+
return animator;
|
|
2191
|
+
}
|
|
2192
|
+
function expandList(properties) {
|
|
2193
|
+
const out = new Set();
|
|
2194
|
+
for (const property of properties) {
|
|
2195
|
+
if (property === 'scale') {
|
|
2196
|
+
out.add('scaleX');
|
|
2197
|
+
out.add('scaleY');
|
|
2198
|
+
} else {
|
|
2199
|
+
out.add(property);
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
return [...out];
|
|
2203
|
+
}
|