@astryxdesign/core 0.7.5 → 0.7.6-canary.0d71e8d
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AlertDialog/AlertDialog.d.ts +1 -1
- package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
- package/dist/AlertDialog/AlertDialog.js +5 -5
- package/dist/AppShell/AppShell.d.ts.map +1 -1
- package/dist/AppShell/AppShell.js +183 -5
- package/dist/AppShell/observeMainOverflow.d.ts +9 -0
- package/dist/AppShell/observeMainOverflow.d.ts.map +1 -0
- package/dist/AppShell/observeMainOverflow.js +125 -0
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +12 -3
- package/dist/Badge/Badge.d.ts.map +1 -1
- package/dist/Badge/Badge.js +7 -1
- package/dist/BottomSheet/BottomSheet.js +1 -1
- package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetPanel.js +2 -1
- package/dist/BottomSheet/useSheetGestures.d.ts +12 -0
- package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
- package/dist/BottomSheet/useSheetGestures.js +30 -3
- package/dist/Button/Button.js +4 -4
- package/dist/Carousel/Carousel.d.ts +3 -1
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +12 -3
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +6 -5
- package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
- package/dist/ClickableCard/ClickableCard.js +37 -4
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +41 -4
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +12 -4
- package/dist/DateInput/MonthScroller.d.ts.map +1 -1
- package/dist/DateInput/MonthScroller.js +101 -15
- package/dist/DateInput/TouchDateField.d.ts.map +1 -1
- package/dist/DateInput/TouchDateField.js +8 -3
- package/dist/DateInput/Wheel.d.ts.map +1 -1
- package/dist/DateInput/Wheel.js +11 -7
- package/dist/DateInput/usePointerDragScroll.d.ts +10 -1
- package/dist/DateInput/usePointerDragScroll.d.ts.map +1 -1
- package/dist/DateInput/usePointerDragScroll.js +25 -9
- package/dist/DateInput/useScrollSettle.d.ts +3 -1
- package/dist/DateInput/useScrollSettle.d.ts.map +1 -1
- package/dist/DateInput/useScrollSettle.js +37 -8
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +1 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts +5 -2
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +23 -12
- package/dist/DateTimeInput/NativeTimeSegment.d.ts.map +1 -1
- package/dist/DateTimeInput/NativeTimeSegment.js +5 -4
- package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
- package/dist/DateTimeInput/TouchDateTimeField.js +4 -3
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +34 -9
- package/dist/Dialog/DialogHeader.d.ts.map +1 -1
- package/dist/Dialog/DialogHeader.js +5 -4
- package/dist/Divider/Divider.d.ts.map +1 -1
- package/dist/Divider/Divider.js +2 -1
- package/dist/Drawer/Drawer.d.ts +1 -0
- package/dist/Drawer/Drawer.d.ts.map +1 -1
- package/dist/Drawer/Drawer.js +7 -2
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +35 -22
- package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts.map +1 -1
- package/dist/DropdownMenu/MenuBottomSheetActionList.js +6 -5
- package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts +20 -2
- package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts.map +1 -1
- package/dist/DropdownMenu/lazyMenuBottomSheet.js +31 -3
- package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
- package/dist/DropdownMenu/renderDropdownItems.js +21 -16
- package/dist/EmptyState/EmptyState.d.ts.map +1 -1
- package/dist/EmptyState/EmptyState.js +2 -1
- package/dist/Field/FieldLabel.d.ts +1 -1
- package/dist/Field/FieldLabel.d.ts.map +1 -1
- package/dist/Field/FieldLabel.js +4 -4
- package/dist/Field/InputClearButton.js +3 -3
- package/dist/Field/PanelSearchInput.js +2 -2
- package/dist/FieldStatus/FieldStatus.d.ts +3 -0
- package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
- package/dist/FieldStatus/FieldStatus.js +3 -2
- package/dist/FileInput/FileInput.d.ts +3 -0
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +34 -13
- package/dist/Heading/Heading.js +1 -1
- package/dist/Icon/Icon.js +1 -1
- package/dist/Indicator/CheckIndicator.d.ts.map +1 -1
- package/dist/Indicator/CheckIndicator.js +2 -1
- package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
- package/dist/Indicator/CheckboxIndicator.js +3 -1
- package/dist/Indicator/RadioIndicator.d.ts.map +1 -1
- package/dist/Indicator/RadioIndicator.js +3 -1
- package/dist/Item/Item.d.ts.map +1 -1
- package/dist/Item/Item.js +21 -7
- package/dist/Item/ItemSwipeLayer.js +1 -1
- package/dist/Layer/popoverOperations.d.ts +27 -0
- package/dist/Layer/popoverOperations.d.ts.map +1 -0
- package/dist/Layer/popoverOperations.js +159 -0
- package/dist/Layer/useLayer.d.ts +1 -1
- package/dist/Layer/useLayer.d.ts.map +1 -1
- package/dist/Layer/useLayer.js +120 -65
- package/dist/Layout/Layout.js +1 -1
- package/dist/Layout/LayoutContent.js +1 -1
- package/dist/Layout/LayoutFooter.js +1 -1
- package/dist/Layout/LayoutHeader.js +1 -1
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +5 -0
- package/dist/Lightbox/LightboxViewport.a11y.browser.spec.js +5 -4
- package/dist/Link/Link.d.ts.map +1 -1
- package/dist/Link/Link.js +8 -3
- package/dist/List/List.js +1 -1
- package/dist/List/ListItem.js +3 -3
- package/dist/Markdown/plugins/headingLinks.a11y.chromium.spec.js +1 -1
- package/dist/MobileNav/MobileNav.d.ts.map +1 -1
- package/dist/MobileNav/MobileNav.js +7 -2
- package/dist/MultiSelector/CreateRow.a11y.chromium.spec.js +7 -6
- package/dist/MultiSelector/MultiSelector.d.ts +1 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +26 -10
- package/dist/NavMenu/NavHeadingMenuItem.d.ts.map +1 -1
- package/dist/NavMenu/NavHeadingMenuItem.js +5 -29
- package/dist/NumberInput/NumberInput.d.ts +3 -1
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +26 -5
- package/dist/NumberInput/numberParser.d.ts +3 -0
- package/dist/NumberInput/numberParser.d.ts.map +1 -1
- package/dist/NumberInput/numberParser.js +7 -44
- package/dist/Outline/Outline.d.ts.map +1 -1
- package/dist/Outline/Outline.js +27 -0
- package/dist/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +32 -12
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +10 -3
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +37 -30
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +16 -12
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +11 -3
- package/dist/Resizable/useResizable.d.ts.map +1 -1
- package/dist/Resizable/useResizable.js +28 -2
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +4 -1
- package/dist/Selector/useSelectorPresentation.d.ts +8 -1
- package/dist/Selector/useSelectorPresentation.d.ts.map +1 -1
- package/dist/Selector/useSelectorPresentation.js +20 -4
- package/dist/SideNav/SideNavItem.d.ts +2 -2
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +18 -7
- package/dist/Skeleton/Skeleton.d.ts.map +1 -1
- package/dist/Skeleton/Skeleton.js +1 -1
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +10 -2
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +3 -3
- package/dist/Stack/Stack.js +1 -1
- package/dist/Stack/StackItem.js +1 -1
- package/dist/Stepper/Step.d.ts.map +1 -1
- package/dist/Stepper/Step.js +26 -6
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +10 -3
- package/dist/TabList/TabList.d.ts.map +1 -1
- package/dist/TabList/TabList.js +16 -1
- package/dist/Table/Table.d.ts.map +1 -1
- package/dist/Table/Table.js +36 -10
- package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
- package/dist/Table/plugins/columnResize/useTableColumnResize.js +9 -1
- package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortable.js +46 -58
- package/dist/Text/Text.js +1 -1
- package/dist/Text/useTruncation.d.ts.map +1 -1
- package/dist/Text/useTruncation.js +23 -5
- package/dist/TextArea/TextArea.d.ts +1 -1
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +13 -2
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +3 -3
- package/dist/TimeInput/TimeInput.d.ts +6 -2
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +21 -14
- package/dist/TimeInput/TouchTimeField.d.ts.map +1 -1
- package/dist/TimeInput/TouchTimeField.js +5 -4
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +2 -2
- package/dist/Timestamp/TimestampHoverCard.js +4 -4
- package/dist/Toast/Toast.d.ts.map +1 -1
- package/dist/Toast/Toast.js +60 -20
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +101 -54
- package/dist/ToggleButton/ToggleButtonGroup.d.ts +7 -1
- package/dist/ToggleButton/ToggleButtonGroup.d.ts.map +1 -1
- package/dist/ToggleButton/ToggleButtonGroup.js +16 -2
- package/dist/Token/Token.d.ts +1 -1
- package/dist/Token/Token.d.ts.map +1 -1
- package/dist/Token/Token.js +52 -5
- package/dist/Token/TokenLink.d.ts.map +1 -1
- package/dist/Token/TokenLink.js +14 -2
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +44 -7
- package/dist/Tokenizer/TokenizerBottomSheet.js +3 -3
- package/dist/Tokenizer/TokenizerClearAllMessageContext.d.ts +2 -0
- package/dist/Tokenizer/TokenizerClearAllMessageContext.d.ts.map +1 -0
- package/dist/Tokenizer/TokenizerClearAllMessageContext.js +17 -0
- package/dist/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/Toolbar/Toolbar.js +2 -1
- package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenu.js +3 -0
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +1 -0
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +27 -4
- package/dist/Typeahead/BaseTypeahead.d.ts +4 -4
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +43 -7
- package/dist/Typeahead/Typeahead.d.ts +1 -1
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +43 -9
- package/dist/astryx.css +25 -4
- package/dist/gestures/GestureArena.d.ts +32 -1
- package/dist/gestures/GestureArena.d.ts.map +1 -1
- package/dist/gestures/GestureArena.js +110 -9
- package/dist/gestures/GestureHost.d.ts +351 -0
- package/dist/gestures/GestureHost.d.ts.map +1 -0
- package/dist/gestures/GestureHost.js +1773 -0
- package/dist/gestures/GestureRecognizer.d.ts +13 -1
- package/dist/gestures/GestureRecognizer.d.ts.map +1 -1
- package/dist/gestures/GestureRecognizer.js +19 -1
- package/dist/gestures/LongPressGestureRecognizer.d.ts +1 -0
- package/dist/gestures/LongPressGestureRecognizer.d.ts.map +1 -1
- package/dist/gestures/LongPressGestureRecognizer.js +3 -0
- package/dist/gestures/PanGestureRecognizer.d.ts +1 -0
- package/dist/gestures/PanGestureRecognizer.d.ts.map +1 -1
- package/dist/gestures/PanGestureRecognizer.js +3 -0
- package/dist/gestures/PinchGestureRecognizer.d.ts +31 -0
- package/dist/gestures/PinchGestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/PinchGestureRecognizer.js +128 -0
- package/dist/gestures/PressTracker.d.ts +112 -0
- package/dist/gestures/PressTracker.d.ts.map +1 -0
- package/dist/gestures/PressTracker.js +267 -0
- package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts +71 -0
- package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/ScreenEdgePanGestureRecognizer.js +223 -0
- package/dist/gestures/SwipeGestureRecognizer.d.ts +37 -0
- package/dist/gestures/SwipeGestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/SwipeGestureRecognizer.js +148 -0
- package/dist/gestures/TapGestureRecognizer.d.ts +1 -0
- package/dist/gestures/TapGestureRecognizer.d.ts.map +1 -1
- package/dist/gestures/TapGestureRecognizer.js +3 -0
- package/dist/gestures/VelocityTracker.d.ts +6 -2
- package/dist/gestures/VelocityTracker.d.ts.map +1 -1
- package/dist/gestures/VelocityTracker.js +7 -3
- package/dist/gestures/haptics.d.ts +65 -0
- package/dist/gestures/haptics.d.ts.map +1 -0
- package/dist/gestures/haptics.js +182 -0
- package/dist/gestures/index.d.ts +37 -0
- package/dist/gestures/index.d.ts.map +1 -0
- package/dist/gestures/index.js +25 -0
- package/dist/gestures/nativeScroll.d.ts +42 -0
- package/dist/gestures/nativeScroll.d.ts.map +1 -0
- package/dist/gestures/nativeScroll.js +228 -0
- package/dist/gestures/touchAction.d.ts +60 -0
- package/dist/gestures/touchAction.d.ts.map +1 -0
- package/dist/gestures/touchAction.js +157 -0
- package/dist/gestures/touchActionRule.d.ts +34 -0
- package/dist/gestures/touchActionRule.d.ts.map +1 -0
- package/dist/gestures/touchActionRule.js +140 -0
- package/dist/gestures/types.d.ts +18 -7
- package/dist/gestures/types.d.ts.map +1 -1
- package/dist/gestures/types.js +22 -2
- package/dist/gestures/useGestureRecognizer.d.ts +56 -0
- package/dist/gestures/useGestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/useGestureRecognizer.js +226 -0
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/modalAnnounce.d.ts.map +1 -1
- package/dist/hooks/modalAnnounce.js +107 -61
- package/dist/hooks/scrollKeyboardDelegation.d.ts.map +1 -1
- package/dist/hooks/scrollKeyboardDelegation.js +7 -3
- package/dist/hooks/useAdaptivePresentation.d.ts.map +1 -1
- package/dist/hooks/useAdaptivePresentation.js +4 -1
- package/dist/hooks/useAnnounce.d.ts +101 -42
- package/dist/hooks/useAnnounce.d.ts.map +1 -1
- package/dist/hooks/useAnnounce.js +161 -75
- package/dist/hooks/useAnnounceRenderedText.d.ts +11 -3
- package/dist/hooks/useAnnounceRenderedText.d.ts.map +1 -1
- package/dist/hooks/useAnnounceRenderedText.js +5 -3
- package/dist/hooks/useBusyAnnouncement.d.ts.map +1 -1
- package/dist/hooks/useBusyAnnouncement.js +6 -12
- package/dist/hooks/useMenuHover.d.ts.map +1 -1
- package/dist/hooks/useMenuHover.js +12 -1
- package/dist/hooks/useScrollOverflow.d.ts +2 -1
- package/dist/hooks/useScrollOverflow.d.ts.map +1 -1
- package/dist/hooks/useScrollOverflow.js +56 -10
- package/dist/i18n/generated-locales/af-ZA.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/af-ZA.generated.js +0 -5
- package/dist/i18n/generated-locales/ar-SA.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/ar-SA.generated.js +0 -5
- package/dist/i18n/generated-locales/ca-ES.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/ca-ES.generated.js +0 -5
- package/dist/i18n/generated-locales/cs-CZ.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/cs-CZ.generated.js +0 -5
- package/dist/i18n/generated-locales/da-DK.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/da-DK.generated.js +0 -5
- package/dist/i18n/generated-locales/de-DE.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/de-DE.generated.js +0 -5
- package/dist/i18n/generated-locales/el-GR.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/el-GR.generated.js +0 -5
- package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/en.generated.js +8 -5
- package/dist/i18n/generated-locales/es-ES.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/es-ES.generated.js +0 -5
- package/dist/i18n/generated-locales/fi-FI.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/fi-FI.generated.js +0 -5
- package/dist/i18n/generated-locales/fr-FR.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/fr-FR.generated.js +0 -5
- package/dist/i18n/generated-locales/he-IL.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/he-IL.generated.js +0 -5
- package/dist/i18n/generated-locales/hu-HU.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/hu-HU.generated.js +0 -5
- package/dist/i18n/generated-locales/it-IT.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/it-IT.generated.js +0 -5
- package/dist/i18n/generated-locales/ja-JP.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/ja-JP.generated.js +0 -5
- package/dist/i18n/generated-locales/ko-KR.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/ko-KR.generated.js +0 -5
- package/dist/i18n/generated-locales/nl-NL.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/nl-NL.generated.js +0 -5
- package/dist/i18n/generated-locales/no-NO.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/no-NO.generated.js +0 -5
- package/dist/i18n/generated-locales/pl-PL.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pl-PL.generated.js +0 -5
- package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pseudo.generated.js +8 -5
- package/dist/i18n/generated-locales/pt-BR.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pt-BR.generated.js +0 -5
- package/dist/i18n/generated-locales/pt-PT.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pt-PT.generated.js +0 -5
- package/dist/i18n/generated-locales/ro-RO.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/ro-RO.generated.js +0 -5
- package/dist/i18n/generated-locales/ru-RU.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/ru-RU.generated.js +0 -5
- package/dist/i18n/generated-locales/sr-SP.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/sr-SP.generated.js +0 -5
- package/dist/i18n/generated-locales/sv-SE.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/sv-SE.generated.js +0 -5
- package/dist/i18n/generated-locales/tr-TR.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/tr-TR.generated.js +0 -5
- package/dist/i18n/generated-locales/uk-UA.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/uk-UA.generated.js +0 -5
- package/dist/i18n/generated-locales/vi-VN.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/vi-VN.generated.js +0 -5
- package/dist/i18n/generated-locales/zh-CN.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/zh-CN.generated.js +0 -5
- package/dist/i18n/generated-locales/zh-TW.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/zh-TW.generated.js +0 -5
- package/dist/i18n/useListFormatter.d.ts +9 -0
- package/dist/i18n/useListFormatter.d.ts.map +1 -0
- package/dist/i18n/useListFormatter.js +41 -0
- package/dist/motion/animator.d.ts +83 -8
- package/dist/motion/animator.d.ts.map +1 -1
- package/dist/motion/animator.js +701 -119
- package/dist/motion/coast.d.ts +4 -1
- package/dist/motion/coast.d.ts.map +1 -1
- package/dist/motion/coast.js +4 -2
- package/dist/motion/reducedMotion.d.ts.map +1 -1
- package/dist/motion/reducedMotion.js +6 -2
- package/dist/motion/reducedMotionPlan.d.ts +7 -0
- package/dist/motion/reducedMotionPlan.d.ts.map +1 -1
- package/dist/motion/reducedMotionPlan.js +14 -1
- package/dist/motion/resolveMotion.d.ts +42 -0
- package/dist/motion/resolveMotion.d.ts.map +1 -0
- package/dist/motion/resolveMotion.js +43 -0
- package/dist/motion/roleValue.d.ts +2 -1
- package/dist/motion/roleValue.d.ts.map +1 -1
- package/dist/motion/roleValue.js +2 -1
- package/dist/motion/uikit/timing.d.ts +7 -1
- package/dist/motion/uikit/timing.d.ts.map +1 -1
- package/dist/motion/uikit/timing.js +19 -1
- package/dist/theme/defineTheme.d.ts +19 -5
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/defineTheme.js +64 -4
- package/dist/theme/domainTokens/domainTokenDefaults.d.ts +3 -0
- package/dist/theme/domainTokens/domainTokenDefaults.d.ts.map +1 -0
- package/dist/theme/domainTokens/domainTokenDefaults.js +19 -0
- package/dist/theme/domainTokens/index.d.ts +5 -4
- package/dist/theme/domainTokens/index.d.ts.map +1 -1
- package/dist/theme/domainTokens/index.js +5 -9
- package/dist/theme/index.d.ts +1 -0
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/motionKnobs.d.ts +56 -0
- package/dist/theme/motionKnobs.d.ts.map +1 -0
- package/dist/theme/motionKnobs.js +48 -0
- package/dist/theme/resolveThemeValues.d.ts +11 -4
- package/dist/theme/resolveThemeValues.d.ts.map +1 -1
- package/dist/theme/resolveThemeValues.js +40 -4
- package/dist/theme/themeAdaptations.d.ts +7 -3
- package/dist/theme/themeAdaptations.d.ts.map +1 -1
- package/dist/theme/themeAdaptations.js +36 -7
- package/dist/theme/themeMotion.d.ts +96 -0
- package/dist/theme/themeMotion.d.ts.map +1 -0
- package/dist/theme/themeMotion.js +177 -0
- package/dist/theme/tokenReferences.d.ts +31 -0
- package/dist/theme/tokenReferences.d.ts.map +1 -0
- package/dist/theme/tokenReferences.js +132 -0
- package/dist/theme/tokens.d.ts +1 -0
- package/dist/theme/tokens.d.ts.map +1 -1
- package/dist/theme/tokens.js +9 -129
- package/dist/theme/tokens.stylex.d.ts +12 -0
- package/dist/theme/tokens.stylex.d.ts.map +1 -1
- package/dist/theme/tokens.stylex.js +12 -0
- package/dist/utils/focusOutline.stylex.d.ts +6 -0
- package/dist/utils/focusOutline.stylex.d.ts.map +1 -1
- package/dist/utils/focusOutline.stylex.js +7 -0
- package/dist/utils/focusReturn.d.ts +17 -1
- package/dist/utils/focusReturn.d.ts.map +1 -1
- package/dist/utils/focusReturn.js +11 -4
- package/dist/utils/getNodeText.d.ts +8 -0
- package/dist/utils/getNodeText.d.ts.map +1 -0
- package/dist/utils/getNodeText.js +37 -0
- package/dist/utils/interactionModality.d.ts +11 -2
- package/dist/utils/interactionModality.d.ts.map +1 -1
- package/dist/utils/interactionModality.js +58 -5
- package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
- package/dist/utils/interactionOverlay.stylex.js +17 -8
- package/dist/utils/plainDate.d.ts +2 -0
- package/dist/utils/plainDate.d.ts.map +1 -1
- package/dist/utils/plainDate.js +46 -3
- package/dist/utils/presentation.d.ts +50 -0
- package/dist/utils/presentation.d.ts.map +1 -0
- package/dist/utils/presentation.js +125 -0
- package/dist/utils/pressFeedback.d.ts.map +1 -1
- package/dist/utils/pressFeedback.js +20 -1
- package/dist/utils/primaryClick.d.ts +11 -0
- package/dist/utils/primaryClick.d.ts.map +1 -0
- package/dist/utils/primaryClick.js +25 -0
- package/dist/utils/sharedMutationObserver.d.ts +19 -11
- package/dist/utils/sharedMutationObserver.d.ts.map +1 -1
- package/dist/utils/sharedMutationObserver.js +66 -26
- package/dist/utils/timeFormat.d.ts +47 -0
- package/dist/utils/timeFormat.d.ts.map +1 -0
- package/dist/utils/timeFormat.js +240 -0
- package/dist/utils/touchPresentation.d.ts +2 -2
- package/dist/utils/touchPresentation.d.ts.map +1 -1
- package/dist/utils/touchPresentation.js +13 -8
- package/dist/utils/touchPresentation.stylex.d.ts +15 -0
- package/dist/utils/touchPresentation.stylex.d.ts.map +1 -0
- package/dist/utils/touchPresentation.stylex.js +33 -0
- package/dist/utils/touchQueries.d.ts +13 -0
- package/dist/utils/touchQueries.d.ts.map +1 -0
- package/dist/utils/touchQueries.js +15 -0
- package/dist/utils/unicodeDigits.d.ts +20 -0
- package/dist/utils/unicodeDigits.d.ts.map +1 -0
- package/dist/utils/unicodeDigits.js +70 -0
- package/dist/utils/useFocusAcrossSwap.d.ts +19 -0
- package/dist/utils/useFocusAcrossSwap.d.ts.map +1 -0
- package/dist/utils/useFocusAcrossSwap.js +41 -0
- package/locales/af-ZA.json +0 -21
- package/locales/ar-SA.json +0 -21
- package/locales/ca-ES.json +0 -21
- package/locales/cs-CZ.json +0 -21
- package/locales/da-DK.json +0 -21
- package/locales/de-DE.json +0 -21
- package/locales/el-GR.json +0 -21
- package/locales/en.json +35 -23
- package/locales/es-ES.json +0 -21
- package/locales/fi-FI.json +0 -21
- package/locales/fr-FR.json +0 -21
- package/locales/he-IL.json +0 -21
- package/locales/hu-HU.json +0 -21
- package/locales/it-IT.json +0 -21
- package/locales/ja-JP.json +0 -21
- package/locales/ko-KR.json +0 -21
- package/locales/nl-NL.json +0 -21
- package/locales/no-NO.json +0 -21
- package/locales/pl-PL.json +0 -21
- package/locales/pseudo.json +23 -14
- package/locales/pt-BR.json +0 -21
- package/locales/pt-PT.json +0 -21
- package/locales/ro-RO.json +0 -21
- package/locales/ru-RU.json +0 -21
- package/locales/sr-SP.json +0 -21
- package/locales/sv-SE.json +0 -21
- package/locales/tr-TR.json +0 -21
- package/locales/uk-UA.json +0 -21
- package/locales/vi-VN.json +0 -21
- package/locales/zh-CN.json +0 -21
- package/locales/zh-TW.json +0 -21
- package/package.json +5 -4
- package/src/AlertDialog/AlertDialog.tsx +6 -5
- package/src/AlertDialog/__tests__/AlertDialog.a11y.chromium.spec.ts +18 -6
- package/src/AppShell/AppShell.doc.mjs +1 -1
- package/src/AppShell/AppShell.spec.md +9 -12
- package/src/AppShell/AppShell.test.tsx +164 -2
- package/src/AppShell/AppShell.tsx +225 -5
- package/src/AppShell/__tests__/AppShellMainKeyboard.a11y.browser.spec.ts +534 -0
- package/src/AppShell/observeMainOverflow.test.ts +173 -0
- package/src/AppShell/observeMainOverflow.ts +143 -0
- package/src/Avatar/Avatar.spec.md +22 -22
- package/src/Avatar/Avatar.tsx +18 -1
- package/src/Avatar/__tests__/AvatarForcedColors.a11y.chromium.spec.ts +126 -0
- package/src/Badge/Badge.doc.mjs +1 -1
- package/src/Badge/Badge.tsx +19 -2
- package/src/Badge/__tests__/BadgeLargerText.a11y.chromium.spec.ts +260 -0
- package/src/BottomSheet/BottomSheet.test.tsx +46 -0
- package/src/BottomSheet/BottomSheet.tsx +1 -1
- package/src/BottomSheet/BottomSheetPanel.tsx +2 -1
- package/src/BottomSheet/useSheetGestures.test.ts +52 -0
- package/src/BottomSheet/useSheetGestures.ts +37 -6
- package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.chromium.spec.ts +18 -6
- package/src/Button/Button.tsx +4 -4
- package/src/Calendar/Calendar.spec.md +87 -48
- package/src/Calendar/__tests__/Calendar.a11y.chromium.spec.ts +21 -7
- package/src/Carousel/Carousel.doc.mjs +1 -1
- package/src/Carousel/Carousel.spec.md +9 -9
- package/src/Carousel/Carousel.test.tsx +124 -3
- package/src/Carousel/Carousel.tsx +10 -2
- package/src/Carousel/__tests__/Carousel.a11y.chromium.spec.ts +21 -7
- package/src/Carousel/__tests__/Carousel.a11y.known-failures.ts +1 -16
- package/src/Chat/ChatComposer.spec.md +75 -59
- package/src/Chat/ChatComposerTokenElement.spec.md +45 -33
- package/src/Chat/ChatDictationButton.spec.md +126 -81
- package/src/Chat/ChatLayout.spec.md +41 -36
- package/src/Chat/ChatLayoutScrollButton.spec.md +87 -103
- package/src/Chat/ChatMessage.spec.md +64 -65
- package/src/Chat/ChatSendButton.spec.md +78 -67
- package/src/CheckboxInput/CheckboxInput.tsx +6 -5
- package/src/Citation/Citation.spec.md +60 -47
- package/src/ClickableCard/ClickableCard.test.tsx +96 -0
- package/src/ClickableCard/ClickableCard.tsx +48 -4
- package/src/CodeBlock/__tests__/CodeBlockCopyClearance.a11y.chromium.spec.ts +38 -3
- package/src/Collapsible/Collapsible.spec.md +2 -0
- package/src/Collapsible/CollapsibleGroup.spec.md +57 -29
- package/src/Collapsible/__tests__/Collapsible.a11y.chromium.spec.ts +18 -7
- package/src/Collapsible/__tests__/CollapsibleGroupAccordion.a11y.chromium.spec.ts +18 -6
- package/src/ComplexSelector/ComplexSelector.test.tsx +148 -0
- package/src/ComplexSelector/ComplexSelector.tsx +41 -4
- package/src/ContextMenu/ContextMenu.spec.md +84 -56
- package/src/ContextMenu/ContextMenu.test.tsx +35 -0
- package/src/ContextMenu/ContextMenuItem.spec.md +132 -0
- package/src/DateInput/DateInput.spec.md +74 -57
- package/src/DateInput/DateInput.test.tsx +115 -3
- package/src/DateInput/DateInput.tsx +29 -14
- package/src/DateInput/DateInputTouch.test.tsx +120 -0
- package/src/DateInput/MonthScroller.tsx +106 -20
- package/src/DateInput/MonthScrollerSettle.test.tsx +165 -0
- package/src/DateInput/TouchDateField.tsx +14 -3
- package/src/DateInput/Wheel.tsx +9 -6
- package/src/DateInput/__tests__/MonthScrollerSteps.interaction.browser.spec.ts +312 -0
- package/src/DateInput/usePointerDragScroll.ts +34 -10
- package/src/DateInput/useScrollSettle.ts +38 -7
- package/src/DateRangeInput/DateRangeInput.test.tsx +46 -3
- package/src/DateRangeInput/DateRangeInput.tsx +16 -12
- package/src/DateTimeInput/DateTimeInput.doc.mjs +10 -8
- package/src/DateTimeInput/DateTimeInput.spec.md +75 -51
- package/src/DateTimeInput/DateTimeInput.test.tsx +81 -0
- package/src/DateTimeInput/DateTimeInput.tsx +46 -24
- package/src/DateTimeInput/NativeTimeSegment.tsx +4 -6
- package/src/DateTimeInput/TouchDateTimeField.tsx +3 -7
- package/src/Dialog/Dialog.test.tsx +216 -2
- package/src/Dialog/Dialog.tsx +42 -9
- package/src/Dialog/DialogHeader.tsx +5 -4
- package/src/Dialog/__tests__/DialogSecondDocument.a11y.browser.spec.ts +202 -0
- package/src/Divider/Divider.tsx +2 -1
- package/src/Drawer/Drawer.spec.md +111 -80
- package/src/Drawer/Drawer.test.tsx +35 -0
- package/src/Drawer/Drawer.tsx +13 -2
- package/src/Drawer/DrawerHeader.spec.md +181 -0
- package/src/Drawer/__tests__/DrawerNonModalPageScroll.a11y.chromium.spec.ts +267 -0
- package/src/DropdownMenu/DropdownMenu.spec.md +198 -218
- package/src/DropdownMenu/DropdownMenu.test.tsx +158 -0
- package/src/DropdownMenu/DropdownMenu.tsx +43 -21
- package/src/DropdownMenu/DropdownMenuCheckboxItem.spec.md +192 -0
- package/src/DropdownMenu/DropdownMenuDivider.spec.md +25 -31
- package/src/DropdownMenu/DropdownMenuGroup.spec.md +145 -0
- package/src/DropdownMenu/DropdownMenuItem.spec.md +48 -42
- package/src/DropdownMenu/DropdownMenuRadioGroup.spec.md +34 -37
- package/src/DropdownMenu/DropdownMenuRadioItem.spec.md +54 -53
- package/src/DropdownMenu/DropdownMenuSubMenu.spec.md +47 -41
- package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +34 -0
- package/src/DropdownMenu/MenuBottomSheetActionList.tsx +6 -5
- package/src/DropdownMenu/__tests__/Menu.a11y.chromium.spec.ts +19 -7
- package/src/DropdownMenu/__tests__/MenuButton.a11y.chromium.spec.ts +21 -9
- package/src/DropdownMenu/lazyMenuBottomSheet.test.ts +60 -0
- package/src/DropdownMenu/lazyMenuBottomSheet.ts +36 -7
- package/src/DropdownMenu/renderDropdownItems.tsx +24 -19
- package/src/EmptyState/EmptyState.spec.md +35 -29
- package/src/EmptyState/EmptyState.tsx +2 -1
- package/src/Field/FieldLabel.tsx +5 -4
- package/src/Field/InputClearButton.tsx +3 -3
- package/src/Field/PanelSearchInput.tsx +2 -2
- package/src/FieldStatus/FieldStatus.tsx +9 -2
- package/src/FieldStatus/__tests__/FieldStatusAudit.a11y.chromium.spec.ts +477 -0
- package/src/FileInput/FileInput.test.tsx +159 -0
- package/src/FileInput/FileInput.tsx +34 -6
- package/src/FileInput/__tests__/FileInputAudit.a11y.chromium.spec.ts +796 -0
- package/src/FormLayout/FormLayout.spec.md +11 -20
- package/src/Heading/Heading.tsx +1 -1
- package/src/Icon/Icon.tsx +1 -1
- package/src/Indicator/CheckIndicator.spec.md +47 -42
- package/src/Indicator/CheckIndicator.tsx +2 -1
- package/src/Indicator/CheckboxIndicator.spec.md +40 -37
- package/src/Indicator/CheckboxIndicator.tsx +3 -1
- package/src/Indicator/Indicator.spec.md +188 -0
- package/src/Indicator/RadioIndicator.tsx +3 -1
- package/src/Item/Item.test.tsx +60 -0
- package/src/Item/Item.tsx +16 -8
- package/src/Item/ItemSwipeLayer.tsx +1 -1
- package/src/Kbd/Kbd.spec.md +81 -38
- package/src/Layer/__tests__/LayerBrowserHide.a11y.chromium.spec.ts +126 -0
- package/src/Layer/popoverOperations.test.ts +205 -0
- package/src/Layer/popoverOperations.ts +172 -0
- package/src/Layer/useLayer.test.tsx +426 -4
- package/src/Layer/useLayer.tsx +145 -77
- package/src/Layout/Layout.spec.md +127 -131
- package/src/Layout/Layout.tsx +1 -1
- package/src/Layout/LayoutContent.spec.md +205 -0
- package/src/Layout/LayoutContent.tsx +1 -1
- package/src/Layout/LayoutFooter.spec.md +190 -0
- package/src/Layout/LayoutFooter.tsx +1 -1
- package/src/Layout/LayoutHeader.spec.md +193 -0
- package/src/Layout/LayoutHeader.tsx +1 -1
- package/src/Layout/LayoutPanel.spec.md +194 -0
- package/src/Layout/__tests__/Layout.a11y.chromium.spec.ts +18 -6
- package/src/Lightbox/Lightbox.test.tsx +18 -0
- package/src/Lightbox/Lightbox.tsx +5 -0
- package/src/Lightbox/LightboxViewport.a11y.browser.spec.ts +8 -4
- package/src/Link/Link.spec.md +3 -2
- package/src/Link/Link.tsx +11 -0
- package/src/Link/__tests__/Link.a11y.chromium.spec.ts +18 -8
- package/src/Link/__tests__/Link.truncation.a11y.chromium.spec.ts +58 -0
- package/src/List/List.tsx +1 -1
- package/src/List/ListItem.spec.md +205 -0
- package/src/List/ListItem.tsx +3 -3
- package/src/Markdown/plugins/headingLinks.a11y.chromium.spec.ts +1 -1
- package/src/MetadataList/MetadataList.spec.md +187 -0
- package/src/MetadataList/MetadataListItem.spec.md +165 -0
- package/src/MobileNav/MobileNav.test.tsx +15 -0
- package/src/MobileNav/MobileNav.tsx +9 -1
- package/src/MobileNav/__tests__/MobileNavHeader.a11y.chromium.spec.ts +89 -0
- package/src/MoreMenu/MoreMenu.test.tsx +11 -0
- package/src/MultiSelector/CreateRow.a11y.chromium.spec.ts +7 -8
- package/src/MultiSelector/MultiSelector.spec.md +7 -4
- package/src/MultiSelector/MultiSelector.test.tsx +270 -1
- package/src/MultiSelector/MultiSelector.tsx +25 -10
- package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +8 -1
- package/src/NavMenu/NavHeadingMenu.spec.md +1 -5
- package/src/NavMenu/NavHeadingMenuItem.tsx +5 -39
- package/src/NumberInput/NumberInput.doc.mjs +13 -7
- package/src/NumberInput/NumberInput.spec.md +59 -37
- package/src/NumberInput/NumberInput.test.tsx +68 -7
- package/src/NumberInput/NumberInput.tsx +44 -4
- package/src/NumberInput/__tests__/NumberInput.coarse-pointer.a11y.chromium.spec.ts +102 -0
- package/src/NumberInput/numberParser.ts +7 -45
- package/src/Outline/Outline.spec.md +165 -69
- package/src/Outline/Outline.test.tsx +67 -2
- package/src/Outline/Outline.tsx +23 -0
- package/src/Outline/modules/parseOutlineFromMarkdown.spec.md +3 -4
- package/src/OverflowList/OverflowList.spec.md +102 -55
- package/src/Pagination/Pagination.spec.md +158 -84
- package/src/Popover/Popover.test.tsx +84 -0
- package/src/Popover/Popover.tsx +34 -14
- package/src/Popover/__tests__/PopoverSecondDocument.a11y.chromium.spec.ts +70 -8
- package/src/Popover/usePopover.tsx +10 -3
- package/src/PowerSearch/PowerSearch.test.tsx +24 -0
- package/src/PowerSearch/PowerSearch.tsx +42 -31
- package/src/ProgressBar/ProgressBar.spec.md +143 -119
- package/src/ProgressBar/ProgressBar.test.tsx +108 -32
- package/src/ProgressBar/ProgressBar.tsx +58 -10
- package/src/ProgressBar/__tests__/ProgressBar.a11y.chromium.spec.ts +21 -9
- package/src/ProgressBar/__tests__/ProgressBarMotion.a11y.chromium.spec.ts +831 -0
- package/src/RadioList/RadioList.spec.md +1 -1
- package/src/RadioList/RadioList.test.tsx +84 -0
- package/src/RadioList/RadioListItem.tsx +11 -2
- package/src/Resizable/Resizable.spec.md +144 -124
- package/src/Resizable/__tests__/ResizeHandle.a11y.chromium.spec.ts +21 -14
- package/src/Resizable/useResizable.test.ts +43 -0
- package/src/Resizable/useResizable.ts +32 -9
- package/src/SegmentedControl/SegmentedControl.test.tsx +84 -0
- package/src/SegmentedControl/SegmentedControlItem.tsx +11 -5
- package/src/Selector/Selector.test.tsx +90 -1
- package/src/Selector/__tests__/Selector.combobox.a11y.test.tsx +8 -1
- package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +8 -1
- package/src/Selector/useSelectorPresentation.ts +31 -3
- package/src/SideNav/SideNav.test.tsx +151 -4
- package/src/SideNav/SideNavItem.spec.md +14 -24
- package/src/SideNav/SideNavItem.tsx +27 -10
- package/src/SideNav/__tests__/SideNavRailMotion.interaction.browser.spec.ts +176 -51
- package/src/Skeleton/Skeleton.tsx +3 -1
- package/src/Slider/Slider.test.tsx +31 -0
- package/src/Slider/Slider.tsx +8 -3
- package/src/Slider/__tests__/Slider.a11y.chromium.spec.ts +19 -7
- package/src/Spinner/Spinner.tsx +5 -3
- package/src/Stack/Stack.tsx +1 -1
- package/src/Stack/StackItem.tsx +1 -1
- package/src/Stepper/Step.spec.md +36 -42
- package/src/Stepper/Step.tsx +33 -6
- package/src/Stepper/Stepper.doc.mjs +1 -1
- package/src/Stepper/Stepper.test.tsx +69 -7
- package/src/Switch/Switch.spec.md +1 -1
- package/src/Switch/Switch.test.tsx +66 -1
- package/src/Switch/Switch.tsx +11 -3
- package/src/TabList/TabList.test.tsx +58 -4
- package/src/TabList/TabList.tsx +16 -1
- package/src/TabList/__tests__/TabPressPointer.interaction.browser.spec.ts +163 -0
- package/src/Table/Table.spec.md +1 -1
- package/src/Table/Table.test.tsx +32 -0
- package/src/Table/Table.tsx +43 -11
- package/src/Table/__tests__/Table.a11y.chromium.spec.ts +18 -6
- package/src/Table/__tests__/Table.a11y.states.ts +1 -5
- package/src/Table/plugins/columnResize/useTableColumnResize.test.tsx +22 -0
- package/src/Table/plugins/columnResize/useTableColumnResize.tsx +8 -4
- package/src/Table/plugins/sortable/useTableSortable.test.tsx +73 -144
- package/src/Table/plugins/sortable/useTableSortable.tsx +44 -73
- package/src/Table/plugins/sortable/useTableSortableState.test.tsx +9 -21
- package/src/Text/Text.tsx +1 -1
- package/src/Text/useTruncation.test.tsx +128 -1
- package/src/Text/useTruncation.ts +23 -5
- package/src/TextArea/TextArea.spec.md +10 -13
- package/src/TextArea/TextArea.test.tsx +60 -0
- package/src/TextArea/TextArea.tsx +15 -1
- package/src/TextInput/TextInput.test.tsx +21 -0
- package/src/TextInput/TextInput.tsx +3 -1
- package/src/TimeInput/NativeTimeInput.test.tsx +17 -0
- package/src/TimeInput/Presentation.test.tsx +21 -0
- package/src/TimeInput/TimeInput.doc.mjs +8 -8
- package/src/TimeInput/TimeInput.spec.md +58 -61
- package/src/TimeInput/TimeInput.test.tsx +76 -0
- package/src/TimeInput/TimeInput.tsx +36 -18
- package/src/TimeInput/TouchTimeField.tsx +4 -6
- package/src/Timestamp/Timestamp.spec.md +6 -12
- package/src/Timestamp/Timestamp.test.tsx +30 -5
- package/src/Timestamp/Timestamp.tsx +6 -2
- package/src/Timestamp/TimestampHoverCard.tsx +4 -4
- package/src/Toast/Toast.spec.md +15 -24
- package/src/Toast/Toast.tsx +104 -36
- package/src/Toast/ToastViewport.test.tsx +798 -1
- package/src/Toast/ToastViewport.tsx +121 -53
- package/src/ToggleButton/ToggleButton.test.tsx +27 -0
- package/src/ToggleButton/ToggleButtonGroup.tsx +20 -1
- package/src/Token/Token.doc.mjs +2 -2
- package/src/Token/Token.spec.md +52 -61
- package/src/Token/Token.test.tsx +104 -0
- package/src/Token/Token.tsx +75 -5
- package/src/Token/TokenLink.tsx +21 -7
- package/src/Tokenizer/Tokenizer.spec.md +18 -4
- package/src/Tokenizer/Tokenizer.test.tsx +176 -0
- package/src/Tokenizer/Tokenizer.tsx +48 -12
- package/src/Tokenizer/TokenizerBottomSheet.test.tsx +23 -4
- package/src/Tokenizer/TokenizerBottomSheet.tsx +3 -3
- package/src/Tokenizer/TokenizerClearAllMessageContext.ts +21 -0
- package/src/Toolbar/Toolbar.spec.md +5 -6
- package/src/Toolbar/Toolbar.test.tsx +30 -0
- package/src/Toolbar/Toolbar.tsx +2 -1
- package/src/Toolbar/__tests__/Toolbar.a11y.chromium.spec.ts +18 -6
- package/src/Tooltip/__tests__/Tooltip.a11y.chromium.spec.ts +18 -7
- package/src/TopNav/TopNav.test.tsx +27 -0
- package/src/TopNav/TopNavMegaMenu.test.tsx +21 -0
- package/src/TopNav/TopNavMegaMenu.tsx +4 -1
- package/src/TopNav/TopNavMenu.test.tsx +17 -0
- package/src/TopNav/TopNavMenu.tsx +1 -0
- package/src/TreeList/TreeList.test.tsx +26 -0
- package/src/TreeList/TreeListItem.tsx +26 -4
- package/src/Typeahead/BaseTypeahead.doc.mjs +3 -3
- package/src/Typeahead/BaseTypeahead.spec.md +45 -45
- package/src/Typeahead/BaseTypeahead.tsx +48 -9
- package/src/Typeahead/Typeahead.test.tsx +294 -11
- package/src/Typeahead/Typeahead.tsx +51 -9
- package/src/__tests__/internalModulesNotExperimental.test.ts +88 -0
- package/src/__tests__/pressState.ts +8 -4
- package/src/gestures/GestureArena.ts +111 -11
- package/src/gestures/GestureHost.ts +2064 -0
- package/src/gestures/GestureRecognizer.ts +21 -1
- package/src/gestures/LongPressGestureRecognizer.ts +4 -0
- package/src/gestures/PanGestureRecognizer.ts +4 -0
- package/src/gestures/PinchGestureRecognizer.ts +148 -0
- package/src/gestures/PressTracker.ts +386 -0
- package/src/gestures/README.md +420 -0
- package/src/gestures/ScreenEdgePanGestureRecognizer.ts +256 -0
- package/src/gestures/SwipeGestureRecognizer.ts +197 -0
- package/src/gestures/TapGestureRecognizer.ts +4 -0
- package/src/gestures/VelocityTracker.ts +10 -3
- package/src/gestures/__tests__/GestureHost.test.ts +2136 -0
- package/src/gestures/__tests__/PanGestureRecognizer.test.ts +4 -4
- package/src/gestures/__tests__/PinchGestureRecognizer.test.ts +74 -0
- package/src/gestures/__tests__/PressTracker.test.ts +275 -0
- package/src/gestures/__tests__/ScreenEdgePanGestureRecognizer.test.ts +498 -0
- package/src/gestures/__tests__/SwipeGestureRecognizer.test.ts +108 -0
- package/src/gestures/__tests__/arena.test.ts +40 -0
- package/src/gestures/__tests__/arenaPinchSwipeEdge.test.ts +193 -0
- package/src/gestures/__tests__/browser/build-fixture.mjs +46 -0
- package/src/gestures/__tests__/browser/fixture.ts +307 -0
- package/src/gestures/__tests__/browser/gestures.interaction.browser.spec.ts +1137 -0
- package/src/gestures/__tests__/browserNegotiation.test.ts +293 -0
- package/src/gestures/__tests__/haptics.test.ts +187 -0
- package/src/gestures/__tests__/harness.ts +52 -4
- package/src/gestures/__tests__/pressTrackerFactory.test.ts +59 -0
- package/src/gestures/__tests__/recognizerKind.test.ts +78 -0
- package/src/gestures/__tests__/touchActionRule.test.ts +54 -0
- package/src/gestures/__tests__/useGestureRecognizer.test.tsx +570 -0
- package/src/gestures/haptics.ts +203 -0
- package/src/gestures/index.ts +79 -0
- package/src/gestures/nativeScroll.ts +277 -0
- package/src/gestures/touchAction.ts +191 -0
- package/src/gestures/touchActionRule.ts +157 -0
- package/src/gestures/types.ts +30 -9
- package/src/gestures/useGestureRecognizer.ts +305 -0
- package/src/hooks/index.ts +5 -1
- package/src/hooks/modalAnnounce.ts +129 -72
- package/src/hooks/scrollKeyboardDelegation.test.ts +33 -0
- package/src/hooks/scrollKeyboardDelegation.ts +9 -3
- package/src/hooks/useAdaptivePresentation.ts +4 -1
- package/src/hooks/useAnnounce.doc.mjs +35 -3
- package/src/hooks/useAnnounce.replace.test.tsx +516 -0
- package/src/hooks/useAnnounce.ts +209 -102
- package/src/hooks/useAnnounceRenderedText.adopters.test.tsx +10 -1
- package/src/hooks/useAnnounceRenderedText.ts +17 -3
- package/src/hooks/useBusyAnnouncement.ts +7 -10
- package/src/hooks/useMenuHover.ts +15 -1
- package/src/hooks/useScrollOverflow.doc.mjs +1 -1
- package/src/hooks/useScrollOverflow.test.tsx +102 -1
- package/src/hooks/useScrollOverflow.ts +59 -10
- package/src/i18n/__tests__/useListFormatter.test.tsx +58 -0
- package/src/i18n/generated-locales/af-ZA.generated.ts +0 -7
- package/src/i18n/generated-locales/ar-SA.generated.ts +0 -6
- package/src/i18n/generated-locales/ca-ES.generated.ts +0 -7
- package/src/i18n/generated-locales/cs-CZ.generated.ts +0 -6
- package/src/i18n/generated-locales/da-DK.generated.ts +0 -6
- package/src/i18n/generated-locales/de-DE.generated.ts +0 -6
- package/src/i18n/generated-locales/el-GR.generated.ts +0 -7
- package/src/i18n/generated-locales/en.generated.ts +9 -6
- package/src/i18n/generated-locales/es-ES.generated.ts +0 -7
- package/src/i18n/generated-locales/fi-FI.generated.ts +0 -6
- package/src/i18n/generated-locales/fr-FR.generated.ts +0 -7
- package/src/i18n/generated-locales/he-IL.generated.ts +0 -6
- package/src/i18n/generated-locales/hu-HU.generated.ts +0 -6
- package/src/i18n/generated-locales/it-IT.generated.ts +0 -7
- package/src/i18n/generated-locales/ja-JP.generated.ts +0 -6
- package/src/i18n/generated-locales/ko-KR.generated.ts +0 -6
- package/src/i18n/generated-locales/nl-NL.generated.ts +0 -6
- package/src/i18n/generated-locales/no-NO.generated.ts +0 -6
- package/src/i18n/generated-locales/pl-PL.generated.ts +0 -7
- package/src/i18n/generated-locales/pseudo.generated.ts +9 -6
- package/src/i18n/generated-locales/pt-BR.generated.ts +0 -7
- package/src/i18n/generated-locales/pt-PT.generated.ts +0 -7
- package/src/i18n/generated-locales/ro-RO.generated.ts +0 -6
- package/src/i18n/generated-locales/ru-RU.generated.ts +0 -7
- package/src/i18n/generated-locales/sr-SP.generated.ts +0 -6
- package/src/i18n/generated-locales/sv-SE.generated.ts +0 -6
- package/src/i18n/generated-locales/tr-TR.generated.ts +0 -7
- package/src/i18n/generated-locales/uk-UA.generated.ts +0 -7
- package/src/i18n/generated-locales/vi-VN.generated.ts +0 -6
- package/src/i18n/generated-locales/zh-CN.generated.ts +0 -6
- package/src/i18n/generated-locales/zh-TW.generated.ts +0 -6
- package/src/i18n/useListFormatter.ts +42 -0
- package/src/motion/MEASUREMENTS.md +2 -1
- package/src/motion/README.md +113 -33
- package/src/motion/__tests__/animator.interaction.browser.spec.ts +843 -0
- package/src/motion/__tests__/motionBundle.ts +14 -0
- package/src/motion/animator.test.ts +1125 -32
- package/src/motion/animator.ts +932 -130
- package/src/motion/coast.ts +10 -3
- package/src/motion/reducedMotion.ts +12 -2
- package/src/motion/reducedMotionPlan.test.ts +26 -0
- package/src/motion/reducedMotionPlan.ts +20 -1
- package/src/motion/resolveMotion.test.ts +123 -0
- package/src/motion/resolveMotion.ts +68 -0
- package/src/motion/roleValue.ts +2 -1
- package/src/motion/roles.test.ts +21 -4
- package/src/motion/uikit/tableImports.test.ts +293 -0
- package/src/motion/uikit/timing.ts +15 -1
- package/src/motion/uikit/values.test.ts +23 -12
- package/src/theme/Theme.doc.mjs +1 -1
- package/src/theme/defineTheme.ts +117 -8
- package/src/theme/derivedVarRegistry.test.ts +4 -0
- package/src/theme/domainTokens/domainTokenDefaults.ts +19 -0
- package/src/theme/domainTokens/index.ts +6 -10
- package/src/theme/expandMotionScale.test.ts +1 -1
- package/src/theme/index.ts +5 -0
- package/src/theme/motionKnobs.ts +82 -0
- package/src/theme/resolveThemeValues.ts +68 -6
- package/src/theme/themeAdaptations.ts +52 -12
- package/src/theme/themeMotion.test.ts +620 -0
- package/src/theme/themeMotion.ts +221 -0
- package/src/theme/tokenReferences.ts +149 -0
- package/src/theme/tokens.stylex.ts +10 -0
- package/src/theme/tokens.ts +15 -158
- package/src/utils/focusOutline.stylex.ts +28 -0
- package/src/utils/focusReturn.ts +17 -6
- package/src/utils/getNodeText.test.ts +24 -0
- package/src/utils/getNodeText.ts +37 -0
- package/src/utils/interactionModality.test.ts +45 -1
- package/src/utils/interactionModality.ts +64 -5
- package/src/utils/interactionOverlay.stylex.ts +12 -3
- package/src/utils/plainDate.test.ts +90 -0
- package/src/utils/plainDate.ts +58 -3
- package/src/utils/presentation.test.ts +380 -0
- package/src/utils/presentation.ts +180 -0
- package/src/utils/pressFeedback.test.ts +138 -0
- package/src/utils/pressFeedback.ts +34 -11
- package/src/utils/pressGesture.test.ts +9 -0
- package/src/utils/pressPaintPriority.test.ts +80 -0
- package/src/utils/primaryClick.test.ts +38 -0
- package/src/utils/primaryClick.ts +28 -0
- package/src/utils/sharedMutationObserver.test.ts +54 -1
- package/src/utils/sharedMutationObserver.ts +87 -29
- package/src/utils/timeFormat.test.ts +204 -0
- package/src/utils/timeFormat.ts +314 -0
- package/src/utils/touchPresentation.stylex.ts +43 -0
- package/src/utils/touchPresentation.test.ts +140 -1
- package/src/utils/touchPresentation.ts +13 -8
- package/src/utils/touchQueries.ts +15 -0
- package/src/utils/unicodeDigits.test.ts +44 -0
- package/src/utils/unicodeDigits.ts +71 -0
- package/src/utils/useFocusAcrossSwap.ts +55 -0
|
@@ -9,9 +9,8 @@
|
|
|
9
9
|
* back: presentation values and velocities, retarget continuity, stop,
|
|
10
10
|
* completion, Reduced Motion substitutions, momentum curves (a coast with
|
|
11
11
|
* edges), late attach
|
|
12
|
-
* @position Testing;
|
|
13
|
-
*
|
|
14
|
-
* cover
|
|
12
|
+
* @position Testing; the compositor itself is proven in
|
|
13
|
+
* __tests__/animator.interaction.browser.spec.ts
|
|
15
14
|
*/
|
|
16
15
|
|
|
17
16
|
import {execFileSync} from 'node:child_process';
|
|
@@ -67,18 +66,43 @@ const requestFrame = (callback: () => void) => {
|
|
|
67
66
|
* properties, so the page is modeled here).
|
|
68
67
|
*/
|
|
69
68
|
let stylesheet: Record<string, string> = {};
|
|
69
|
+
/** The stylesheet's values for a pseudo-element of the element. */
|
|
70
|
+
let pseudoStylesheet: Record<string, string> = {};
|
|
70
71
|
|
|
71
72
|
beforeEach(() => {
|
|
72
73
|
clock = 1000;
|
|
73
74
|
recorded = [];
|
|
74
75
|
frameQueue = [];
|
|
75
76
|
stylesheet = {};
|
|
77
|
+
pseudoStylesheet = {};
|
|
76
78
|
vi.spyOn(window, 'getComputedStyle').mockImplementation(
|
|
77
|
-
element =>
|
|
79
|
+
(element, pseudo) =>
|
|
78
80
|
({
|
|
79
|
-
getPropertyValue: (property: string) =>
|
|
80
|
-
(
|
|
81
|
-
|
|
81
|
+
getPropertyValue: (property: string) => {
|
|
82
|
+
if (pseudo != null) {
|
|
83
|
+
return pseudoStylesheet[property] ?? '';
|
|
84
|
+
}
|
|
85
|
+
// A live animation wins over the inline value, holding its last
|
|
86
|
+
// keyframe (the end-state tail) until it is cancelled.
|
|
87
|
+
const held = recorded
|
|
88
|
+
.filter(
|
|
89
|
+
animation =>
|
|
90
|
+
animation.element === element &&
|
|
91
|
+
!animation.cancelled &&
|
|
92
|
+
animation.keyframes.some(frame => property in frame),
|
|
93
|
+
)
|
|
94
|
+
.at(-1);
|
|
95
|
+
const last = held?.keyframes
|
|
96
|
+
.filter(frame => property in frame)
|
|
97
|
+
.at(-1) as Record<string, unknown> | undefined;
|
|
98
|
+
if (last != null) {
|
|
99
|
+
return String(last[property]);
|
|
100
|
+
}
|
|
101
|
+
return (
|
|
102
|
+
(element as HTMLElement).style.getPropertyValue(property) ||
|
|
103
|
+
(stylesheet[property] ?? '')
|
|
104
|
+
);
|
|
105
|
+
},
|
|
82
106
|
}) as unknown as CSSStyleDeclaration,
|
|
83
107
|
);
|
|
84
108
|
vi.useFakeTimers();
|
|
@@ -261,7 +285,7 @@ function sampleAt(
|
|
|
261
285
|
}
|
|
262
286
|
|
|
263
287
|
describe('animate: keyframes on the compositor', () => {
|
|
264
|
-
it('bakes the spring into keyframes within a
|
|
288
|
+
it('bakes the spring into keyframes within a quarter of a pixel of the curve', async () => {
|
|
265
289
|
const {animator} = setup();
|
|
266
290
|
animator.animate({x: 300}, {spring: springs.settle});
|
|
267
291
|
const [animation] = live('translate');
|
|
@@ -280,7 +304,7 @@ describe('animate: keyframes on the compositor', () => {
|
|
|
280
304
|
expect(duration).toBeCloseTo(curve.settleTime(0.05) * 1000, 6);
|
|
281
305
|
for (let t = 0; t <= duration; t += 0.5) {
|
|
282
306
|
const [x, y] = sampleAt(animation, 'translate', 1000 + t);
|
|
283
|
-
expect(Math.abs(x - curve.value(t / 1000))).toBeLessThan(0.
|
|
307
|
+
expect(Math.abs(x - curve.value(t / 1000))).toBeLessThan(0.26);
|
|
284
308
|
expect(y).toBe(0);
|
|
285
309
|
expect(animator.get('x', 1000 + t)).toBeCloseTo(curve.value(t / 1000), 9);
|
|
286
310
|
}
|
|
@@ -290,10 +314,41 @@ describe('animate: keyframes on the compositor', () => {
|
|
|
290
314
|
).toEqual([300, 0]);
|
|
291
315
|
});
|
|
292
316
|
|
|
317
|
+
it('on a 3x screen, bakes the spring within a quarter of a device pixel', async () => {
|
|
318
|
+
vi.stubGlobal('devicePixelRatio', 3);
|
|
319
|
+
const {animator} = setup();
|
|
320
|
+
animator.animate({x: 300}, {spring: springs.settle});
|
|
321
|
+
const [animation] = live('translate');
|
|
322
|
+
await commitFrame(1000);
|
|
323
|
+
const curve = solveSpring(toSpring(springs.settle), {from: 0, to: 300});
|
|
324
|
+
const duration = Number(animation.options.duration) - END_HOLD_MS;
|
|
325
|
+
let worst = 0;
|
|
326
|
+
for (let t = 0; t <= duration; t += 0.5) {
|
|
327
|
+
const [x] = sampleAt(animation, 'translate', 1000 + t);
|
|
328
|
+
worst = Math.max(worst, Math.abs(x - curve.value(t / 1000)));
|
|
329
|
+
}
|
|
330
|
+
// In device pixels, as the curve's chords are checked at their quarters.
|
|
331
|
+
expect(worst * 3).toBeLessThan(0.26);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it('on a 1x screen, bakes no more keyframes than a quarter of a CSS pixel needs', () => {
|
|
335
|
+
const count = (ratio: number) => {
|
|
336
|
+
vi.stubGlobal('devicePixelRatio', ratio);
|
|
337
|
+
recorded = [];
|
|
338
|
+
const {animator} = setup();
|
|
339
|
+
animator.animate({x: 300}, {spring: springs.settle});
|
|
340
|
+
return live('translate')[0].keyframes.length;
|
|
341
|
+
};
|
|
342
|
+
const desktop = count(1);
|
|
343
|
+
expect(desktop).toBeLessThan(count(3));
|
|
344
|
+
// A ratio below 1 (a zoomed-out page) bakes as 1, never coarser.
|
|
345
|
+
expect(count(0.5)).toBe(desktop);
|
|
346
|
+
});
|
|
347
|
+
|
|
293
348
|
it('keeps the keyframe count modest (adaptive sampling)', () => {
|
|
294
349
|
const {animator} = setup();
|
|
295
350
|
animator.animate({x: 300}, {spring: springs.settle});
|
|
296
|
-
expect(live('translate')[0].keyframes.length).toBeLessThan(
|
|
351
|
+
expect(live('translate')[0].keyframes.length).toBeLessThan(60);
|
|
297
352
|
});
|
|
298
353
|
|
|
299
354
|
it('clears an identity transform at rest, so a fixed child stays fixed', async () => {
|
|
@@ -302,18 +357,16 @@ describe('animate: keyframes on the compositor', () => {
|
|
|
302
357
|
{x: 100, scale: 0.9, rotate: 10},
|
|
303
358
|
{spring: springs.quickChange},
|
|
304
359
|
);
|
|
305
|
-
|
|
360
|
+
// While the curve runs, the inline value is where it starts.
|
|
361
|
+
expect(['', 'none']).toContain(element.style.translate);
|
|
306
362
|
await advance(100);
|
|
307
363
|
animator.animate(
|
|
308
364
|
{x: 0, scale: 1, rotate: 0},
|
|
309
365
|
{spring: springs.quickChange},
|
|
310
366
|
);
|
|
311
|
-
// While the motion back runs, the
|
|
312
|
-
// nothing where the stylesheet agrees.
|
|
367
|
+
// While the motion back runs, the inline value is where it starts.
|
|
313
368
|
expect(live('translate')).toHaveLength(1);
|
|
314
|
-
|
|
315
|
-
expect(['', 'none']).toContain(element.style[channel]);
|
|
316
|
-
}
|
|
369
|
+
expect(parse(element.style.translate)[0]).toBeCloseTo(animator.get('x'), 2);
|
|
317
370
|
await advance(5000);
|
|
318
371
|
// At rest the identity leaves the element: translate 0px 0px would make
|
|
319
372
|
// the element a fixed child's containing block, and scale 1 stops a
|
|
@@ -350,21 +403,41 @@ describe('animate: keyframes on the compositor', () => {
|
|
|
350
403
|
expect(element.style.scale).toBe('none');
|
|
351
404
|
});
|
|
352
405
|
|
|
353
|
-
it('
|
|
406
|
+
it('keeps a fade to 1 inline where the stylesheet rests at 0', async () => {
|
|
407
|
+
const {animator, element} = setup();
|
|
408
|
+
// A scroll edge layer: transparent in its stylesheet, faded in on a
|
|
409
|
+
// spring. At retire the end-state tail still holds 1, so the end can only
|
|
410
|
+
// be judged against the stylesheet once the animation is gone.
|
|
411
|
+
stylesheet.opacity = '0';
|
|
412
|
+
animator.set({opacity: 0});
|
|
413
|
+
animator.animate({opacity: 1}, {spring: springs.quickChange});
|
|
414
|
+
await advance(5000);
|
|
415
|
+
expect(live('opacity')).toHaveLength(0);
|
|
416
|
+
expect(element.style.opacity).toBe('1');
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it('holds the value on screen inline while a curve runs, and the end once it retires', async () => {
|
|
354
420
|
const {animator, element} = setup();
|
|
421
|
+
animator.set({x: 10, opacity: 0.5});
|
|
355
422
|
animator.animate(
|
|
356
423
|
{x: 120, y: -40, scale: 1.1, rotate: 12, opacity: 0.25},
|
|
357
424
|
{spring: springs.quickChange},
|
|
358
425
|
);
|
|
426
|
+
// A frame painted before the compositor has the curve shows where the
|
|
427
|
+
// element is, never where it is going.
|
|
428
|
+
expect(element.style.translate).toBe('10px 0px');
|
|
429
|
+
expect(element.style.opacity).toBe('0.5');
|
|
430
|
+
const animations = ['translate', 'scale', 'rotate', 'opacity'].map(
|
|
431
|
+
channel => live(channel)[0],
|
|
432
|
+
);
|
|
433
|
+
await commitFrame(clock);
|
|
434
|
+
await advance(5000);
|
|
359
435
|
expect(element.style.translate).toBe('120px -40px');
|
|
360
436
|
expect(element.style.scale).toBe('1.1 1.1');
|
|
361
437
|
expect(element.style.rotate).toBe('12deg');
|
|
362
438
|
expect(element.style.opacity).toBe('0.25');
|
|
363
|
-
for (const
|
|
364
|
-
|
|
365
|
-
expect(animation.keyframes[animation.keyframes.length - 1][channel]).toBe(
|
|
366
|
-
(element.style as unknown as Record<string, string>)[channel],
|
|
367
|
-
);
|
|
439
|
+
for (const animation of animations) {
|
|
440
|
+
expect(animation.cancelled).toBe(true);
|
|
368
441
|
}
|
|
369
442
|
});
|
|
370
443
|
|
|
@@ -385,6 +458,47 @@ describe('animate: keyframes on the compositor', () => {
|
|
|
385
458
|
);
|
|
386
459
|
});
|
|
387
460
|
|
|
461
|
+
it('looks for its end again on the next frame when the engine dates animations per frame (WebKit), not in a spin', async () => {
|
|
462
|
+
// WebKit moves an animation's currentTime once per frame. A retire that
|
|
463
|
+
// looks within a frame reads the frame's time, a fraction of a ms short of
|
|
464
|
+
// the end, and a timer set to that fraction fires again before anything
|
|
465
|
+
// moved: dozens of timers on the main thread after every motion.
|
|
466
|
+
const {animator} = setup();
|
|
467
|
+
const handle = animator.animate({x: 100}, {spring: springs.settle});
|
|
468
|
+
const [animation] = live('translate');
|
|
469
|
+
await commitFrame(clock);
|
|
470
|
+
const start = animation.startTime as number;
|
|
471
|
+
const curve = Number(animation.options.duration) - END_HOLD_MS;
|
|
472
|
+
let frameTime = start;
|
|
473
|
+
Object.defineProperty(animation, 'currentTime', {
|
|
474
|
+
configurable: true,
|
|
475
|
+
get: () => frameTime - start,
|
|
476
|
+
});
|
|
477
|
+
Object.defineProperty(animation, 'effect', {
|
|
478
|
+
configurable: true,
|
|
479
|
+
value: {getTiming: () => ({duration: animation.options.duration})},
|
|
480
|
+
});
|
|
481
|
+
// The last frame before the end dates the animation 0.5 ms short of it.
|
|
482
|
+
frameTime = start + curve - 0.5;
|
|
483
|
+
// Up to just before the retire is due (the injected clock and the timers
|
|
484
|
+
// move together).
|
|
485
|
+
clock += curve - 1;
|
|
486
|
+
await vi.advanceTimersByTimeAsync(curve - 1);
|
|
487
|
+
const timers = vi.spyOn(globalThis, 'setTimeout');
|
|
488
|
+
// A frame's worth of main-thread time with no frame in between.
|
|
489
|
+
for (let ms = 0; ms < 16; ms++) {
|
|
490
|
+
clock += 1;
|
|
491
|
+
await vi.advanceTimersByTimeAsync(1);
|
|
492
|
+
}
|
|
493
|
+
expect(timers.mock.calls.length).toBeLessThanOrEqual(2);
|
|
494
|
+
// The next frame: the engine's time passes the end, and it retires.
|
|
495
|
+
frameTime = start + curve + 16;
|
|
496
|
+
clock += 1;
|
|
497
|
+
await vi.advanceTimersByTimeAsync(17);
|
|
498
|
+
expect(animation.cancelled).toBe(true);
|
|
499
|
+
await expect(handle.finished).resolves.toBe(true);
|
|
500
|
+
});
|
|
501
|
+
|
|
388
502
|
it('never retires an animation a sampler has paused', async () => {
|
|
389
503
|
const {animator} = setup();
|
|
390
504
|
animator.animate({x: 120}, {spring: springs.quickChange});
|
|
@@ -450,6 +564,157 @@ describe('a call from rest starts at the commit, as Core Animation does', () =>
|
|
|
450
564
|
);
|
|
451
565
|
});
|
|
452
566
|
|
|
567
|
+
it('a start the browser dates before the block (WebKit) moves to after it', async () => {
|
|
568
|
+
// WebKit dates a pending animation at its last frame time before the
|
|
569
|
+
// task that made it, so after a block its first frame would show the
|
|
570
|
+
// motion that far in; the call's first read of that start moves it to
|
|
571
|
+
// the read. Chromium's commit start (the case above) stays.
|
|
572
|
+
Object.defineProperty(document, 'timeline', {
|
|
573
|
+
configurable: true,
|
|
574
|
+
get: () => ({currentTime: clock}),
|
|
575
|
+
});
|
|
576
|
+
try {
|
|
577
|
+
const {animator} = setup();
|
|
578
|
+
const handle = animator.animate({x: 300}, {spring: springs.settle});
|
|
579
|
+
const [animation] = live('translate');
|
|
580
|
+
// The task blocks for 300 ms; the browser dates the animation at 1000.
|
|
581
|
+
clock = 1300;
|
|
582
|
+
animation.startTime = 1000;
|
|
583
|
+
await commitFrame(1300);
|
|
584
|
+
await expect(handle.started).resolves.toBe(1300);
|
|
585
|
+
expect(animation.startTime).toBe(1300);
|
|
586
|
+
expect(animator.get('x', 1300)).toBeCloseTo(0, 9);
|
|
587
|
+
expect(animator.get('x', 1400)).toBeCloseTo(curve.value(0.1), 9);
|
|
588
|
+
} finally {
|
|
589
|
+
Reflect.deleteProperty(document, 'timeline');
|
|
590
|
+
}
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
it('a start the browser dates at the frame before the call stays when the call\u2019s task runs on a few ms', async () => {
|
|
594
|
+
// Browser start times are frame times and a task's end is a live clock:
|
|
595
|
+
// on every call they differ by up to a frame plus the task's own length.
|
|
596
|
+
// A task that runs on 5 ms after the call (a component's effect after a
|
|
597
|
+
// tap) blocks nothing, so the start stays the browser's and a sequence
|
|
598
|
+
// chained on it keeps its clock.
|
|
599
|
+
Object.defineProperty(document, 'timeline', {
|
|
600
|
+
configurable: true,
|
|
601
|
+
get: () => ({currentTime: clock}),
|
|
602
|
+
});
|
|
603
|
+
try {
|
|
604
|
+
const {animator} = setup();
|
|
605
|
+
clock = 1005;
|
|
606
|
+
const handle = animator.animate({x: 300}, {spring: springs.settle});
|
|
607
|
+
const [animation] = live('translate');
|
|
608
|
+
clock = 1010;
|
|
609
|
+
animation.startTime = 1000;
|
|
610
|
+
await commitFrame(1016);
|
|
611
|
+
await expect(handle.started).resolves.toBe(1000);
|
|
612
|
+
expect(animation.startTime).toBe(1000);
|
|
613
|
+
} finally {
|
|
614
|
+
Reflect.deleteProperty(document, 'timeline');
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
it('calls made in one task start together, whichever of them a block follows', async () => {
|
|
619
|
+
// One motion's parts are often separate calls in one task (a
|
|
620
|
+
// component's effect): a block measured per call would move the
|
|
621
|
+
// earlier calls past it and leave a later one on the browser's start,
|
|
622
|
+
// splitting the parts by a frame.
|
|
623
|
+
Object.defineProperty(document, 'timeline', {
|
|
624
|
+
configurable: true,
|
|
625
|
+
get: () => ({currentTime: clock}),
|
|
626
|
+
});
|
|
627
|
+
try {
|
|
628
|
+
const {animator} = setup();
|
|
629
|
+
const first = animator.animate({x: 300}, {spring: springs.settle});
|
|
630
|
+
clock = 1030;
|
|
631
|
+
const second = animator.animate({opacity: 0.5}, {spring: springs.settle});
|
|
632
|
+
clock = 1040;
|
|
633
|
+
for (const animation of recorded) {
|
|
634
|
+
animation.startTime = 1000;
|
|
635
|
+
}
|
|
636
|
+
await commitFrame(1040);
|
|
637
|
+
const [a, b] = await Promise.all([first.started, second.started]);
|
|
638
|
+
expect(a).toBe(b);
|
|
639
|
+
} finally {
|
|
640
|
+
Reflect.deleteProperty(document, 'timeline');
|
|
641
|
+
}
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
it('a block within two frames of the call keeps the browser\u2019s start', async () => {
|
|
645
|
+
// Browser start times are frame times and the task's end a live clock: a
|
|
646
|
+
// 20 ms block cannot be told apart from that quantization, so the start
|
|
647
|
+
// stays where the browser put it.
|
|
648
|
+
Object.defineProperty(document, 'timeline', {
|
|
649
|
+
configurable: true,
|
|
650
|
+
get: () => ({currentTime: clock}),
|
|
651
|
+
});
|
|
652
|
+
try {
|
|
653
|
+
const {animator} = setup();
|
|
654
|
+
const handle = animator.animate({x: 300}, {spring: springs.settle});
|
|
655
|
+
const [animation] = live('translate');
|
|
656
|
+
// The task blocks for 20 ms; the browser dates the animation at 1000.
|
|
657
|
+
clock = 1020;
|
|
658
|
+
animation.startTime = 1000;
|
|
659
|
+
await commitFrame(1020);
|
|
660
|
+
await expect(handle.started).resolves.toBe(1000);
|
|
661
|
+
expect(animation.startTime).toBe(1000);
|
|
662
|
+
} finally {
|
|
663
|
+
Reflect.deleteProperty(document, 'timeline');
|
|
664
|
+
}
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
it('a start the browser dates by the call stays when its own task ended at once, however late the first read', async () => {
|
|
668
|
+
// WebKit under load from other tasks: the call's task ends at once, and
|
|
669
|
+
// another task holds the main thread before the first frame. Nothing in
|
|
670
|
+
// the call's task delayed it, so its start stays the browser's and the
|
|
671
|
+
// calls a sequence chains on it keep their clock (design:reduced-motion
|
|
672
|
+
// DR2's 200 ms cross-fade, a cancel fading on the previous fade's end).
|
|
673
|
+
Object.defineProperty(document, 'timeline', {
|
|
674
|
+
configurable: true,
|
|
675
|
+
get: () => ({currentTime: clock}),
|
|
676
|
+
});
|
|
677
|
+
try {
|
|
678
|
+
const {animator} = setup();
|
|
679
|
+
const handle = animator.animate({x: 300}, {spring: springs.settle});
|
|
680
|
+
const [animation] = live('translate');
|
|
681
|
+
// The call's task ends; then another task runs for 100 ms, and the
|
|
682
|
+
// browser commits the animation after it, dated at the call.
|
|
683
|
+
await Promise.resolve();
|
|
684
|
+
clock = 1100;
|
|
685
|
+
animation.startTime = 1000;
|
|
686
|
+
await commitFrame(1100);
|
|
687
|
+
await expect(handle.started).resolves.toBe(1000);
|
|
688
|
+
expect(animation.startTime).toBe(1000);
|
|
689
|
+
} finally {
|
|
690
|
+
Reflect.deleteProperty(document, 'timeline');
|
|
691
|
+
}
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
it('a start read after the first frame stays the browser\u2019s', async () => {
|
|
695
|
+
Object.defineProperty(document, 'timeline', {
|
|
696
|
+
configurable: true,
|
|
697
|
+
get: () => ({currentTime: clock}),
|
|
698
|
+
});
|
|
699
|
+
try {
|
|
700
|
+
const {animator} = setup();
|
|
701
|
+
const handle = animator.animate({x: 300}, {spring: springs.settle});
|
|
702
|
+
const [animation] = live('translate');
|
|
703
|
+
// Paused before its first frame (a harness), then started late: the
|
|
704
|
+
// first frame passed with no start to read.
|
|
705
|
+
animation.paused = true;
|
|
706
|
+
await commitFrame(1016);
|
|
707
|
+
clock = 1600;
|
|
708
|
+
animation.paused = false;
|
|
709
|
+
animation.startTime = 1000;
|
|
710
|
+
await commitFrame(1600);
|
|
711
|
+
await expect(handle.started).resolves.toBe(1000);
|
|
712
|
+
expect(animation.startTime).toBe(1000);
|
|
713
|
+
} finally {
|
|
714
|
+
Reflect.deleteProperty(document, 'timeline');
|
|
715
|
+
}
|
|
716
|
+
});
|
|
717
|
+
|
|
453
718
|
it('holds its start until the browser starts it', async () => {
|
|
454
719
|
const {animator} = setup();
|
|
455
720
|
animator.animate({x: 300}, {spring: springs.settle});
|
|
@@ -793,12 +1058,373 @@ describe('review fixes', () => {
|
|
|
793
1058
|
});
|
|
794
1059
|
});
|
|
795
1060
|
|
|
1061
|
+
describe('additive: a retarget adds to the curve on screen', () => {
|
|
1062
|
+
const old = solveSpring(toSpring(springs.settle), {from: 0, to: 300});
|
|
1063
|
+
const oldEnd = old.settleTime(0.05);
|
|
1064
|
+
const oldAt = (t: number) => (t >= oldEnd ? 300 : old.value(t));
|
|
1065
|
+
|
|
1066
|
+
it('starts from the old curve\u2019s value and velocity, and reads as their sum', async () => {
|
|
1067
|
+
const {animator} = setup();
|
|
1068
|
+
animator.animate({x: 300}, {spring: springs.settle});
|
|
1069
|
+
await commitFrame(1000);
|
|
1070
|
+
clock = 1150;
|
|
1071
|
+
const x = animator.get('x');
|
|
1072
|
+
const v = animator.getVelocity('x');
|
|
1073
|
+
animator.animate({x: 100}, {spring: springs.settle}, {additive: true});
|
|
1074
|
+
expect(animator.get('x')).toBeCloseTo(x, 9);
|
|
1075
|
+
expect(animator.getVelocity('x')).toBeCloseTo(v, 6);
|
|
1076
|
+
const delta = solveSpring(toSpring(springs.settle), {from: 0, to: -200});
|
|
1077
|
+
for (const t of [0.02, 0.1, 0.4]) {
|
|
1078
|
+
expect(animator.get('x', 1150 + t * 1000)).toBeCloseTo(
|
|
1079
|
+
oldAt(0.15 + t) + delta.value(t),
|
|
1080
|
+
6,
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1083
|
+
await advance(10_000);
|
|
1084
|
+
expect(animator.get('x')).toBe(100);
|
|
1085
|
+
});
|
|
1086
|
+
|
|
1087
|
+
it('a played reversal keeps the old velocity: a late first frame shows only the reversal\u2019s own start', async () => {
|
|
1088
|
+
// An ease-in-out reversal starts at rest; replacing a curve that moves
|
|
1089
|
+
// at v puts the old curve v·d ahead of it by a first frame d ms late.
|
|
1090
|
+
const ease = cubicBezier(timing.curves.move);
|
|
1091
|
+
const reversal = (from: number, to: number) => ({
|
|
1092
|
+
duration: 0.35,
|
|
1093
|
+
at: (t: number) => from + (to - from) * ease(Math.min(1, t / 0.35)),
|
|
1094
|
+
velocity: (t: number) =>
|
|
1095
|
+
((to - from) / 0.35) *
|
|
1096
|
+
((ease(Math.min(1, t / 0.35 + 1e-4)) -
|
|
1097
|
+
ease(Math.max(0, t / 0.35 - 1e-4))) /
|
|
1098
|
+
2e-4),
|
|
1099
|
+
});
|
|
1100
|
+
const steps = async (additive: boolean) => {
|
|
1101
|
+
const {animator} = setup();
|
|
1102
|
+
animator.animate({x: 300}, {spring: springs.settle}, {at: 1000});
|
|
1103
|
+
clock = 1120;
|
|
1104
|
+
const from = animator.get('x');
|
|
1105
|
+
animator.play({x: reversal(from, 0)}, {at: 1120, additive});
|
|
1106
|
+
return [30, 60, 100].map(d =>
|
|
1107
|
+
Math.abs(animator.get('x', 1120 + d) - oldAt((120 + d) / 1000)),
|
|
1108
|
+
);
|
|
1109
|
+
};
|
|
1110
|
+
const replaced = await steps(false);
|
|
1111
|
+
const added = await steps(true);
|
|
1112
|
+
for (let i = 0; i < added.length; i++) {
|
|
1113
|
+
expect(added[i]).toBeLessThan(replaced[i] / 2);
|
|
1114
|
+
}
|
|
1115
|
+
// What remains is the reversal's own first d ms, scaled to the targets.
|
|
1116
|
+
expect(added[0]).toBeCloseTo(300 * ease(30 / 350), 6);
|
|
1117
|
+
});
|
|
1118
|
+
|
|
1119
|
+
it('nests: a second retarget adds to the first', async () => {
|
|
1120
|
+
const {animator} = setup();
|
|
1121
|
+
animator.animate({x: 300}, {spring: springs.settle});
|
|
1122
|
+
await commitFrame(1000);
|
|
1123
|
+
clock = 1100;
|
|
1124
|
+
animator.animate({x: 100}, {spring: springs.settle}, {additive: true});
|
|
1125
|
+
clock = 1200;
|
|
1126
|
+
const x = animator.get('x');
|
|
1127
|
+
const v = animator.getVelocity('x');
|
|
1128
|
+
animator.animate({x: 250}, {spring: springs.settle}, {additive: true});
|
|
1129
|
+
expect(animator.get('x')).toBeCloseTo(x, 9);
|
|
1130
|
+
expect(animator.getVelocity('x')).toBeCloseTo(v, 6);
|
|
1131
|
+
const first = solveSpring(toSpring(springs.settle), {from: 0, to: -200});
|
|
1132
|
+
const second = solveSpring(toSpring(springs.settle), {from: 0, to: 150});
|
|
1133
|
+
expect(animator.get('x', 1300)).toBeCloseTo(
|
|
1134
|
+
oldAt(0.3) + first.value(0.2) + second.value(0.1),
|
|
1135
|
+
6,
|
|
1136
|
+
);
|
|
1137
|
+
await advance(10_000);
|
|
1138
|
+
expect(animator.get('x')).toBe(250);
|
|
1139
|
+
});
|
|
1140
|
+
|
|
1141
|
+
it('replaces as before for a property at rest or given a velocity', async () => {
|
|
1142
|
+
const {animator} = setup();
|
|
1143
|
+
animator.animate({x: 300}, {spring: springs.settle}, {additive: true});
|
|
1144
|
+
await commitFrame(1000);
|
|
1145
|
+
const fromRest = solveSpring(toSpring(springs.settle), {from: 0, to: 300});
|
|
1146
|
+
expect(animator.get('x', 1100)).toBeCloseTo(fromRest.value(0.1), 9);
|
|
1147
|
+
clock = 1100;
|
|
1148
|
+
const x = animator.get('x');
|
|
1149
|
+
animator.animate(
|
|
1150
|
+
{x: 0},
|
|
1151
|
+
{spring: springs.settle, velocity: {x: 0}},
|
|
1152
|
+
{additive: true},
|
|
1153
|
+
);
|
|
1154
|
+
const back = solveSpring(toSpring(springs.settle), {from: x, to: 0});
|
|
1155
|
+
expect(animator.get('x', 1200)).toBeCloseTo(back.value(0.1), 9);
|
|
1156
|
+
});
|
|
1157
|
+
});
|
|
1158
|
+
|
|
1159
|
+
describe('start: when a call\u2019s animations start', () => {
|
|
1160
|
+
it('presented: leaves the element at rest, and creates the animations two frames later', async () => {
|
|
1161
|
+
const {animator, element} = setup();
|
|
1162
|
+
animator.set({x: 20});
|
|
1163
|
+
// The set's own promotion ends first.
|
|
1164
|
+
await vi.advanceTimersByTimeAsync(PROMOTION_LINGER_MS + 1);
|
|
1165
|
+
expect(element.style.willChange).toBe('');
|
|
1166
|
+
const handle = animator.animate(
|
|
1167
|
+
{x: 100},
|
|
1168
|
+
{spring: springs.settle},
|
|
1169
|
+
{start: 'presented'},
|
|
1170
|
+
);
|
|
1171
|
+
// At rest, with no layer yet: the next frame commits it where it is.
|
|
1172
|
+
// (A layer at rest for two frames left a stale paint in WebKit on iOS.)
|
|
1173
|
+
expect(live('translate')).toHaveLength(0);
|
|
1174
|
+
expect(element.style.translate).toBe('20px 0px');
|
|
1175
|
+
expect(element.style.willChange).toBe('');
|
|
1176
|
+
expect(animator.get('x')).toBe(20);
|
|
1177
|
+
clock = 1016;
|
|
1178
|
+
await commitFrame(1016);
|
|
1179
|
+
expect(live('translate')).toHaveLength(0);
|
|
1180
|
+
// The frame after it creates them; the browser starts them at its commit.
|
|
1181
|
+
clock = 1033;
|
|
1182
|
+
await commitFrame(1033);
|
|
1183
|
+
expect(live('translate')).toHaveLength(1);
|
|
1184
|
+
expect(element.style.willChange).toContain('translate');
|
|
1185
|
+
clock = 1050;
|
|
1186
|
+
await commitFrame(1050);
|
|
1187
|
+
await expect(handle.started).resolves.toBe(1050);
|
|
1188
|
+
const curve = solveSpring(toSpring(springs.settle), {from: 20, to: 100});
|
|
1189
|
+
expect(animator.get('x', 1150)).toBeCloseTo(curve.value(0.1), 9);
|
|
1190
|
+
expect(sampleAt(live('translate')[0], 'translate', 1150)[0]).toBeCloseTo(
|
|
1191
|
+
curve.value(0.1),
|
|
1192
|
+
0,
|
|
1193
|
+
);
|
|
1194
|
+
});
|
|
1195
|
+
|
|
1196
|
+
it('presented: a start WebKit dates at the frame that creates the animations keeps it, wherever in its frame the call was made', async () => {
|
|
1197
|
+
// A presented call's animations are created in its second frame's
|
|
1198
|
+
// callback, so its task is that callback's, not the call's: measured
|
|
1199
|
+
// from the call, a call made early in its frame looked like a block
|
|
1200
|
+
// longer than two frames, and a late first read moved the start.
|
|
1201
|
+
Object.defineProperty(document, 'timeline', {
|
|
1202
|
+
configurable: true,
|
|
1203
|
+
get: () => ({currentTime: clock}),
|
|
1204
|
+
});
|
|
1205
|
+
try {
|
|
1206
|
+
const {animator} = setup();
|
|
1207
|
+
const handle = animator.animate(
|
|
1208
|
+
{x: 100},
|
|
1209
|
+
{spring: springs.settle},
|
|
1210
|
+
{start: 'presented'},
|
|
1211
|
+
);
|
|
1212
|
+
clock = 1016;
|
|
1213
|
+
await commitFrame(1016);
|
|
1214
|
+
// The frame that creates the animations; the callback's task runs on
|
|
1215
|
+
// 3 ms.
|
|
1216
|
+
requestFrame(() => {
|
|
1217
|
+
clock = 1036;
|
|
1218
|
+
});
|
|
1219
|
+
clock = 1033;
|
|
1220
|
+
await commitFrame(1033);
|
|
1221
|
+
expect(live('translate')).toHaveLength(1);
|
|
1222
|
+
// Other tasks hold the main thread until the next frame, where WebKit's
|
|
1223
|
+
// start for them, that frame's time, is first read.
|
|
1224
|
+
clock = 1090;
|
|
1225
|
+
live('translate')[0].startTime = 1033;
|
|
1226
|
+
await commitFrame(1090);
|
|
1227
|
+
await expect(handle.started).resolves.toBe(1033);
|
|
1228
|
+
expect(live('translate')[0].startTime).toBe(1033);
|
|
1229
|
+
} finally {
|
|
1230
|
+
Reflect.deleteProperty(document, 'timeline');
|
|
1231
|
+
}
|
|
1232
|
+
});
|
|
1233
|
+
|
|
1234
|
+
it('presented: a block in the frame that creates the animations moves their start past it', async () => {
|
|
1235
|
+
Object.defineProperty(document, 'timeline', {
|
|
1236
|
+
configurable: true,
|
|
1237
|
+
get: () => ({currentTime: clock}),
|
|
1238
|
+
});
|
|
1239
|
+
try {
|
|
1240
|
+
const {animator} = setup();
|
|
1241
|
+
const handle = animator.animate(
|
|
1242
|
+
{x: 100},
|
|
1243
|
+
{spring: springs.settle},
|
|
1244
|
+
{start: 'presented'},
|
|
1245
|
+
);
|
|
1246
|
+
clock = 1016;
|
|
1247
|
+
await commitFrame(1016);
|
|
1248
|
+
// The frame that creates the animations blocks for 300 ms; WebKit's
|
|
1249
|
+
// start for them, that frame's time, is read at the next frame.
|
|
1250
|
+
requestFrame(() => {
|
|
1251
|
+
clock = 1333;
|
|
1252
|
+
});
|
|
1253
|
+
clock = 1033;
|
|
1254
|
+
await commitFrame(1033);
|
|
1255
|
+
clock = 1340;
|
|
1256
|
+
live('translate')[0].startTime = 1033;
|
|
1257
|
+
await commitFrame(1340);
|
|
1258
|
+
await expect(handle.started).resolves.toBe(1340);
|
|
1259
|
+
expect(live('translate')[0].startTime).toBe(1340);
|
|
1260
|
+
} finally {
|
|
1261
|
+
Reflect.deleteProperty(document, 'timeline');
|
|
1262
|
+
}
|
|
1263
|
+
});
|
|
1264
|
+
|
|
1265
|
+
it('presented: a call queued after another change flushed the queue still waits its own two frames', async () => {
|
|
1266
|
+
const {animator} = setup();
|
|
1267
|
+
animator.animate({x: 100}, {spring: springs.settle}, {start: 'presented'});
|
|
1268
|
+
// Another change creates the waiting call's animations first.
|
|
1269
|
+
animator.animate({y: 50}, {spring: springs.settle});
|
|
1270
|
+
clock = 1016;
|
|
1271
|
+
await commitFrame(1016);
|
|
1272
|
+
animator.animate(
|
|
1273
|
+
{opacity: 0.5},
|
|
1274
|
+
{spring: springs.settle},
|
|
1275
|
+
{start: 'presented'},
|
|
1276
|
+
);
|
|
1277
|
+
expect(live('opacity')).toHaveLength(0);
|
|
1278
|
+
// The first call's second frame is this call's first: it still waits.
|
|
1279
|
+
clock = 1033;
|
|
1280
|
+
await commitFrame(1033);
|
|
1281
|
+
expect(live('opacity')).toHaveLength(0);
|
|
1282
|
+
clock = 1050;
|
|
1283
|
+
await commitFrame(1050);
|
|
1284
|
+
expect(live('opacity')).toHaveLength(1);
|
|
1285
|
+
});
|
|
1286
|
+
|
|
1287
|
+
it('presented: a second presented call on the same property a frame later keeps its own two frames', async () => {
|
|
1288
|
+
const {animator} = setup();
|
|
1289
|
+
animator.animate({x: 100}, {spring: springs.settle}, {start: 'presented'});
|
|
1290
|
+
clock = 1016;
|
|
1291
|
+
await commitFrame(1016);
|
|
1292
|
+
animator.animate({x: 200}, {spring: springs.settle}, {start: 'presented'});
|
|
1293
|
+
// The first call's second frame: its property is now the second call's,
|
|
1294
|
+
// so nothing starts yet.
|
|
1295
|
+
clock = 1033;
|
|
1296
|
+
await commitFrame(1033);
|
|
1297
|
+
expect(live('translate')).toHaveLength(0);
|
|
1298
|
+
// The second call's own second frame.
|
|
1299
|
+
clock = 1050;
|
|
1300
|
+
await commitFrame(1050);
|
|
1301
|
+
expect(live('translate')).toHaveLength(1);
|
|
1302
|
+
await commitFrame(1066);
|
|
1303
|
+
expect(animator.get('x', 1066 + 5000)).toBeCloseTo(200, 3);
|
|
1304
|
+
});
|
|
1305
|
+
|
|
1306
|
+
it('presented: a later presented call that takes part of a waiting call\u2019s channel starts the rest of it, from its own start', async () => {
|
|
1307
|
+
const {animator} = setup();
|
|
1308
|
+
const first = animator.animate(
|
|
1309
|
+
{x: 100, y: 100},
|
|
1310
|
+
{spring: springs.settle},
|
|
1311
|
+
{start: 'presented'},
|
|
1312
|
+
);
|
|
1313
|
+
clock = 1016;
|
|
1314
|
+
await commitFrame(1016);
|
|
1315
|
+
animator.animate({x: 200}, {spring: springs.settle}, {start: 'presented'});
|
|
1316
|
+
// The first call's second frame: its channel now holds the second call's
|
|
1317
|
+
// curve, so nothing starts yet.
|
|
1318
|
+
clock = 1033;
|
|
1319
|
+
await commitFrame(1033);
|
|
1320
|
+
expect(live('translate')).toHaveLength(0);
|
|
1321
|
+
// The second call's second frame creates the channel, with the first
|
|
1322
|
+
// call's y in it; the browser starts it at the next commit.
|
|
1323
|
+
clock = 1050;
|
|
1324
|
+
await commitFrame(1050);
|
|
1325
|
+
expect(live('translate')).toHaveLength(1);
|
|
1326
|
+
clock = 1066;
|
|
1327
|
+
await commitFrame(1066);
|
|
1328
|
+
await expect(first.started).resolves.toBe(1066);
|
|
1329
|
+
// y was at rest on screen until then: its curve starts there, and the
|
|
1330
|
+
// model reads what the screen shows.
|
|
1331
|
+
const y = solveSpring(toSpring(springs.settle), {from: 0, to: 100});
|
|
1332
|
+
expect(animator.get('y', 1166)).toBeCloseTo(y.value(0.1), 6);
|
|
1333
|
+
expect(sampleAt(live('translate')[0], 'translate', 1166)[1]).toBeCloseTo(
|
|
1334
|
+
y.value(0.1),
|
|
1335
|
+
0,
|
|
1336
|
+
);
|
|
1337
|
+
});
|
|
1338
|
+
|
|
1339
|
+
it('presented: another change before then creates the animations first', async () => {
|
|
1340
|
+
const {animator} = setup();
|
|
1341
|
+
animator.animate({x: 100}, {spring: springs.settle}, {start: 'presented'});
|
|
1342
|
+
animator.animate({y: 50}, {spring: springs.settle});
|
|
1343
|
+
expect(live('translate').length).toBeGreaterThan(0);
|
|
1344
|
+
await commitFrame(clock);
|
|
1345
|
+
const x = solveSpring(toSpring(springs.settle), {from: 0, to: 100});
|
|
1346
|
+
expect(animator.get('x', clock + 100)).toBeCloseTo(x.value(0.1), 9);
|
|
1347
|
+
});
|
|
1348
|
+
|
|
1349
|
+
it('presented: a retarget of a moving property starts now, from the value and velocity on screen (FR6)', async () => {
|
|
1350
|
+
const {animator} = setup();
|
|
1351
|
+
animator.animate({x: 300}, {spring: springs.settle});
|
|
1352
|
+
await commitFrame(1000);
|
|
1353
|
+
clock = 1100;
|
|
1354
|
+
const from = animator.get('x');
|
|
1355
|
+
const velocity = animator.getVelocity('x');
|
|
1356
|
+
animator.animate({x: 0}, {spring: springs.settle}, {start: 'presented'});
|
|
1357
|
+
// The element is moving, not at rest: no two frames on the old curve and
|
|
1358
|
+
// no restart at the commit from the call's value. The new curve starts at
|
|
1359
|
+
// the call, from what is on screen, as a retarget does.
|
|
1360
|
+
const [animation] = live('translate');
|
|
1361
|
+
expect(animation.startTime).toBe(1100);
|
|
1362
|
+
expect(animator.get('x', 1100)).toBeCloseTo(from, 6);
|
|
1363
|
+
expect(animator.getVelocity('x', 1100)).toBeCloseTo(velocity, 3);
|
|
1364
|
+
const back = solveSpring(toSpring(springs.settle), {
|
|
1365
|
+
from,
|
|
1366
|
+
to: 0,
|
|
1367
|
+
velocity,
|
|
1368
|
+
});
|
|
1369
|
+
expect(animator.get('x', 1200)).toBeCloseTo(back.value(0.1), 6);
|
|
1370
|
+
});
|
|
1371
|
+
|
|
1372
|
+
it('commit: a retarget is dated by the browser\u2019s commit, from the value on screen', async () => {
|
|
1373
|
+
const {animator} = setup();
|
|
1374
|
+
animator.animate({x: 300}, {spring: springs.settle});
|
|
1375
|
+
await commitFrame(1000);
|
|
1376
|
+
clock = 1100;
|
|
1377
|
+
const from = animator.get('x');
|
|
1378
|
+
const velocity = animator.getVelocity('x');
|
|
1379
|
+
const handle = animator.animate(
|
|
1380
|
+
{x: 0},
|
|
1381
|
+
{spring: springs.settle},
|
|
1382
|
+
{start: 'commit'},
|
|
1383
|
+
);
|
|
1384
|
+
const [animation] = live('translate');
|
|
1385
|
+
expect(animation.startTime).toBeNull();
|
|
1386
|
+
// Held where it is until the browser starts it.
|
|
1387
|
+
expect(animator.get('x')).toBeCloseTo(from, 9);
|
|
1388
|
+
clock = 1130;
|
|
1389
|
+
await commitFrame(1130);
|
|
1390
|
+
await expect(handle.started).resolves.toBe(1130);
|
|
1391
|
+
const back = solveSpring(toSpring(springs.settle), {
|
|
1392
|
+
from,
|
|
1393
|
+
to: 0,
|
|
1394
|
+
velocity,
|
|
1395
|
+
});
|
|
1396
|
+
expect(animator.get('x', 1230)).toBeCloseTo(back.value(0.1), 6);
|
|
1397
|
+
});
|
|
1398
|
+
|
|
1399
|
+
it('a handle hands out the curve it plays, for a companion on the same clock', async () => {
|
|
1400
|
+
const {animator} = setup();
|
|
1401
|
+
const handle = animator.animate({y: 400}, {spring: springs.sheetPresent});
|
|
1402
|
+
const curve = handle.curve('y');
|
|
1403
|
+
expect(curve).not.toBeNull();
|
|
1404
|
+
expect(handle.curve('x')).toBeNull();
|
|
1405
|
+
await commitFrame(1000);
|
|
1406
|
+
const y = solveSpring(toSpring(springs.sheetPresent), {from: 0, to: 400});
|
|
1407
|
+
for (const t of [0, 0.05, 0.2]) {
|
|
1408
|
+
expect(curve?.at(t)).toBeCloseTo(y.value(t), 9);
|
|
1409
|
+
expect(curve?.velocity(t)).toBeCloseTo(y.velocity(t), 6);
|
|
1410
|
+
expect(animator.get('y', 1000 + t * 1000)).toBeCloseTo(y.value(t), 9);
|
|
1411
|
+
}
|
|
1412
|
+
expect(curve?.at(curve.duration)).toBe(400);
|
|
1413
|
+
});
|
|
1414
|
+
});
|
|
1415
|
+
|
|
796
1416
|
describe('animatorFor: one animator per element', () => {
|
|
797
1417
|
it('gives every caller the same animator for an element', () => {
|
|
798
1418
|
const a = document.createElement('div');
|
|
799
1419
|
const b = document.createElement('div');
|
|
800
1420
|
expect(animatorFor(a)).toBe(animatorFor(a));
|
|
801
1421
|
expect(animatorFor(a)).not.toBe(animatorFor(b));
|
|
1422
|
+
expect(animatorFor(a, {pseudoElement: '::backdrop'})).toBe(
|
|
1423
|
+
animatorFor(a, {pseudoElement: '::backdrop'}),
|
|
1424
|
+
);
|
|
1425
|
+
expect(animatorFor(a, {pseudoElement: '::backdrop'})).not.toBe(
|
|
1426
|
+
animatorFor(a),
|
|
1427
|
+
);
|
|
802
1428
|
});
|
|
803
1429
|
|
|
804
1430
|
it('composes two callers’ axes in one translate track', async () => {
|
|
@@ -820,8 +1446,8 @@ describe('animatorFor: one animator per element', () => {
|
|
|
820
1446
|
const y = solveSpring(toSpring(springs.settle), {from: 0, to: -44});
|
|
821
1447
|
expect(swipe.get('y', clock + 30)).toBeCloseTo(y.value(0.03), 9);
|
|
822
1448
|
const [tx, ty] = sampleAt(tracks[0], 'translate', clock + 30);
|
|
823
|
-
expect(
|
|
824
|
-
expect(
|
|
1449
|
+
expect(Math.abs(tx - x.value(0.08))).toBeLessThan(0.26);
|
|
1450
|
+
expect(Math.abs(ty - y.value(0.03))).toBeLessThan(0.26);
|
|
825
1451
|
});
|
|
826
1452
|
});
|
|
827
1453
|
|
|
@@ -890,6 +1516,74 @@ describe('promotion: a compositing layer while it moves', () => {
|
|
|
890
1516
|
expect(element.style.willChange).toBe('');
|
|
891
1517
|
});
|
|
892
1518
|
|
|
1519
|
+
it("reads the stylesheet's will-change in a task of its own, so a promotion reads no style in its own task", async () => {
|
|
1520
|
+
// A drag's first move promotes: reading the stylesheet's reasons then
|
|
1521
|
+
// would force a style recalculation in the input handler.
|
|
1522
|
+
stylesheet['will-change'] = 'transform, opacity';
|
|
1523
|
+
const {animator, element} = setup();
|
|
1524
|
+
// In the document, where the read happens.
|
|
1525
|
+
document.body.append(element);
|
|
1526
|
+
await commitFrame(clock);
|
|
1527
|
+
await vi.advanceTimersByTimeAsync(0);
|
|
1528
|
+
const readsOf = () =>
|
|
1529
|
+
vi
|
|
1530
|
+
.mocked(window.getComputedStyle)
|
|
1531
|
+
.mock.calls.filter(([read]) => read === element).length;
|
|
1532
|
+
const before = readsOf();
|
|
1533
|
+
animator.set({y: 10});
|
|
1534
|
+
expect(readsOf()).toBe(before);
|
|
1535
|
+
expect(element.style.willChange).toBe('transform, opacity, translate');
|
|
1536
|
+
await advance(PROMOTION_LINGER_MS + 10);
|
|
1537
|
+
expect(element.style.willChange).toBe('');
|
|
1538
|
+
element.remove();
|
|
1539
|
+
});
|
|
1540
|
+
|
|
1541
|
+
it('reads no style for a node out of the document: a node removed before its read is never read', async () => {
|
|
1542
|
+
// A shared animator (animatorFor) is never detached: its node can leave
|
|
1543
|
+
// the document with the read still waiting. A node out of the document
|
|
1544
|
+
// has no style to read, and reading one costs a style pass for nothing.
|
|
1545
|
+
stubMedia(false, false);
|
|
1546
|
+
const readsOf = (node: Element) =>
|
|
1547
|
+
vi
|
|
1548
|
+
.mocked(window.getComputedStyle)
|
|
1549
|
+
.mock.calls.filter(([read]) => read === node).length;
|
|
1550
|
+
const removed = document.createElement('div');
|
|
1551
|
+
document.body.append(removed);
|
|
1552
|
+
createAnimator(removed, {now: () => clock, requestFrame});
|
|
1553
|
+
removed.remove();
|
|
1554
|
+
const kept = document.createElement('div');
|
|
1555
|
+
document.body.append(kept);
|
|
1556
|
+
createAnimator(kept, {now: () => clock, requestFrame});
|
|
1557
|
+
await vi.advanceTimersByTimeAsync(0);
|
|
1558
|
+
expect(readsOf(removed)).toBe(0);
|
|
1559
|
+
// A node still in the document is read, once, in that task.
|
|
1560
|
+
expect(readsOf(kept)).toBe(1);
|
|
1561
|
+
kept.remove();
|
|
1562
|
+
});
|
|
1563
|
+
|
|
1564
|
+
it("keeps the stylesheet's will-change when a node read nothing before it moves", () => {
|
|
1565
|
+
// Put back after its read was skipped, a node reads its stylesheet's
|
|
1566
|
+
// reasons when a promotion needs them.
|
|
1567
|
+
stylesheet['will-change'] = 'transform, opacity';
|
|
1568
|
+
stubMedia(false, false);
|
|
1569
|
+
const element = document.createElement('div');
|
|
1570
|
+
const animator = createAnimator(element, {
|
|
1571
|
+
now: () => clock,
|
|
1572
|
+
requestFrame,
|
|
1573
|
+
});
|
|
1574
|
+
document.body.append(element);
|
|
1575
|
+
animator.set({y: 10});
|
|
1576
|
+
expect(element.style.willChange).toBe('transform, opacity, translate');
|
|
1577
|
+
element.remove();
|
|
1578
|
+
});
|
|
1579
|
+
|
|
1580
|
+
it("keeps the stylesheet's will-change when an element attached in this task moves at once", () => {
|
|
1581
|
+
stylesheet['will-change'] = 'transform, opacity';
|
|
1582
|
+
const {animator, element} = setup();
|
|
1583
|
+
animator.animate({y: 100}, {spring: springs.settle});
|
|
1584
|
+
expect(element.style.willChange).toBe('transform, opacity, translate');
|
|
1585
|
+
});
|
|
1586
|
+
|
|
893
1587
|
it('promotes during direct manipulation, until the writes stop', async () => {
|
|
894
1588
|
const {animator, element} = setup();
|
|
895
1589
|
animator.set({y: 10});
|
|
@@ -902,6 +1596,52 @@ describe('promotion: a compositing layer while it moves', () => {
|
|
|
902
1596
|
expect(element.style.willChange).toBe('');
|
|
903
1597
|
});
|
|
904
1598
|
|
|
1599
|
+
it('set with promote: false writes the values without a layer, and leaves other promotion alone', async () => {
|
|
1600
|
+
// A one-off switch (a veil under a sticky header during a programmatic
|
|
1601
|
+
// scroll) must not give its element a layer.
|
|
1602
|
+
const {animator, element} = setup();
|
|
1603
|
+
animator.set({opacity: 0.5}, {promote: false});
|
|
1604
|
+
expect(element.style.opacity).toBe('0.5');
|
|
1605
|
+
expect(element.style.willChange).toBe('');
|
|
1606
|
+
await advance(PROMOTION_LINGER_MS + 10);
|
|
1607
|
+
expect(element.style.willChange).toBe('');
|
|
1608
|
+
|
|
1609
|
+
// A channel a running motion promoted stays promoted, on its own clock.
|
|
1610
|
+
const moving = setup();
|
|
1611
|
+
moving.animator.animate({x: 120}, {spring: springs.settle});
|
|
1612
|
+
expect(moving.element.style.willChange).toBe('translate');
|
|
1613
|
+
moving.animator.set({opacity: 0.25}, {promote: false});
|
|
1614
|
+
expect(moving.element.style.opacity).toBe('0.25');
|
|
1615
|
+
expect(moving.element.style.willChange).toBe('translate');
|
|
1616
|
+
await advance(5000 + PROMOTION_LINGER_MS);
|
|
1617
|
+
expect(moving.element.style.willChange).toBe('');
|
|
1618
|
+
|
|
1619
|
+
// A channel with other motion still running gets its animation's layer.
|
|
1620
|
+
const shared = setup();
|
|
1621
|
+
shared.animator.animate({y: 80}, {spring: springs.settle});
|
|
1622
|
+
shared.animator.set({x: 10}, {promote: false});
|
|
1623
|
+
expect(live('translate').length).toBeGreaterThan(0);
|
|
1624
|
+
expect(shared.element.style.willChange).toBe('translate');
|
|
1625
|
+
});
|
|
1626
|
+
|
|
1627
|
+
it('promotes a pseudo-element through a custom property on its host', async () => {
|
|
1628
|
+
const {animator, element} = setup({pseudoElement: '::backdrop'});
|
|
1629
|
+
animator.set({opacity: 0});
|
|
1630
|
+
// set() promotes a pseudo-element too.
|
|
1631
|
+
expect(
|
|
1632
|
+
element.style.getPropertyValue('--astryx-motion-will-change-backdrop'),
|
|
1633
|
+
).toBe('opacity');
|
|
1634
|
+
animator.animate({opacity: 1}, {spring: springs.alertPresent});
|
|
1635
|
+
expect(
|
|
1636
|
+
element.style.getPropertyValue('--astryx-motion-will-change-backdrop'),
|
|
1637
|
+
).toBe('opacity');
|
|
1638
|
+
expect(element.style.willChange).toBe('');
|
|
1639
|
+
await advance(5000 + PROMOTION_LINGER_MS);
|
|
1640
|
+
expect(
|
|
1641
|
+
element.style.getPropertyValue('--astryx-motion-will-change-backdrop'),
|
|
1642
|
+
).toBe('');
|
|
1643
|
+
});
|
|
1644
|
+
|
|
905
1645
|
it('ends promotion on the element it leaves', () => {
|
|
906
1646
|
const {animator, element} = setup();
|
|
907
1647
|
animator.animate({x: 50}, {spring: springs.settle});
|
|
@@ -1000,10 +1740,12 @@ describe('presentation values, retargeting, velocity (spec:AST-094 FR6)', () =>
|
|
|
1000
1740
|
animator.animate({x: 0}, {spring: springs.settle});
|
|
1001
1741
|
const [animation] = live('translate');
|
|
1002
1742
|
for (let t = clock; t < clock + 400; t += 7) {
|
|
1003
|
-
expect(
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1743
|
+
expect(
|
|
1744
|
+
Math.abs(
|
|
1745
|
+
sampleAt(animation, 'translate', t)[1] -
|
|
1746
|
+
yCurve.value((t - 1000) / 1000),
|
|
1747
|
+
),
|
|
1748
|
+
).toBeLessThan(0.26);
|
|
1007
1749
|
expect(animator.get('y', t)).toBeCloseTo(
|
|
1008
1750
|
yCurve.value((t - 1000) / 1000),
|
|
1009
1751
|
9,
|
|
@@ -1098,6 +1840,52 @@ describe('finished', () => {
|
|
|
1098
1840
|
await expect(c.finished).resolves.toBe(false);
|
|
1099
1841
|
});
|
|
1100
1842
|
|
|
1843
|
+
it('waits while a sampler holds the animation past the curve\u2019s end', async () => {
|
|
1844
|
+
const {animator} = setup();
|
|
1845
|
+
const handle = animator.animate({x: 40}, {spring: springs.quickChange});
|
|
1846
|
+
let result: boolean | undefined;
|
|
1847
|
+
void handle.finished.then(value => {
|
|
1848
|
+
result = value;
|
|
1849
|
+
});
|
|
1850
|
+
await advance(16);
|
|
1851
|
+
// A harness pauses it (holdAnimations) and keeps it held well past the
|
|
1852
|
+
// end of the curve on the wall clock: the motion has not ended.
|
|
1853
|
+
const [animation] = live('translate');
|
|
1854
|
+
animation.paused = true;
|
|
1855
|
+
await advance(5000);
|
|
1856
|
+
expect(result).toBeUndefined();
|
|
1857
|
+
});
|
|
1858
|
+
|
|
1859
|
+
it('resolves true once a held animation resumes and reaches its end', async () => {
|
|
1860
|
+
const {animator} = setup();
|
|
1861
|
+
const handle = animator.animate({x: 40}, {spring: springs.quickChange});
|
|
1862
|
+
let result: boolean | undefined;
|
|
1863
|
+
void handle.finished.then(value => {
|
|
1864
|
+
result = value;
|
|
1865
|
+
});
|
|
1866
|
+
await advance(16);
|
|
1867
|
+
const [animation] = live('translate');
|
|
1868
|
+
animation.paused = true;
|
|
1869
|
+
await advance(5000);
|
|
1870
|
+
animation.paused = false;
|
|
1871
|
+
await advance(1000);
|
|
1872
|
+
expect(result).toBe(true);
|
|
1873
|
+
});
|
|
1874
|
+
|
|
1875
|
+
it('resolves on time for an animation nobody holds', async () => {
|
|
1876
|
+
const {animator} = setup();
|
|
1877
|
+
const handle = animator.animate({x: 40}, {spring: springs.quickChange});
|
|
1878
|
+
let result: boolean | undefined;
|
|
1879
|
+
void handle.finished.then(value => {
|
|
1880
|
+
result = value;
|
|
1881
|
+
});
|
|
1882
|
+
const end = (handle.curve('x')?.duration ?? NaN) * 1000;
|
|
1883
|
+
await advance(end - 50);
|
|
1884
|
+
expect(result).toBeUndefined();
|
|
1885
|
+
await advance(100);
|
|
1886
|
+
expect(result).toBe(true);
|
|
1887
|
+
});
|
|
1888
|
+
|
|
1101
1889
|
it('dispose cancels and settles everything', async () => {
|
|
1102
1890
|
const {animator} = setup();
|
|
1103
1891
|
const handle = animator.animate(
|
|
@@ -1127,6 +1915,64 @@ describe('Reduced Motion (design:reduced-motion)', () => {
|
|
|
1127
1915
|
),
|
|
1128
1916
|
);
|
|
1129
1917
|
|
|
1918
|
+
it('a call naming the opacity a running dip heads to keeps the dip', async () => {
|
|
1919
|
+
const {animator} = reduced();
|
|
1920
|
+
animator.animate({x: 100}, {spring: springs.navigationPush});
|
|
1921
|
+
await commitFrame(clock);
|
|
1922
|
+
const start = clock;
|
|
1923
|
+
await advance(fade / 4);
|
|
1924
|
+
// Mid fade out: a call names the opacity the dip returns to.
|
|
1925
|
+
const handle = animator.animate({opacity: 1}, {spring: springs.settle});
|
|
1926
|
+
// The dip goes on as it was: at its bottom when it was due, not opening
|
|
1927
|
+
// a second dip from where it had got to.
|
|
1928
|
+
expect(animator.get('opacity', start + fade / 2)).toBeCloseTo(0, 6);
|
|
1929
|
+
await advance(fade / 4 + 1);
|
|
1930
|
+
// Landed at the bottom; the fade back in ends when the dip was due to.
|
|
1931
|
+
expect(animator.get('x')).toBe(100);
|
|
1932
|
+
expect(animator.get('opacity', start + fade)).toBeCloseTo(1, 6);
|
|
1933
|
+
await advance(fade + END_HOLD_MS + 1);
|
|
1934
|
+
expect(animator.get('x')).toBe(100);
|
|
1935
|
+
expect(animator.get('opacity')).toBe(1);
|
|
1936
|
+
expect(await handle.finished).toBe(true);
|
|
1937
|
+
});
|
|
1938
|
+
|
|
1939
|
+
it('a call naming the opacity a dip heads to, in the same task as the dip, hands the browser no second fade', async () => {
|
|
1940
|
+
const {animator} = reduced();
|
|
1941
|
+
animator.animate({x: 100}, {spring: springs.navigationPush});
|
|
1942
|
+
animator.animate({opacity: 1}, {spring: springs.settle});
|
|
1943
|
+
await commitFrame(clock);
|
|
1944
|
+
const fades = recorded.filter(animation =>
|
|
1945
|
+
animation.keyframes.some(keyframe => 'opacity' in keyframe),
|
|
1946
|
+
);
|
|
1947
|
+
expect(fades).toHaveLength(1);
|
|
1948
|
+
expect(fades[0].cancelled).toBe(false);
|
|
1949
|
+
});
|
|
1950
|
+
|
|
1951
|
+
it('a release played as a dip, then its state layer naming the opacity the dip returns to, fades once', async () => {
|
|
1952
|
+
// A swipe row's release: the move's layer plays the travel, then the
|
|
1953
|
+
// state layer lands its opacity on the same element in the same task.
|
|
1954
|
+
const {animator} = reduced();
|
|
1955
|
+
const move = animator.play(
|
|
1956
|
+
{x: {duration: 0.3, at: t => (100 * t) / 0.3, velocity: () => 100 / 0.3}},
|
|
1957
|
+
{touchRelease: true},
|
|
1958
|
+
);
|
|
1959
|
+
const state = animator.animate(
|
|
1960
|
+
{opacity: 1},
|
|
1961
|
+
{spring: springs.settle},
|
|
1962
|
+
{touchRelease: true},
|
|
1963
|
+
);
|
|
1964
|
+
await commitFrame(clock);
|
|
1965
|
+
const fades = recorded.filter(animation =>
|
|
1966
|
+
animation.keyframes.some(keyframe => 'opacity' in keyframe),
|
|
1967
|
+
);
|
|
1968
|
+
expect(fades).toHaveLength(1);
|
|
1969
|
+
await advance(fade + END_HOLD_MS + 1);
|
|
1970
|
+
expect(animator.get('x')).toBe(100);
|
|
1971
|
+
expect(animator.get('opacity')).toBe(1);
|
|
1972
|
+
expect(await move.finished).toBe(true);
|
|
1973
|
+
expect(await state.finished).toBe(true);
|
|
1974
|
+
});
|
|
1975
|
+
|
|
1130
1976
|
it('an entrance after an outside finish starts from what is on screen', async () => {
|
|
1131
1977
|
const {animator} = reduced();
|
|
1132
1978
|
animator.animate(
|
|
@@ -1149,6 +1995,24 @@ describe('Reduced Motion (design:reduced-motion)', () => {
|
|
|
1149
1995
|
expect(fade?.keyframes[0].opacity).toBe('0');
|
|
1150
1996
|
});
|
|
1151
1997
|
|
|
1998
|
+
it('presented: a call that moves and fades in from transparent lands its travel', async () => {
|
|
1999
|
+
// A control part at its rest offset, transparent, presented in one task
|
|
2000
|
+
// with its own animator (SearchField's close button).
|
|
2001
|
+
const {animator, element} = reduced();
|
|
2002
|
+
animator.set({x: 44, opacity: 0});
|
|
2003
|
+
animator.animate(
|
|
2004
|
+
{x: 0, opacity: 1},
|
|
2005
|
+
{spring: springs.settle},
|
|
2006
|
+
{start: 'presented'},
|
|
2007
|
+
);
|
|
2008
|
+
await commitFrame(clock);
|
|
2009
|
+
await commitFrame(clock);
|
|
2010
|
+
await advance(fade + 16);
|
|
2011
|
+
expect(animator.get('x')).toBe(0);
|
|
2012
|
+
expect(parse(element.style.translate)[0] || 0).toBe(0);
|
|
2013
|
+
expect(animator.get('opacity', clock)).toBeCloseTo(1, 9);
|
|
2014
|
+
});
|
|
2015
|
+
|
|
1152
2016
|
it('fades for 200 ms at most', () => {
|
|
1153
2017
|
expect(fade).toBeGreaterThan(0);
|
|
1154
2018
|
expect(fade).toBeLessThanOrEqual(200);
|
|
@@ -1351,10 +2215,13 @@ describe('Reduced Motion (design:reduced-motion)', () => {
|
|
|
1351
2215
|
|
|
1352
2216
|
it('a node detached mid-dip and attached again in a later task shows the move landed and visible', async () => {
|
|
1353
2217
|
const {animator, element} = reduced();
|
|
1354
|
-
// The call owns its opacity: its dip writes the
|
|
2218
|
+
// The call owns its opacity: its dip writes opacity inline, the value on
|
|
2219
|
+
// screen as the fade starts (its end is written when the fade retires),
|
|
2220
|
+
// while the model is already partway down.
|
|
1355
2221
|
animator.animate({x: 240, opacity: 1}, {spring: springs.navigationPush});
|
|
1356
2222
|
await advance(25);
|
|
1357
|
-
expect(
|
|
2223
|
+
expect(element.style.opacity).not.toBe('');
|
|
2224
|
+
expect(animator.get('opacity')).toBeLessThan(1);
|
|
1358
2225
|
animator.attach(null);
|
|
1359
2226
|
await advance(100);
|
|
1360
2227
|
animator.attach(element);
|
|
@@ -1364,6 +2231,24 @@ describe('Reduced Motion (design:reduced-motion)', () => {
|
|
|
1364
2231
|
expect(Number(element.style.opacity || '1')).toBe(1);
|
|
1365
2232
|
});
|
|
1366
2233
|
|
|
2234
|
+
it('a node detached mid-dip from a dimmed opacity shows the move landed and visible when attached again', async () => {
|
|
2235
|
+
// With the presented start, a curve writes its starting value inline: a
|
|
2236
|
+
// dip from a dimmed opacity leaves that low value on the node while the
|
|
2237
|
+
// move still waits to land.
|
|
2238
|
+
const {animator, element} = reduced();
|
|
2239
|
+
animator.set({opacity: 0.3});
|
|
2240
|
+
animator.animate({x: 240, opacity: 1}, {spring: springs.navigationPush});
|
|
2241
|
+
await advance(25);
|
|
2242
|
+
expect(Number(element.style.opacity)).toBeLessThan(1);
|
|
2243
|
+
expect(animator.get('x')).toBe(0);
|
|
2244
|
+
animator.attach(null);
|
|
2245
|
+
await advance(100);
|
|
2246
|
+
animator.attach(element);
|
|
2247
|
+
expect(animator.get('x')).toBe(240);
|
|
2248
|
+
expect(animator.get('opacity')).toBe(1);
|
|
2249
|
+
expect(Number(element.style.opacity || '1')).toBe(1);
|
|
2250
|
+
});
|
|
2251
|
+
|
|
1367
2252
|
it('a node detached mid-dip while another axis moves is left with no animation or promotion', async () => {
|
|
1368
2253
|
const {animator, element} = reduced();
|
|
1369
2254
|
animator.set({x: 0, y: 0});
|
|
@@ -1642,6 +2527,92 @@ describe('Reduced Motion (design:reduced-motion)', () => {
|
|
|
1642
2527
|
}
|
|
1643
2528
|
});
|
|
1644
2529
|
|
|
2530
|
+
it('a landing later than the whole fade back in shows the element at once and settles then', async () => {
|
|
2531
|
+
// A hold released long after the dip was due: the landing\u2019s timer runs
|
|
2532
|
+
// more than the whole fade back in late, so the element appears at once
|
|
2533
|
+
// where it landed. No fade is left for the browser to start a frame
|
|
2534
|
+
// later, so the call\u2019s finished never reports an element still at 0.
|
|
2535
|
+
for (const sheet of ['own', 'stylesheet'] as const) {
|
|
2536
|
+
const {animator, element} = reduced();
|
|
2537
|
+
stylesheet = sheet === 'stylesheet' ? {opacity: '0.6'} : {};
|
|
2538
|
+
const handle = animator.animate(
|
|
2539
|
+
{x: 100},
|
|
2540
|
+
{spring: springs.navigationPush},
|
|
2541
|
+
);
|
|
2542
|
+
for (const animation of recorded) {
|
|
2543
|
+
animation.paused = true;
|
|
2544
|
+
}
|
|
2545
|
+
await commitFrame(clock);
|
|
2546
|
+
clock += fade * 2;
|
|
2547
|
+
for (const animation of recorded) {
|
|
2548
|
+
animation.finish();
|
|
2549
|
+
}
|
|
2550
|
+
const before = recorded.length;
|
|
2551
|
+
let settled: boolean | undefined;
|
|
2552
|
+
void handle.finished.then(value => {
|
|
2553
|
+
settled = value;
|
|
2554
|
+
});
|
|
2555
|
+
// The next frame finds the hold finished: the landing is long due.
|
|
2556
|
+
await commitFrame(clock);
|
|
2557
|
+
await vi.advanceTimersByTimeAsync(0);
|
|
2558
|
+
expect(animator.get('x')).toBe(100);
|
|
2559
|
+
expect(
|
|
2560
|
+
recorded
|
|
2561
|
+
.slice(before)
|
|
2562
|
+
.filter(
|
|
2563
|
+
animation =>
|
|
2564
|
+
!animation.cancelled &&
|
|
2565
|
+
'opacity' in (animation.keyframes[0] ?? {}),
|
|
2566
|
+
),
|
|
2567
|
+
).toHaveLength(0);
|
|
2568
|
+
expect(settled).toBe(true);
|
|
2569
|
+
if (sheet === 'stylesheet') {
|
|
2570
|
+
// Handed back to the stylesheet at once.
|
|
2571
|
+
expect(element.style.opacity).toBe('');
|
|
2572
|
+
} else {
|
|
2573
|
+
expect(animator.get('opacity')).toBe(1);
|
|
2574
|
+
}
|
|
2575
|
+
animator.dispose();
|
|
2576
|
+
recorded = [];
|
|
2577
|
+
}
|
|
2578
|
+
});
|
|
2579
|
+
|
|
2580
|
+
it('a move held before its first frame and finished from outside still lands when its retire runs before the next frame', async () => {
|
|
2581
|
+
// A sampler pauses the call's animations in its own task and finishes
|
|
2582
|
+
// them later. A loaded page can run the retire timer, which cancels the
|
|
2583
|
+
// finished fade, before the frame that would read the call's start: the
|
|
2584
|
+
// call must not be left waiting for a start that no animation can give.
|
|
2585
|
+
const {animator} = reduced();
|
|
2586
|
+
const handle = animator.animate({x: 100}, {spring: springs.navigationPush});
|
|
2587
|
+
for (const animation of recorded) {
|
|
2588
|
+
animation.paused = true;
|
|
2589
|
+
}
|
|
2590
|
+
await commitFrame(clock);
|
|
2591
|
+
clock += 30;
|
|
2592
|
+
await commitFrame(clock);
|
|
2593
|
+
for (const animation of recorded) {
|
|
2594
|
+
animation.finish();
|
|
2595
|
+
}
|
|
2596
|
+
let settled: boolean | undefined;
|
|
2597
|
+
void handle.finished.then(value => {
|
|
2598
|
+
settled = value;
|
|
2599
|
+
});
|
|
2600
|
+
// The retire timer first, with no frame in between: it cancels the
|
|
2601
|
+
// finished fade out.
|
|
2602
|
+
clock += fade * 2;
|
|
2603
|
+
await vi.advanceTimersByTimeAsync(fade * 2);
|
|
2604
|
+
expect(
|
|
2605
|
+
recorded.filter(animation => animation.finished && !animation.cancelled),
|
|
2606
|
+
).toEqual([]);
|
|
2607
|
+
// Frames keep coming: step the clock through the landing.
|
|
2608
|
+
for (let step = 0; step < 16; step++) {
|
|
2609
|
+
await advance(fade / 4);
|
|
2610
|
+
}
|
|
2611
|
+
expect(animator.get('x')).toBe(100);
|
|
2612
|
+
expect(animator.get('opacity')).toBe(1);
|
|
2613
|
+
expect(settled).toBe(true);
|
|
2614
|
+
});
|
|
2615
|
+
|
|
1645
2616
|
it('a waiting call whose own opacity a later call retargets settles interrupted', async () => {
|
|
1646
2617
|
const {animator} = reduced();
|
|
1647
2618
|
const dismiss = animator.animate(
|
|
@@ -1937,6 +2908,75 @@ describe('Reduced Motion (design:reduced-motion)', () => {
|
|
|
1937
2908
|
expect(element.style.translate).toBe('100px 0px');
|
|
1938
2909
|
});
|
|
1939
2910
|
|
|
2911
|
+
it("a pseudo-element's dip borrows its own opacity and hands it back without touching the host", async () => {
|
|
2912
|
+
for (const role of [undefined, 'exit'] as const) {
|
|
2913
|
+
const {animator, element} = setup({
|
|
2914
|
+
reduced: true,
|
|
2915
|
+
coarse: true,
|
|
2916
|
+
pseudoElement: '::before',
|
|
2917
|
+
});
|
|
2918
|
+
element.style.opacity = '0.5'; // the host's own, untouched
|
|
2919
|
+
pseudoStylesheet = {opacity: '0.3'};
|
|
2920
|
+
animator.animate(
|
|
2921
|
+
{x: 100},
|
|
2922
|
+
{spring: springs.navigationPush},
|
|
2923
|
+
role == null ? {} : {crossfadeAs: role},
|
|
2924
|
+
);
|
|
2925
|
+
await commitFrame(clock);
|
|
2926
|
+
const [fadeLayer] = live('opacity');
|
|
2927
|
+
const values = fadeLayer.keyframes.map(k => Number(k.opacity));
|
|
2928
|
+
expect(Math.max(...values)).toBeCloseTo(0.3, 9);
|
|
2929
|
+
await advance(fade * 2 + END_HOLD_MS + 1);
|
|
2930
|
+
expect(animator.get('x')).toBe(100);
|
|
2931
|
+
// The pseudo-element's stylesheet owns its opacity again; the host
|
|
2932
|
+
// keeps its own.
|
|
2933
|
+
expect(live('opacity')).toEqual([]);
|
|
2934
|
+
expect(element.style.opacity).toBe('0.5');
|
|
2935
|
+
}
|
|
2936
|
+
});
|
|
2937
|
+
|
|
2938
|
+
it("a pseudo-element's identity hold follows a node replacement", () => {
|
|
2939
|
+
const {animator, element} = setup({pseudoElement: '::backdrop'});
|
|
2940
|
+
pseudoStylesheet = {opacity: '0'};
|
|
2941
|
+
animator.set({opacity: 1});
|
|
2942
|
+
animator.attach(null);
|
|
2943
|
+
const next = document.createElement('div');
|
|
2944
|
+
document.body.append(next);
|
|
2945
|
+
animator.attach(next);
|
|
2946
|
+
const held = live('opacity').filter(
|
|
2947
|
+
animation => animation.element === next,
|
|
2948
|
+
);
|
|
2949
|
+
expect(held).toHaveLength(1);
|
|
2950
|
+
expect(Number(held[0].keyframes.at(-1)!.opacity)).toBe(1);
|
|
2951
|
+
expect(live('opacity').some(a => a.element === element)).toBe(false);
|
|
2952
|
+
next.remove();
|
|
2953
|
+
});
|
|
2954
|
+
|
|
2955
|
+
it('a pseudo-element shown over a hiding stylesheet keeps its own opacity through a move', async () => {
|
|
2956
|
+
for (const role of [undefined, 'entrance'] as const) {
|
|
2957
|
+
const {animator} = setup({
|
|
2958
|
+
reduced: true,
|
|
2959
|
+
coarse: true,
|
|
2960
|
+
pseudoElement: '::backdrop',
|
|
2961
|
+
});
|
|
2962
|
+
pseudoStylesheet = {opacity: '0'};
|
|
2963
|
+
animator.set({opacity: 1});
|
|
2964
|
+
animator.animate(
|
|
2965
|
+
{x: 100},
|
|
2966
|
+
{spring: springs.navigationPush},
|
|
2967
|
+
role == null ? {} : {crossfadeAs: role},
|
|
2968
|
+
);
|
|
2969
|
+
await commitFrame(clock);
|
|
2970
|
+
for (let step = 0; step < 8; step++) {
|
|
2971
|
+
await advance(fade / 4);
|
|
2972
|
+
}
|
|
2973
|
+
expect(animator.get('x')).toBe(100);
|
|
2974
|
+
expect(animator.get('opacity')).toBe(1);
|
|
2975
|
+
const [held] = live('opacity');
|
|
2976
|
+
expect(Number(held.keyframes.at(-1)?.opacity)).toBe(1);
|
|
2977
|
+
}
|
|
2978
|
+
});
|
|
2979
|
+
|
|
1940
2980
|
it('a move dips: fades out, lands while transparent, fades back in', async () => {
|
|
1941
2981
|
const {animator, element} = reduced();
|
|
1942
2982
|
animator.animate({x: 100}, {spring: springs.navigationPush});
|
|
@@ -2328,6 +3368,8 @@ describe('play: any closed-form curve', () => {
|
|
|
2328
3368
|
const end = press.value(0.266);
|
|
2329
3369
|
expect(animator.get('scale', 1266)).toBeCloseTo(end, 12);
|
|
2330
3370
|
expect(animator.getVelocity('scale', 1266)).toBe(0);
|
|
3371
|
+
// Retired, the end is inline.
|
|
3372
|
+
await advance(400);
|
|
2331
3373
|
for (const axis of parse(element.style.scale)) {
|
|
2332
3374
|
expect(axis).toBeCloseTo(end, 4);
|
|
2333
3375
|
}
|
|
@@ -2434,7 +3476,7 @@ describe('decay: momentum curves (a coast)', () => {
|
|
|
2434
3476
|
Math.abs(
|
|
2435
3477
|
sampleAt(animation, 'translate', 1000 + t)[1] - glide.value(t / 1000),
|
|
2436
3478
|
),
|
|
2437
|
-
).toBeLessThan(0.
|
|
3479
|
+
).toBeLessThan(0.26);
|
|
2438
3480
|
}
|
|
2439
3481
|
expect(animator.get('y', 1e7)).toBeCloseTo(glide.rest, 9);
|
|
2440
3482
|
});
|
|
@@ -2555,6 +3597,56 @@ describe('decay: momentum curves (a coast)', () => {
|
|
|
2555
3597
|
});
|
|
2556
3598
|
});
|
|
2557
3599
|
|
|
3600
|
+
describe('pseudoElement: a dialog\u2019s ::backdrop', () => {
|
|
3601
|
+
it('targets the pseudo-element, writes no inline style and holds the end state', async () => {
|
|
3602
|
+
const {animator, element} = setup({pseudoElement: '::backdrop'});
|
|
3603
|
+
animator.set({opacity: 0});
|
|
3604
|
+
const [held] = live('opacity');
|
|
3605
|
+
expect(held.options).toMatchObject({
|
|
3606
|
+
fill: 'both',
|
|
3607
|
+
pseudoElement: '::backdrop',
|
|
3608
|
+
});
|
|
3609
|
+
expect(held.keyframes.map(k => k.opacity)).toEqual(['0', '0']);
|
|
3610
|
+
expect(element.style.opacity).toBe('');
|
|
3611
|
+
|
|
3612
|
+
animator.animate({opacity: 1}, {spring: springs.alertPresent});
|
|
3613
|
+
// The new animation exists before the hold goes: no frame in between.
|
|
3614
|
+
expect(held.cancelled).toBe(true);
|
|
3615
|
+
const [fade] = live('opacity');
|
|
3616
|
+
expect(fade.options).toMatchObject({
|
|
3617
|
+
fill: 'both',
|
|
3618
|
+
pseudoElement: '::backdrop',
|
|
3619
|
+
});
|
|
3620
|
+
expect(fade.keyframes[fade.keyframes.length - 1].opacity).toBe('1');
|
|
3621
|
+
expect(element.style.opacity).toBe('');
|
|
3622
|
+
await advance(Number(fade.options.duration) + 1000);
|
|
3623
|
+
// Past the curve's end, a static animation holds the end value.
|
|
3624
|
+
expect(fade.cancelled).toBe(true);
|
|
3625
|
+
const [rest] = live('opacity');
|
|
3626
|
+
expect(rest.options).toMatchObject({
|
|
3627
|
+
fill: 'both',
|
|
3628
|
+
pseudoElement: '::backdrop',
|
|
3629
|
+
});
|
|
3630
|
+
expect(rest.keyframes.map(k => k.opacity)).toEqual(['1', '1']);
|
|
3631
|
+
expect(animator.get('opacity')).toBe(1);
|
|
3632
|
+
});
|
|
3633
|
+
|
|
3634
|
+
it('an exit holds 0 until the next animation replaces it', async () => {
|
|
3635
|
+
const {animator} = setup({
|
|
3636
|
+
pseudoElement: '::backdrop',
|
|
3637
|
+
initial: {opacity: 1},
|
|
3638
|
+
});
|
|
3639
|
+
animator.animate({opacity: 0}, {spring: springs.alertDismiss});
|
|
3640
|
+
const [fade] = live('opacity');
|
|
3641
|
+
await advance(Number(fade.options.duration) + 1000);
|
|
3642
|
+
const [rest] = live('opacity');
|
|
3643
|
+
expect(rest.keyframes.map(k => k.opacity)).toEqual(['0', '0']);
|
|
3644
|
+
animator.animate({opacity: 1}, {spring: springs.alertPresent});
|
|
3645
|
+
expect(rest.cancelled).toBe(true);
|
|
3646
|
+
expect(live('opacity')).toHaveLength(1);
|
|
3647
|
+
});
|
|
3648
|
+
});
|
|
3649
|
+
|
|
2558
3650
|
describe('attach', () => {
|
|
2559
3651
|
it('keeps state without an element and resumes the curve on attach', () => {
|
|
2560
3652
|
const animator = createAnimator(null, {
|
|
@@ -2572,7 +3664,8 @@ describe('attach', () => {
|
|
|
2572
3664
|
curve.value(0.1),
|
|
2573
3665
|
1,
|
|
2574
3666
|
);
|
|
2575
|
-
|
|
3667
|
+
// The value on screen, not the end.
|
|
3668
|
+
expect(parse(element.style.translate)[0]).toBeCloseTo(curve.value(0.1), 2);
|
|
2576
3669
|
});
|
|
2577
3670
|
|
|
2578
3671
|
it('unmounting settles a pending atRest() wait and leaves no poll timer', async () => {
|