@astryxdesign/core 0.4.5 → 0.4.6-canary.cea9ecd
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +134 -0
- package/dist/Avatar/Avatar.d.ts +10 -5
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +139 -30
- package/dist/Avatar/AvatarStatusDot.d.ts +2 -1
- package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
- package/dist/Avatar/AvatarStatusDot.js +30 -4
- package/dist/Avatar/AvatarStatusLabelContext.d.ts +39 -0
- package/dist/Avatar/AvatarStatusLabelContext.d.ts.map +1 -0
- package/dist/Avatar/AvatarStatusLabelContext.js +41 -0
- package/dist/AvatarGroup/AvatarGroup.d.ts +3 -1
- package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.js +15 -9
- package/dist/Blockquote/Blockquote.d.ts +4 -3
- package/dist/Blockquote/Blockquote.d.ts.map +1 -1
- package/dist/Blockquote/Blockquote.js +9 -10
- package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetPanel.js +21 -5
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +47 -34
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +7 -7
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +21 -12
- package/dist/Calendar/hooks/useCalendarNavigation.d.ts.map +1 -1
- package/dist/Calendar/hooks/useCalendarNavigation.js +8 -4
- package/dist/Calendar/styles.d.ts +2 -2
- package/dist/Calendar/styles.d.ts.map +1 -1
- package/dist/Calendar/styles.js +5 -5
- package/dist/Center/Center.d.ts +23 -1
- package/dist/Center/Center.d.ts.map +1 -1
- package/dist/Center/Center.js +12 -6
- package/dist/Chat/ChatComposer.d.ts.map +1 -1
- package/dist/Chat/ChatComposer.js +3 -3
- package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
- package/dist/Chat/ChatComposerDrawer.js +2 -2
- package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.js +2 -2
- package/dist/Chat/useChatDictation.d.ts +1 -1
- package/dist/Chat/useChatDictation.d.ts.map +1 -1
- package/dist/Chat/useSpeechRecognition.d.ts +1 -1
- package/dist/Chat/useSpeechRecognition.d.ts.map +1 -1
- package/dist/Chat/useSpeechRecognition.js +4 -3
- package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
- package/dist/Chat/useTriggerMenu.js +2 -2
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +2 -2
- package/dist/Citation/Citation.d.ts.map +1 -1
- package/dist/Citation/Citation.js +5 -5
- package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
- package/dist/ClickableCard/ClickableCard.js +4 -4
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +1 -1
- package/dist/Collapsible/Collapsible.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.js +2 -2
- package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteItem.js +3 -3
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +6 -6
- package/dist/DateInput/DateInput.d.ts +31 -2
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +90 -22
- package/dist/DateInput/MonthScroller.d.ts +35 -0
- package/dist/DateInput/MonthScroller.d.ts.map +1 -0
- package/dist/DateInput/MonthScroller.js +658 -0
- package/dist/DateInput/MonthYearWheels.d.ts +24 -0
- package/dist/DateInput/MonthYearWheels.d.ts.map +1 -0
- package/dist/DateInput/MonthYearWheels.js +114 -0
- package/dist/DateInput/TouchDateField.d.ts +10 -0
- package/dist/DateInput/TouchDateField.d.ts.map +1 -0
- package/dist/DateInput/TouchDateField.js +810 -0
- package/dist/DateInput/Wheel.d.ts +41 -0
- package/dist/DateInput/Wheel.d.ts.map +1 -0
- package/dist/DateInput/Wheel.js +315 -0
- package/dist/DateInput/monthGeometry.d.ts +65 -0
- package/dist/DateInput/monthGeometry.d.ts.map +1 -0
- package/dist/DateInput/monthGeometry.js +98 -0
- package/dist/DateInput/tokens.stylex.d.ts +63 -0
- package/dist/DateInput/tokens.stylex.d.ts.map +1 -0
- package/dist/DateInput/tokens.stylex.js +74 -0
- package/dist/DateInput/useOwnScrollGesture.d.ts +41 -0
- package/dist/DateInput/useOwnScrollGesture.d.ts.map +1 -0
- package/dist/DateInput/useOwnScrollGesture.js +202 -0
- package/dist/DateInput/usePointerDragScroll.d.ts +15 -0
- package/dist/DateInput/usePointerDragScroll.d.ts.map +1 -0
- package/dist/DateInput/usePointerDragScroll.js +216 -0
- package/dist/DateInput/useScrollSettle.d.ts +26 -0
- package/dist/DateInput/useScrollSettle.d.ts.map +1 -0
- package/dist/DateInput/useScrollSettle.js +149 -0
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +17 -14
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +17 -14
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +3 -3
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +2 -2
- package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuItem.js +2 -2
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioItem.js +2 -2
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.js +2 -2
- package/dist/Field/FieldLabel.d.ts.map +1 -1
- package/dist/Field/FieldLabel.js +4 -4
- package/dist/Field/InputClearButton.d.ts.map +1 -1
- package/dist/Field/InputClearButton.js +6 -0
- package/dist/Field/inputStyles.stylex.d.ts +1 -1
- package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
- package/dist/Field/inputStyles.stylex.js +5 -5
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +6 -6
- package/dist/HoverCard/HoverCard.d.ts +13 -3
- package/dist/HoverCard/HoverCard.d.ts.map +1 -1
- package/dist/HoverCard/HoverCard.js +2 -0
- package/dist/HoverCard/index.d.ts +1 -1
- package/dist/HoverCard/index.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.d.ts +17 -2
- package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +78 -9
- package/dist/InputGroup/InputGroup.js +1 -1
- package/dist/Item/Item.d.ts +9 -1
- package/dist/Item/Item.d.ts.map +1 -1
- package/dist/Item/Item.js +52 -14
- package/dist/Layer/index.d.ts +4 -2
- package/dist/Layer/index.d.ts.map +1 -1
- package/dist/Layer/index.js +2 -0
- package/dist/Layer/useLayer.d.ts.map +1 -1
- package/dist/Layer/useLayer.js +3 -2
- package/dist/Layer/useTouchTrigger.d.ts +113 -0
- package/dist/Layer/useTouchTrigger.d.ts.map +1 -0
- package/dist/Layer/useTouchTrigger.js +241 -0
- package/dist/Layout/container.stylex.d.ts +4 -2
- package/dist/Layout/container.stylex.d.ts.map +1 -1
- package/dist/Layout/container.stylex.js +19 -11
- package/dist/Layout/index.d.ts +1 -0
- package/dist/Layout/index.d.ts.map +1 -1
- package/dist/Layout/index.js +4 -0
- package/dist/Layout/padding.stylex.d.ts +315 -12
- package/dist/Layout/padding.stylex.d.ts.map +1 -1
- package/dist/Layout/padding.stylex.js +389 -12
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +5 -4
- package/dist/Link/Link.d.ts.map +1 -1
- package/dist/Link/Link.js +6 -6
- package/dist/MetadataList/MetadataList.d.ts.map +1 -1
- package/dist/MetadataList/MetadataList.js +1 -1
- package/dist/MobileNav/MobileNav.d.ts +1 -0
- package/dist/MobileNav/MobileNav.d.ts.map +1 -1
- package/dist/MobileNav/MobileNav.js +24 -6
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +8 -8
- package/dist/NavItem/navItemStyles.stylex.d.ts +4 -4
- package/dist/NavItem/navItemStyles.stylex.d.ts.map +1 -1
- package/dist/NavItem/navItemStyles.stylex.js +4 -4
- package/dist/NavMenu/NavHeadingMenuItem.d.ts.map +1 -1
- package/dist/NavMenu/NavHeadingMenuItem.js +3 -3
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +36 -8
- package/dist/Outline/Outline.d.ts.map +1 -1
- package/dist/Outline/Outline.js +3 -3
- package/dist/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Pagination/Pagination.js +2 -2
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +11 -12
- package/dist/PowerSearch/PowerSearchToken.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchToken.js +3 -1
- package/dist/PowerSearch/formatFilterValue.d.ts +11 -2
- package/dist/PowerSearch/formatFilterValue.d.ts.map +1 -1
- package/dist/PowerSearch/formatFilterValue.js +23 -8
- package/dist/RadioList/RadioListItem.d.ts +2 -2
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +70 -40
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +7 -7
- package/dist/Section/Section.d.ts +36 -1
- package/dist/Section/Section.d.ts.map +1 -1
- package/dist/Section/Section.js +12 -2
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +7 -2
- package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
- package/dist/SelectableCard/SelectableCard.js +3 -3
- package/dist/Selector/Selector.d.ts +26 -1
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +93 -20
- package/dist/Selector/SelectorOption.d.ts +14 -1
- package/dist/Selector/SelectorOption.d.ts.map +1 -1
- package/dist/Selector/SelectorOption.js +6 -0
- package/dist/Selector/SelectorRowLayoutContext.d.ts +8 -0
- package/dist/Selector/SelectorRowLayoutContext.d.ts.map +1 -0
- package/dist/Selector/SelectorRowLayoutContext.js +27 -0
- package/dist/Selector/types.d.ts +1 -0
- package/dist/Selector/types.d.ts.map +1 -1
- package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
- package/dist/SideNav/SideNavHeading.js +4 -4
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +3 -3
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +7 -7
- package/dist/Stack/Stack.d.ts +23 -1
- package/dist/Stack/Stack.d.ts.map +1 -1
- package/dist/Stack/Stack.js +12 -6
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +2 -2
- package/dist/TabList/Tab.d.ts.map +1 -1
- package/dist/TabList/Tab.js +1 -1
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +3 -3
- package/dist/Table/TableRow.js +4 -4
- package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
- package/dist/Table/plugins/columnResize/useTableColumnResize.js +1 -1
- package/dist/Table/plugins/filtering/useTableFiltering.d.ts.map +1 -1
- package/dist/Table/plugins/filtering/useTableFiltering.js +2 -2
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +2 -2
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +2 -2
- package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortable.js +1 -1
- package/dist/Table/plugins/sortable/useTableSortableState.d.ts +5 -3
- package/dist/Table/plugins/sortable/useTableSortableState.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortableState.js +12 -9
- package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
- package/dist/Table/plugins/tree/useTableTreeData.js +3 -3
- package/dist/TextArea/TextArea.js +1 -1
- package/dist/TextInput/TextInput.js +1 -1
- package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
- package/dist/Thumbnail/Thumbnail.js +3 -3
- package/dist/TimeInput/TimeInput.js +2 -2
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +6 -4
- package/dist/Timestamp/formatInstant.d.ts +3 -2
- package/dist/Timestamp/formatInstant.d.ts.map +1 -1
- package/dist/Timestamp/formatInstant.js +8 -8
- package/dist/Timestamp/tooltipEntries.d.ts +2 -1
- package/dist/Timestamp/tooltipEntries.d.ts.map +1 -1
- package/dist/Timestamp/tooltipEntries.js +5 -3
- package/dist/Toast/Toast.d.ts +4 -3
- package/dist/Toast/Toast.d.ts.map +1 -1
- package/dist/Toast/Toast.js +10 -7
- package/dist/Token/Token.d.ts.map +1 -1
- package/dist/Token/Token.js +6 -6
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +1 -1
- package/dist/Tooltip/Tooltip.d.ts +16 -3
- package/dist/Tooltip/Tooltip.d.ts.map +1 -1
- package/dist/Tooltip/Tooltip.js +2 -0
- package/dist/Tooltip/index.d.ts +1 -1
- package/dist/Tooltip/index.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.d.ts +21 -2
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +71 -13
- package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
- package/dist/TopNav/TopNavHeading.js +4 -4
- package/dist/TopNav/TopNavItem.d.ts.map +1 -1
- package/dist/TopNav/TopNavItem.js +3 -3
- package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenu.js +2 -2
- package/dist/TopNav/TopNavMegaMenuItem.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenuItem.js +2 -2
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +4 -4
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +7 -7
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +2 -2
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +1 -1
- package/dist/astryx.css +124 -62
- package/dist/hooks/containerReveal.stylex.d.ts.map +1 -1
- package/dist/hooks/containerReveal.stylex.js +18 -18
- package/dist/hooks/scrollbarGutter.d.ts +28 -0
- package/dist/hooks/scrollbarGutter.d.ts.map +1 -0
- package/dist/hooks/scrollbarGutter.js +103 -0
- package/dist/hooks/useAutoMediaMode.d.ts +31 -0
- package/dist/hooks/useAutoMediaMode.d.ts.map +1 -0
- package/dist/hooks/useAutoMediaMode.js +201 -0
- package/dist/hooks/useInputStatusIcon.d.ts.map +1 -1
- package/dist/hooks/useInputStatusIcon.js +1 -1
- package/dist/hooks/useListFocus.d.ts +5 -2
- package/dist/hooks/useListFocus.d.ts.map +1 -1
- package/dist/hooks/useListFocus.js +12 -6
- package/dist/hooks/useScrollLock.d.ts +4 -0
- package/dist/hooks/useScrollLock.d.ts.map +1 -1
- package/dist/hooks/useScrollLock.js +13 -1
- package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
- package/dist/i18n/InternationalizationContext.js +2 -0
- package/dist/i18n/index.d.ts +6 -0
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js +6 -0
- package/dist/i18n/useCollator.d.ts +12 -0
- package/dist/i18n/useCollator.d.ts.map +1 -0
- package/dist/i18n/useCollator.js +40 -0
- package/dist/i18n/useLocale.d.ts +3 -0
- package/dist/i18n/useLocale.d.ts.map +1 -0
- package/dist/i18n/useLocale.js +31 -0
- package/dist/theme/MediaTheme.d.ts +25 -5
- package/dist/theme/MediaTheme.d.ts.map +1 -1
- package/dist/theme/MediaTheme.js +31 -6
- package/dist/theme/defineTheme.d.ts +5 -0
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/defineTheme.js +5 -0
- package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
- package/dist/theme/derivedVarRegistry.js +7 -0
- package/dist/theme/generateThemeRules.d.ts.map +1 -1
- package/dist/theme/generateThemeRules.js +66 -5
- package/dist/theme/index.d.ts +1 -1
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/syntax/index.d.ts +6 -0
- package/dist/theme/syntax/index.d.ts.map +1 -1
- package/dist/theme/syntax/index.js +7 -2
- package/dist/utils/dateParser.d.ts +4 -3
- package/dist/utils/dateParser.d.ts.map +1 -1
- package/dist/utils/dateParser.js +8 -8
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/plainDate.d.ts +5 -2
- package/dist/utils/plainDate.d.ts.map +1 -1
- package/dist/utils/plainDate.js +20 -4
- package/locales/en.json +28 -0
- package/locales/pseudo.json +21 -0
- package/package.json +3 -3
- package/src/Avatar/Avatar.doc.mjs +10 -6
- package/src/Avatar/Avatar.test.tsx +219 -1
- package/src/Avatar/Avatar.tsx +179 -37
- package/src/Avatar/AvatarStatusDot.tsx +30 -4
- package/src/Avatar/AvatarStatusLabelContext.ts +47 -0
- package/src/AvatarGroup/AvatarGroup.doc.mjs +10 -6
- package/src/AvatarGroup/AvatarGroup.test.tsx +62 -0
- package/src/AvatarGroup/AvatarGroup.tsx +3 -1
- package/src/AvatarGroup/AvatarGroupOverflow.tsx +31 -8
- package/src/Banner/Banner.doc.mjs +2 -0
- package/src/Blockquote/Blockquote.doc.mjs +30 -7
- package/src/Blockquote/Blockquote.test.tsx +34 -3
- package/src/Blockquote/Blockquote.tsx +11 -9
- package/src/BottomSheet/BottomSheet.doc.mjs +3 -3
- package/src/BottomSheet/BottomSheet.test.tsx +24 -0
- package/src/BottomSheet/BottomSheetPanel.test.tsx +91 -0
- package/src/BottomSheet/BottomSheetPanel.tsx +58 -3
- package/src/Breadcrumbs/BreadcrumbItem.tsx +36 -23
- package/src/Breadcrumbs/Breadcrumbs.doc.mjs +26 -2
- package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -0
- package/src/Button/Button.tsx +12 -9
- package/src/Calendar/Calendar.tsx +15 -9
- package/src/Calendar/hooks/useCalendarNavigation.ts +6 -4
- package/src/Calendar/styles.ts +8 -5
- package/src/Center/Center.doc.mjs +48 -0
- package/src/Center/Center.test.tsx +158 -0
- package/src/Center/Center.tsx +50 -9
- package/src/Chat/ChatComposer.tsx +11 -5
- package/src/Chat/ChatComposerDrawer.tsx +4 -1
- package/src/Chat/ChatToolCalls.tsx +13 -4
- package/src/Chat/{useChatDictation.test.ts → useChatDictation.test.tsx} +29 -14
- package/src/Chat/useChatDictation.ts +1 -1
- package/src/Chat/useSpeechRecognition.ts +5 -3
- package/src/Chat/useTriggerMenu.tsx +4 -1
- package/src/CheckboxInput/CheckboxInput.tsx +5 -2
- package/src/Citation/Citation.test.tsx +6 -1
- package/src/Citation/Citation.tsx +11 -5
- package/src/ClickableCard/ClickableCard.tsx +7 -4
- package/src/CodeBlock/CodeBlock.tsx +4 -1
- package/src/Collapsible/Collapsible.tsx +5 -2
- package/src/CommandPalette/CommandPaletteItem.tsx +6 -3
- package/src/ComplexSelector/ComplexSelector.doc.mjs +10 -0
- package/src/ComplexSelector/ComplexSelector.tsx +14 -7
- package/src/DateInput/DateInput.test.tsx +17 -1
- package/src/DateInput/DateInput.tsx +93 -21
- package/src/DateInput/DateInputTouch.test.tsx +2582 -0
- package/src/DateInput/MonthScroller.tsx +878 -0
- package/src/DateInput/MonthYearWheels.tsx +147 -0
- package/src/DateInput/TouchDateField.tsx +1223 -0
- package/src/DateInput/Wheel.tsx +473 -0
- package/src/DateInput/monthGeometry.ts +118 -0
- package/src/DateInput/tokens.stylex.ts +99 -0
- package/src/DateInput/useOwnScrollGesture.ts +227 -0
- package/src/DateInput/usePointerDragScroll.ts +220 -0
- package/src/DateInput/useScrollSettle.ts +164 -0
- package/src/DateRangeInput/DateRangeInput.doc.mjs +1 -1
- package/src/DateRangeInput/DateRangeInput.tsx +31 -13
- package/src/DateTimeInput/DateTimeInput.tsx +25 -9
- package/src/Dialog/Dialog.test.tsx +24 -0
- package/src/Dialog/Dialog.tsx +3 -0
- package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +5 -2
- package/src/DropdownMenu/DropdownMenuItem.tsx +5 -2
- package/src/DropdownMenu/DropdownMenuRadioItem.tsx +5 -2
- package/src/DropdownMenu/DropdownMenuSubMenu.tsx +5 -2
- package/src/Field/Field.doc.mjs +2 -0
- package/src/Field/FieldLabel.tsx +9 -3
- package/src/Field/InputClearButton.test.tsx +110 -3
- package/src/Field/InputClearButton.tsx +38 -0
- package/src/Field/inputStyles.stylex.ts +17 -13
- package/src/FileInput/FileInput.tsx +15 -8
- package/src/FormLayout/FormLayout.doc.mjs +1 -1
- package/src/HoverCard/HoverCard.doc.mjs +18 -0
- package/src/HoverCard/HoverCard.test.tsx +189 -0
- package/src/HoverCard/HoverCard.tsx +22 -2
- package/src/HoverCard/index.ts +1 -0
- package/src/HoverCard/useHoverCard.doc.mjs +2 -0
- package/src/HoverCard/useHoverCard.tsx +103 -7
- package/src/InputGroup/InputGroup.tsx +1 -1
- package/src/Item/Item.doc.mjs +1 -0
- package/src/Item/Item.test.tsx +50 -0
- package/src/Item/Item.tsx +54 -6
- package/src/Layer/index.ts +10 -0
- package/src/Layer/useLayer.tsx +13 -2
- package/src/Layer/useTouchTrigger.test.ts +70 -0
- package/src/Layer/useTouchTrigger.ts +366 -0
- package/src/Layout/Layout.doc.mjs +18 -0
- package/src/Layout/container.stylex.ts +11 -3
- package/src/Layout/index.ts +4 -0
- package/src/Layout/overlayPaddingReset.test.tsx +222 -0
- package/src/Layout/padding.stylex.ts +192 -12
- package/src/Lightbox/Lightbox.tsx +11 -3
- package/src/Link/Link.tsx +9 -6
- package/src/MetadataList/MetadataList.tsx +4 -1
- package/src/MobileNav/MobileNav.tsx +61 -5
- package/src/MobileNav/MobileNavEntryAnimation.test.tsx +212 -0
- package/src/MobileNav/MobileNavScrollbarGutter.test.tsx +109 -0
- package/src/MultiSelector/MultiSelector.test.tsx +1 -1
- package/src/MultiSelector/MultiSelector.tsx +26 -10
- package/src/NavItem/navItemStyles.stylex.ts +7 -4
- package/src/NavMenu/NavHeadingMenuItem.tsx +6 -3
- package/src/NumberInput/NumberInput.doc.mjs +24 -0
- package/src/NumberInput/NumberInput.test.tsx +138 -0
- package/src/NumberInput/NumberInput.tsx +47 -8
- package/src/Outline/Outline.tsx +5 -2
- package/src/Pagination/Pagination.tsx +5 -2
- package/src/Popover/Popover.test.tsx +27 -1
- package/src/PowerSearch/PowerSearch.tsx +17 -8
- package/src/PowerSearch/PowerSearchToken.tsx +3 -0
- package/src/PowerSearch/formatFilterValue.test.ts +22 -2
- package/src/PowerSearch/formatFilterValue.ts +31 -6
- package/src/RadioList/RadioList.doc.mjs +1 -1
- package/src/RadioList/RadioList.test.tsx +170 -2
- package/src/RadioList/RadioListItem.tsx +88 -53
- package/src/Resizable/ResizeHandle.test.tsx +22 -0
- package/src/Resizable/ResizeHandle.tsx +25 -7
- package/src/Section/Section.doc.mjs +55 -0
- package/src/Section/Section.test.tsx +201 -4
- package/src/Section/Section.tsx +69 -0
- package/src/SegmentedControl/SegmentedControl.test.tsx +31 -0
- package/src/SegmentedControl/SegmentedControlItem.tsx +15 -3
- package/src/SelectableCard/SelectableCard.tsx +6 -3
- package/src/Selector/Selector.doc.mjs +16 -2
- package/src/Selector/Selector.test.tsx +525 -1
- package/src/Selector/Selector.tsx +180 -25
- package/src/Selector/SelectorOption.doc.mjs +7 -0
- package/src/Selector/SelectorOption.tsx +21 -0
- package/src/Selector/SelectorRowLayoutContext.ts +32 -0
- package/src/Selector/types.ts +4 -4
- package/src/SideNav/SideNavHeading.tsx +14 -5
- package/src/SideNav/SideNavItem.tsx +9 -3
- package/src/Slider/Slider.tsx +25 -6
- package/src/Stack/Stack.doc.mjs +72 -0
- package/src/Stack/Stack.test.tsx +156 -0
- package/src/Stack/Stack.tsx +50 -8
- package/src/Switch/Switch.tsx +5 -2
- package/src/TabList/Tab.tsx +4 -1
- package/src/TabList/TabMenu.tsx +9 -3
- package/src/Table/TableRow.tsx +4 -4
- package/src/Table/plugins/columnResize/useTableColumnResize.tsx +6 -2
- package/src/Table/plugins/filtering/useTableFiltering.tsx +4 -1
- package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +10 -3
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +6 -3
- package/src/Table/plugins/sortable/useTableSortable.tsx +4 -1
- package/src/Table/plugins/sortable/useTableSortableState.test.tsx +34 -0
- package/src/Table/plugins/sortable/useTableSortableState.tsx +26 -23
- package/src/Table/plugins/tree/useTableTreeData.tsx +10 -4
- package/src/TextArea/TextArea.doc.mjs +1 -1
- package/src/TextArea/TextArea.tsx +1 -1
- package/src/TextInput/TextInput.tsx +1 -1
- package/src/Thumbnail/Thumbnail.tsx +9 -3
- package/src/TimeInput/TimeInput.tsx +1 -1
- package/src/Timestamp/Timestamp.test.tsx +27 -7
- package/src/Timestamp/Timestamp.tsx +8 -4
- package/src/Timestamp/formatInstant.ts +9 -7
- package/src/Timestamp/tooltipEntries.test.ts +11 -6
- package/src/Timestamp/tooltipEntries.ts +6 -2
- package/src/Toast/Toast.tsx +9 -7
- package/src/Token/Token.tsx +14 -5
- package/src/Tokenizer/Tokenizer.tsx +4 -1
- package/src/Tooltip/Tooltip.doc.mjs +18 -0
- package/src/Tooltip/Tooltip.test.tsx +254 -1
- package/src/Tooltip/Tooltip.tsx +22 -2
- package/src/Tooltip/index.ts +1 -0
- package/src/Tooltip/useTooltip.doc.mjs +2 -0
- package/src/Tooltip/useTooltip.tsx +99 -21
- package/src/TopNav/TopNavHeading.tsx +13 -4
- package/src/TopNav/TopNavItem.tsx +6 -3
- package/src/TopNav/TopNavMegaMenu.tsx +5 -2
- package/src/TopNav/TopNavMegaMenuItem.tsx +5 -2
- package/src/TopNav/TopNavMenu.tsx +10 -4
- package/src/TreeList/TreeListItem.tsx +22 -7
- package/src/Typeahead/BaseTypeahead.tsx +5 -2
- package/src/Typeahead/Typeahead.tsx +4 -1
- package/src/hooks/containerReveal.stylex.ts +54 -18
- package/src/hooks/scrollbarGutter.test.ts +167 -0
- package/src/hooks/scrollbarGutter.ts +121 -0
- package/src/hooks/useAutoMediaMode.test.ts +109 -0
- package/src/hooks/useAutoMediaMode.ts +233 -0
- package/src/hooks/useInputStatusIcon.tsx +4 -1
- package/src/hooks/useListFocus.doc.mjs +2 -2
- package/src/hooks/useListFocus.test.tsx +65 -3
- package/src/hooks/useListFocus.ts +15 -7
- package/src/hooks/useScrollLock.doc.mjs +4 -4
- package/src/hooks/useScrollLock.test.ts +73 -0
- package/src/hooks/useScrollLock.ts +14 -0
- package/src/i18n/InternationalizationContext.ts +2 -0
- package/src/i18n/__tests__/e2e-powersearch.test.tsx +37 -1
- package/src/i18n/__tests__/useCollator.test.tsx +115 -0
- package/src/i18n/__tests__/useLocale.test.tsx +52 -0
- package/src/i18n/index.ts +6 -0
- package/src/i18n/useCollator.doc.mjs +84 -0
- package/src/i18n/useCollator.ts +44 -0
- package/src/i18n/useLocale.doc.mjs +70 -0
- package/src/i18n/useLocale.ts +34 -0
- package/src/i18n/useTranslator.doc.mjs +1 -1
- package/src/reset.css +9 -2
- package/src/reset.test.ts +22 -0
- package/src/theme/MediaTheme.doc.mjs +23 -4
- package/src/theme/MediaTheme.dom.test.tsx +97 -0
- package/src/theme/MediaTheme.tsx +42 -7
- package/src/theme/defineTheme.test.ts +9 -3
- package/src/theme/defineTheme.ts +5 -0
- package/src/theme/derivedVarRegistry.test.ts +19 -5
- package/src/theme/derivedVarRegistry.ts +4 -0
- package/src/theme/generateThemeRules.test.ts +118 -0
- package/src/theme/generateThemeRules.ts +83 -5
- package/src/theme/index.ts +1 -1
- package/src/theme/syntax/index.ts +6 -2
- package/src/theme/syntax/serverSafeSyntax.test.ts +68 -0
- package/src/utils/dateParser.test.ts +20 -0
- package/src/utils/dateParser.ts +14 -7
- package/src/utils/index.ts +2 -0
- package/src/utils/plainDate.test.ts +6 -0
- package/src/utils/plainDate.ts +21 -4
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file useOwnScrollGesture.ts
|
|
7
|
+
* @input A scroll container ref and which axis it owns
|
|
8
|
+
* @output Stops touch gestures on that container from reaching an ancestor
|
|
9
|
+
* @position Internal behavior hook; consumed by Wheel.tsx and MonthScroller.tsx
|
|
10
|
+
*
|
|
11
|
+
* Lets a nested scroller keep the finger that lands on it.
|
|
12
|
+
*
|
|
13
|
+
* ## The conflict
|
|
14
|
+
*
|
|
15
|
+
* `BottomSheet` implements swipe-to-dismiss by watching touches on its
|
|
16
|
+
* scrolling body: at the body's scroll top, a downward pull stops being a
|
|
17
|
+
* scroll and becomes a sheet drag. The test is `body.scrollTop`, and the body
|
|
18
|
+
* of a sheet sized to hug its content never scrolls, so it reads as "at the
|
|
19
|
+
* top" forever — every downward-ish drag anywhere inside promotes to a drag.
|
|
20
|
+
*
|
|
21
|
+
* `touch-action` does NOT protect against this. It governs what the BROWSER
|
|
22
|
+
* will pan natively; the sheet's listener is JavaScript, and its
|
|
23
|
+
* `preventDefault()` cancels the native scroll whatever `touch-action` says.
|
|
24
|
+
* Measured: with `touch-action: pan-x` on the month scroller, a swipe just 9°
|
|
25
|
+
* off horizontal (120px across, 20px down — an ordinary thumb arc) had every
|
|
26
|
+
* `touchmove` cancelled by the sheet, so the calendar did not move at all and
|
|
27
|
+
* the sheet sprang back. The month simply would not change.
|
|
28
|
+
*
|
|
29
|
+
* ## Two ways to own a gesture
|
|
30
|
+
*
|
|
31
|
+
* - `'all'` — the wheels. They scroll vertically, exactly the axis the sheet
|
|
32
|
+
* wants, so there is no way to share: the scroller takes every touch that
|
|
33
|
+
* lands on it and the sheet is dismissed from the handle, the header or the
|
|
34
|
+
* scrim instead.
|
|
35
|
+
*
|
|
36
|
+
* - `'inline'` — the month scroller. It pages sideways, so the two CAN share
|
|
37
|
+
* by direction: lock the axis on the first significant move, claim the
|
|
38
|
+
* gesture only when it is horizontal, and let vertical ones through so
|
|
39
|
+
* swipe-to-dismiss still works on the calendar.
|
|
40
|
+
*
|
|
41
|
+
* ## Where the line falls, and the gestures that fall past it
|
|
42
|
+
*
|
|
43
|
+
* A thumb swiping sideways travels in an arc, not a line — a "horizontal"
|
|
44
|
+
* swipe routinely drifts 10-30° down, and sometimes further. So an `'inline'`
|
|
45
|
+
* scroller claims generously: vertical has to beat horizontal by
|
|
46
|
+
* {@link VERTICAL_DOMINANCE} before the sheet gets the gesture, which puts the
|
|
47
|
+
* split near 63° rather than at the diagonal.
|
|
48
|
+
*
|
|
49
|
+
* That is wider than the browser will pan. `touch-action: pan-x` pans only
|
|
50
|
+
* while `|dx| > |dy|` and refuses the moment a gesture tips past 45°, so the
|
|
51
|
+
* band between the two lines used to be the worst outcome available: measured
|
|
52
|
+
* on an iPhone 15 profile, a 200px swipe at 45-60° neither paged a month nor
|
|
53
|
+
* dismissed the sheet, because we had told the sheet to keep off a gesture the
|
|
54
|
+
* compositor then declined to pan. Nothing moved at all.
|
|
55
|
+
*
|
|
56
|
+
* {@link OwnScrollGestureOptions.onSwipe} closes that band, and it does not
|
|
57
|
+
* need to know where the browser's line is: if the element's scroll offset
|
|
58
|
+
* never changed across a claimed gesture, the browser declined it, and a
|
|
59
|
+
* swipe that cleared {@link SWIPE_DISTANCE} is reported so the caller can page
|
|
60
|
+
* itself. Native pans keep their momentum and snapping and report nothing.
|
|
61
|
+
*
|
|
62
|
+
* The remaining asymmetry is deliberate and in the direction the scroller
|
|
63
|
+
* needs: it is generous about distance too, paging on a 30px flick where the
|
|
64
|
+
* sheet asks for ~200px of pull before it dismisses.
|
|
65
|
+
*
|
|
66
|
+
* ## Why native listeners
|
|
67
|
+
*
|
|
68
|
+
* React delegates its events at the root container, which is an ANCESTOR of
|
|
69
|
+
* the sheet body — so a React `onTouchStart` would run after the body's own
|
|
70
|
+
* listener had already claimed the gesture. `stopPropagation` has to happen on
|
|
71
|
+
* a listener attached to the element itself, during the real bubble phase,
|
|
72
|
+
* before the event ever reaches the body.
|
|
73
|
+
*
|
|
74
|
+
* Nothing here calls `preventDefault`, so the listeners stay passive and the
|
|
75
|
+
* scroller keeps native momentum, snapping and rubber-banding.
|
|
76
|
+
*
|
|
77
|
+
* SYNC: When modified, update DateInputTouch.test.tsx.
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
import {useEffect, useRef} from 'react';
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* How much a gesture's vertical travel must exceed its horizontal travel
|
|
84
|
+
* before an `'inline'` scroller gives it up. Above 1, so horizontal wins the
|
|
85
|
+
* ambiguous middle; see the file header for why the split is deliberately
|
|
86
|
+
* unfair, and how the gestures past the browser's own 45° line are handled.
|
|
87
|
+
*/
|
|
88
|
+
export const VERTICAL_DOMINANCE = 2;
|
|
89
|
+
|
|
90
|
+
/** Movement below this is noise, not a direction. */
|
|
91
|
+
const AXIS_LOCK_SLOP = 6;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Horizontal travel a claimed-but-unpanned gesture needs before it counts as
|
|
95
|
+
* a swipe. Roughly the shortest flick the browser itself will page on.
|
|
96
|
+
*/
|
|
97
|
+
export const SWIPE_DISTANCE = 32;
|
|
98
|
+
|
|
99
|
+
export type OwnedAxis = 'all' | 'inline';
|
|
100
|
+
|
|
101
|
+
export type OwnScrollGestureOptions = {
|
|
102
|
+
/** Skip while the scroller is hidden; a hidden panel keeps its layout box,
|
|
103
|
+
* so its listeners would otherwise still be live. */
|
|
104
|
+
isEnabled?: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Called when a claimed gesture ended without the browser having scrolled
|
|
107
|
+
* the element at all — the diagonal swipes `touch-action: pan-x` refuses.
|
|
108
|
+
*
|
|
109
|
+
* `direction` is the sign to move the scroll offset by, so it composes
|
|
110
|
+
* directly: `scrollBy({left: direction * pageWidth})`. It is physical, and
|
|
111
|
+
* therefore correct in both directions of writing.
|
|
112
|
+
*
|
|
113
|
+
* Only fires for `'inline'`. A gesture the browser panned reports nothing,
|
|
114
|
+
* so momentum and snapping are never second-guessed.
|
|
115
|
+
*/
|
|
116
|
+
onSwipe?: (direction: 1 | -1) => void;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Claim touch gestures that start on `ref`'s element, so an ancestor cannot
|
|
121
|
+
* reinterpret them as its own drag.
|
|
122
|
+
*
|
|
123
|
+
* @param ref - the scroll container
|
|
124
|
+
* @param axis - `'all'` to take every touch, `'inline'` to take only the
|
|
125
|
+
* horizontal ones and leave vertical drags to the sheet
|
|
126
|
+
* @param options - see {@link OwnScrollGestureOptions}
|
|
127
|
+
*/
|
|
128
|
+
export function useOwnScrollGesture(
|
|
129
|
+
ref: React.RefObject<HTMLElement | null>,
|
|
130
|
+
axis: OwnedAxis = 'all',
|
|
131
|
+
options: OwnScrollGestureOptions = {},
|
|
132
|
+
): void {
|
|
133
|
+
const {isEnabled = true, onSwipe} = options;
|
|
134
|
+
|
|
135
|
+
// Read through a ref so a caller need not memoize the callback: re-running
|
|
136
|
+
// this effect would tear the listeners off mid-gesture.
|
|
137
|
+
const onSwipeRef = useRef(onSwipe);
|
|
138
|
+
onSwipeRef.current = onSwipe;
|
|
139
|
+
|
|
140
|
+
useEffect(() => {
|
|
141
|
+
const element = ref.current;
|
|
142
|
+
if (element == null || !isEnabled) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// null = not yet decided; true = ours for the rest of this gesture.
|
|
147
|
+
let claimed: boolean | null = null;
|
|
148
|
+
let originX = 0;
|
|
149
|
+
let originY = 0;
|
|
150
|
+
let lastX = 0;
|
|
151
|
+
let offsetAtStart = 0;
|
|
152
|
+
// Whether the browser actually panned the element during this gesture.
|
|
153
|
+
// Sampled per move rather than compared once at the end, because a short
|
|
154
|
+
// pan that snaps back finishes at the offset it started from.
|
|
155
|
+
let didPan = false;
|
|
156
|
+
|
|
157
|
+
const onStart = (event: TouchEvent) => {
|
|
158
|
+
const touch = event.touches[0];
|
|
159
|
+
originX = touch?.clientX ?? 0;
|
|
160
|
+
originY = touch?.clientY ?? 0;
|
|
161
|
+
lastX = originX;
|
|
162
|
+
offsetAtStart = element.scrollLeft;
|
|
163
|
+
didPan = false;
|
|
164
|
+
// 'all' decides at touchstart, before the sheet's own touchstart
|
|
165
|
+
// listener records an armed edge. 'inline' cannot decide yet — a touch
|
|
166
|
+
// has no direction until it moves — so it lets touchstart through and
|
|
167
|
+
// locks on the first move that clears the slop.
|
|
168
|
+
claimed = axis === 'all' ? true : null;
|
|
169
|
+
if (claimed) {
|
|
170
|
+
event.stopPropagation();
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const onMove = (event: TouchEvent) => {
|
|
175
|
+
if (claimed === false) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const touch = event.touches[0];
|
|
179
|
+
if (touch == null) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (claimed == null) {
|
|
183
|
+
const dx = Math.abs(touch.clientX - originX);
|
|
184
|
+
const dy = Math.abs(touch.clientY - originY);
|
|
185
|
+
if (Math.max(dx, dy) < AXIS_LOCK_SLOP) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
claimed = dy <= dx * VERTICAL_DOMINANCE;
|
|
189
|
+
if (!claimed) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
lastX = touch.clientX;
|
|
194
|
+
didPan ||= Math.abs(element.scrollLeft - offsetAtStart) >= 1;
|
|
195
|
+
event.stopPropagation();
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const onEnd = () => {
|
|
199
|
+
// A gesture we claimed that the browser then refused to pan: the sheet
|
|
200
|
+
// was told to keep off it, so unless it is acted on here it does
|
|
201
|
+
// nothing at all. See the file header.
|
|
202
|
+
if (
|
|
203
|
+
claimed === true &&
|
|
204
|
+
axis === 'inline' &&
|
|
205
|
+
!didPan &&
|
|
206
|
+
Math.abs(lastX - originX) >= SWIPE_DISTANCE
|
|
207
|
+
) {
|
|
208
|
+
onSwipeRef.current?.(lastX < originX ? 1 : -1);
|
|
209
|
+
}
|
|
210
|
+
// touchend/touchcancel deliberately propagate: they carry no
|
|
211
|
+
// interpretation, and letting them through is what resets the sheet's
|
|
212
|
+
// bookkeeping.
|
|
213
|
+
claimed = null;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
element.addEventListener('touchstart', onStart, {passive: true});
|
|
217
|
+
element.addEventListener('touchmove', onMove, {passive: true});
|
|
218
|
+
element.addEventListener('touchend', onEnd, {passive: true});
|
|
219
|
+
element.addEventListener('touchcancel', onEnd, {passive: true});
|
|
220
|
+
return () => {
|
|
221
|
+
element.removeEventListener('touchstart', onStart);
|
|
222
|
+
element.removeEventListener('touchmove', onMove);
|
|
223
|
+
element.removeEventListener('touchend', onEnd);
|
|
224
|
+
element.removeEventListener('touchcancel', onEnd);
|
|
225
|
+
};
|
|
226
|
+
}, [ref, axis, isEnabled]);
|
|
227
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file usePointerDragScroll.ts
|
|
7
|
+
* @input A scroll container ref
|
|
8
|
+
* @output Lets a mouse drag the container's contents the way a finger does
|
|
9
|
+
* @position Internal behavior hook; consumed by Wheel.tsx
|
|
10
|
+
*
|
|
11
|
+
* ## Why this exists
|
|
12
|
+
*
|
|
13
|
+
* A wheel is a scroll container, which is what makes it feel right under a
|
|
14
|
+
* finger: the platform supplies panning, momentum, rubber-banding and the
|
|
15
|
+
* snap. A mouse gets none of that. Browsers do not drag-scroll an overflow
|
|
16
|
+
* container — press and pull on one and nothing happens at all, which is
|
|
17
|
+
* exactly what a wheel invites you to try.
|
|
18
|
+
*
|
|
19
|
+
* That leaves the mouse with the scroll wheel and a click on a visible row.
|
|
20
|
+
* Both work, and neither is the gesture the control is shaped like. It
|
|
21
|
+
* matters more than "mouse users are not the target": the touch surface is
|
|
22
|
+
* reviewed, themed and screenshotted on desktop browsers, where a wheel that
|
|
23
|
+
* ignores the pointer reads as broken rather than as touch-only.
|
|
24
|
+
*
|
|
25
|
+
* ## Mouse only, deliberately
|
|
26
|
+
*
|
|
27
|
+
* Touch and pen already pan natively, and this hook would be strictly worse
|
|
28
|
+
* than what they get — no momentum, no rubber-banding, no compositor
|
|
29
|
+
* threading. So it ignores every pointer type but `mouse` and leaves those
|
|
30
|
+
* gestures untouched.
|
|
31
|
+
*
|
|
32
|
+
* ## Snap has to be suspended for the duration
|
|
33
|
+
*
|
|
34
|
+
* `scroll-snap-type: y mandatory` re-snaps after every scroll, including a
|
|
35
|
+
* programmatic one, so a drag that assigns `scrollTop` is fighting it on
|
|
36
|
+
* every frame. Measured: dragging 5px at a time with mandatory snap on, 7 of
|
|
37
|
+
* 8 steps were yanked back to a snap position — the wheel sticks to a row and
|
|
38
|
+
* then jumps a whole one. The drag therefore suspends snapping, and restores
|
|
39
|
+
* it once the release has settled somewhere legal.
|
|
40
|
+
*
|
|
41
|
+
* SYNC: When modified, update:
|
|
42
|
+
* - /packages/core/src/DateInput/Wheel.tsx
|
|
43
|
+
* - /packages/core/src/DateInput/DateInputTouch.test.tsx
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
import {useEffect, useRef} from 'react';
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Movement below this stays a click. A mouse shifts a pixel or two under the
|
|
50
|
+
* press of the button itself, and a wheel row is a click target.
|
|
51
|
+
*/
|
|
52
|
+
export const DRAG_SLOP = 4;
|
|
53
|
+
|
|
54
|
+
/** How long to wait for a release's settle before restoring snapping. */
|
|
55
|
+
const SETTLE_FALLBACK_MS = 260;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Let a mouse drag `ref`'s contents, on a container that already scrolls and
|
|
59
|
+
* snaps for touch.
|
|
60
|
+
*
|
|
61
|
+
* @param ref - the scroll container
|
|
62
|
+
* @param isEnabled - skip while the wheel is hidden; a hidden panel keeps its
|
|
63
|
+
* layout box, so its listeners would otherwise still be live
|
|
64
|
+
*/
|
|
65
|
+
export function usePointerDragScroll(
|
|
66
|
+
ref: React.RefObject<HTMLElement | null>,
|
|
67
|
+
isEnabled: boolean = true,
|
|
68
|
+
): void {
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
const element = ref.current;
|
|
71
|
+
if (element == null || !isEnabled) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let pointerId: number | null = null;
|
|
76
|
+
let originY = 0;
|
|
77
|
+
let originScrollTop = 0;
|
|
78
|
+
let isDragging = false;
|
|
79
|
+
// Survives from the drag's end to the click it produces, which is the
|
|
80
|
+
// whole reason it is not just `isDragging`.
|
|
81
|
+
let wasDragged = false;
|
|
82
|
+
let restoreTimer: number | undefined;
|
|
83
|
+
|
|
84
|
+
const restoreSnap = () => {
|
|
85
|
+
window.clearTimeout(restoreTimer);
|
|
86
|
+
element.removeEventListener('scrollend', restoreSnap);
|
|
87
|
+
// Back to the stylesheet's value rather than a hardcoded one, so this
|
|
88
|
+
// cannot drift from Wheel's own `scrollSnapType`.
|
|
89
|
+
element.style.removeProperty('scroll-snap-type');
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const suspendSnap = () => {
|
|
93
|
+
window.clearTimeout(restoreTimer);
|
|
94
|
+
element.removeEventListener('scrollend', restoreSnap);
|
|
95
|
+
element.style.scrollSnapType = 'none';
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const onPointerDown = (event: PointerEvent) => {
|
|
99
|
+
// Touch and pen pan natively, and better. Secondary buttons are for the
|
|
100
|
+
// context menu.
|
|
101
|
+
if (event.pointerType !== 'mouse' || event.button !== 0) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
// Keep the press away from BottomSheet, which starts its own
|
|
105
|
+
// drag-to-dismiss from a `pointerdown` on its body and CAPTURES the
|
|
106
|
+
// pointer for it. Two things go wrong if it gets there first: the drag
|
|
107
|
+
// below is fighting the sheet for the same gesture, and — measured on
|
|
108
|
+
// the calendar too, so this is not new — every later pointer event
|
|
109
|
+
// retargets to the sheet body, which means a click that wobbles more
|
|
110
|
+
// than a pixel or two never reaches the row under it and selects
|
|
111
|
+
// nothing at all.
|
|
112
|
+
//
|
|
113
|
+
// A native listener during the real bubble phase is what it takes:
|
|
114
|
+
// the sheet's handler is a React prop, delegated at the root container,
|
|
115
|
+
// which is an ANCESTOR of this element. Same reasoning as
|
|
116
|
+
// useOwnScrollGesture, and the same trade — mouse drag-to-dismiss now
|
|
117
|
+
// starts from the grab handle, the header or the scrim rather than
|
|
118
|
+
// from inside a wheel, which is where a picker sheet puts it anyway.
|
|
119
|
+
event.stopPropagation();
|
|
120
|
+
pointerId = event.pointerId;
|
|
121
|
+
originY = event.clientY;
|
|
122
|
+
originScrollTop = element.scrollTop;
|
|
123
|
+
isDragging = false;
|
|
124
|
+
// Not captured yet: a press that never moves has to stay a click on the
|
|
125
|
+
// row under it, and capturing here would retarget that click.
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const onPointerMove = (event: PointerEvent) => {
|
|
129
|
+
if (pointerId !== event.pointerId) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const delta = event.clientY - originY;
|
|
133
|
+
if (!isDragging) {
|
|
134
|
+
if (Math.abs(delta) < DRAG_SLOP) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
isDragging = true;
|
|
138
|
+
element.setPointerCapture(event.pointerId);
|
|
139
|
+
suspendSnap();
|
|
140
|
+
// Dragging across rows would otherwise select their text, leaving the
|
|
141
|
+
// wheel looking highlighted after the gesture.
|
|
142
|
+
element.style.userSelect = 'none';
|
|
143
|
+
}
|
|
144
|
+
// Content follows the hand: pull up and the list moves up, which means
|
|
145
|
+
// scrolling further down it.
|
|
146
|
+
element.scrollTop = originScrollTop - delta;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const onPointerEnd = (event: PointerEvent) => {
|
|
150
|
+
if (pointerId !== event.pointerId) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
pointerId = null;
|
|
154
|
+
if (!isDragging) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
isDragging = false;
|
|
158
|
+
wasDragged = true;
|
|
159
|
+
element.style.removeProperty('user-select');
|
|
160
|
+
if (element.hasPointerCapture(event.pointerId)) {
|
|
161
|
+
element.releasePointerCapture(event.pointerId);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// The release lands mid-row. Glide to the nearest one and only then let
|
|
165
|
+
// snapping back on — restoring it here instead would jump the distance
|
|
166
|
+
// rather than travel it, and a jump is what this hook exists to avoid.
|
|
167
|
+
const rowHeight = firstRowHeight(element);
|
|
168
|
+
if (rowHeight > 0) {
|
|
169
|
+
const nearest = Math.round(element.scrollTop / rowHeight) * rowHeight;
|
|
170
|
+
element.scrollTo({top: nearest, behavior: 'smooth'});
|
|
171
|
+
}
|
|
172
|
+
// `scrollend` is the honest signal; the timer covers browsers that do
|
|
173
|
+
// not fire it, and the case where the glide had nowhere to go.
|
|
174
|
+
// Not `{once: true}`: a listener that never fires is never removed by
|
|
175
|
+
// it, so a component unmounted mid-glide would leave one attached.
|
|
176
|
+
// `restoreSnap` removes it itself, whichever of the two gets there
|
|
177
|
+
// first, and the cleanup below calls `restoreSnap` on unmount.
|
|
178
|
+
//
|
|
179
|
+
// eslint-disable-next-line @eslint-react/web-api-no-leaked-event-listener -- removed by restoreSnap, which the cleanup calls
|
|
180
|
+
element.addEventListener('scrollend', restoreSnap);
|
|
181
|
+
restoreTimer = window.setTimeout(restoreSnap, SETTLE_FALLBACK_MS);
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// A drag ends over whichever row the mouse happens to be on, and that row
|
|
185
|
+
// would take the click as a selection. Swallow it — but only the one, and
|
|
186
|
+
// only after a real drag, so an ordinary click still selects.
|
|
187
|
+
const onClickCapture = (event: MouseEvent) => {
|
|
188
|
+
if (!wasDragged) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
wasDragged = false;
|
|
192
|
+
event.stopPropagation();
|
|
193
|
+
event.preventDefault();
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
element.addEventListener('pointerdown', onPointerDown);
|
|
197
|
+
element.addEventListener('pointermove', onPointerMove);
|
|
198
|
+
element.addEventListener('pointerup', onPointerEnd);
|
|
199
|
+
element.addEventListener('pointercancel', onPointerEnd);
|
|
200
|
+
element.addEventListener('click', onClickCapture, {capture: true});
|
|
201
|
+
return () => {
|
|
202
|
+
element.removeEventListener('pointerdown', onPointerDown);
|
|
203
|
+
element.removeEventListener('pointermove', onPointerMove);
|
|
204
|
+
element.removeEventListener('pointerup', onPointerEnd);
|
|
205
|
+
element.removeEventListener('pointercancel', onPointerEnd);
|
|
206
|
+
element.removeEventListener('click', onClickCapture, {capture: true});
|
|
207
|
+
restoreSnap();
|
|
208
|
+
};
|
|
209
|
+
}, [ref, isEnabled]);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* The pitch of the rows, read off the first one. Taken from layout rather
|
|
214
|
+
* than the token so a themed `--date-input-touch-wheel-item-size` is honoured
|
|
215
|
+
* without this hook having to know the variable exists.
|
|
216
|
+
*/
|
|
217
|
+
function firstRowHeight(element: HTMLElement): number {
|
|
218
|
+
const row = element.querySelector('[role="option"]');
|
|
219
|
+
return row instanceof HTMLElement ? row.offsetHeight : 0;
|
|
220
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file useScrollSettle.ts
|
|
7
|
+
* @input A scroll container ref and a settle callback
|
|
8
|
+
* @output Calls back once, after scrolling (including momentum) stops
|
|
9
|
+
* @position Internal behavior hook; consumed by Wheel.tsx and MonthScroller.tsx
|
|
10
|
+
*
|
|
11
|
+
* A snap scroller commits its value when it comes to rest, so it needs a
|
|
12
|
+
* "scrolling stopped" signal. `scrollend` is that signal, but it is recent
|
|
13
|
+
* enough that mobile Safari below 26 does not have it — and it is exactly the
|
|
14
|
+
* browser this component targets. So: listen for `scrollend` where it exists,
|
|
15
|
+
* and fall back to a quiet-period timer everywhere else. The same
|
|
16
|
+
* belt-and-braces pattern is in core's `useScrollSpy`.
|
|
17
|
+
*
|
|
18
|
+
* ## The two things the timer must not mistake for rest
|
|
19
|
+
*
|
|
20
|
+
* A quiet period is a guess, and on iOS it guesses wrong twice unless it is
|
|
21
|
+
* told otherwise. Both mistakes were behind the same bug: opening the
|
|
22
|
+
* month/year wheels, flicking to an earlier month, and watching the month
|
|
23
|
+
* climb on its own afterwards — on real iOS and the simulator, never in
|
|
24
|
+
* Chrome.
|
|
25
|
+
*
|
|
26
|
+
* 1. **A finger resting mid-drag is not rest.** Hold a wheel still for a
|
|
27
|
+
* moment without lifting and the scroll events stop, so the timer fires
|
|
28
|
+
* and the value commits to whatever is under the band. Chrome hides this
|
|
29
|
+
* because it has `scrollend`, which does not fire until the touch is
|
|
30
|
+
* released.
|
|
31
|
+
*
|
|
32
|
+
* 2. **Momentum is not rest either.** iOS momentum runs on the UI thread and
|
|
33
|
+
* keeps going for a second or more after the finger is gone, and its scroll
|
|
34
|
+
* events arrive irregularly — the gaps in the slow tail routinely exceed
|
|
35
|
+
* any sane quiet period. Chrome's compositor-driven momentum fires evenly,
|
|
36
|
+
* so the same timer never trips there.
|
|
37
|
+
*
|
|
38
|
+
* A premature settle is not just a wrong value; it feeds back. The commit
|
|
39
|
+
* moves the value, the value moves the scroller back onto the committed row,
|
|
40
|
+
* that programmatic scroll does not stop the momentum still running
|
|
41
|
+
* underneath, and the fresh scroll events start the next premature settle —
|
|
42
|
+
* each one landing a row further along, which is why the month climbed.
|
|
43
|
+
*
|
|
44
|
+
* So: the settle waits for the touch to end, and then for a quiet period
|
|
45
|
+
* measured from the LAST scroll event, which is what makes it outlast
|
|
46
|
+
* momentum however long it runs. {@link useScrollSettle} also reports whether
|
|
47
|
+
* the scroller is at rest, so a caller can refuse to reposition one that is
|
|
48
|
+
* still moving.
|
|
49
|
+
*
|
|
50
|
+
* SYNC: When modified, update DateInputTouch.test.tsx.
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
import {useEffect, useRef} from 'react';
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* How long the scroller must be quiet before a settle is assumed. Long enough
|
|
57
|
+
* to outlast the browser's own snap animation (~150-300ms after the finger
|
|
58
|
+
* lifts), or a wheel would commit to whichever option it was passing rather
|
|
59
|
+
* than the one it settles on.
|
|
60
|
+
*/
|
|
61
|
+
export const SCROLL_QUIET_MS = 220;
|
|
62
|
+
|
|
63
|
+
/** What {@link useScrollSettle} reports back about the scroller's state. */
|
|
64
|
+
export type ScrollSettleState = {
|
|
65
|
+
/**
|
|
66
|
+
* False from the first touch until the scroller has genuinely stopped —
|
|
67
|
+
* momentum included. Read it before repositioning the scroller: doing that
|
|
68
|
+
* mid-flight fights the platform and, on iOS, restarts the cycle described
|
|
69
|
+
* in the file header.
|
|
70
|
+
*/
|
|
71
|
+
isAtRestRef: React.RefObject<boolean>;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Run `onSettle` when `ref`'s element stops scrolling.
|
|
76
|
+
*
|
|
77
|
+
* @param ref - the scroll container
|
|
78
|
+
* @param onSettle - called with the settled element; may change every render
|
|
79
|
+
* @param isEnabled - skip attaching while false (e.g. a hidden panel)
|
|
80
|
+
*/
|
|
81
|
+
export function useScrollSettle(
|
|
82
|
+
ref: React.RefObject<HTMLElement | null>,
|
|
83
|
+
onSettle: (element: HTMLElement) => void,
|
|
84
|
+
isEnabled: boolean = true,
|
|
85
|
+
): ScrollSettleState {
|
|
86
|
+
// The callback closes over render-fresh values (the option list, the current
|
|
87
|
+
// value); keeping it in a ref means the listeners attach once instead of
|
|
88
|
+
// re-attaching mid-scroll, which would drop an in-flight settle.
|
|
89
|
+
const onSettleRef = useRef(onSettle);
|
|
90
|
+
onSettleRef.current = onSettle;
|
|
91
|
+
|
|
92
|
+
const isAtRestRef = useRef(true);
|
|
93
|
+
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
const element = ref.current;
|
|
96
|
+
if (element == null || !isEnabled) {
|
|
97
|
+
isAtRestRef.current = true;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
102
|
+
let hasSettled = false;
|
|
103
|
+
let isTouching = false;
|
|
104
|
+
|
|
105
|
+
const settle = () => {
|
|
106
|
+
// A finger still on the glass is not rest, however quiet the scroller
|
|
107
|
+
// has gone. The release re-arms this.
|
|
108
|
+
if (hasSettled || isTouching) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
hasSettled = true;
|
|
112
|
+
isAtRestRef.current = true;
|
|
113
|
+
clearTimeout(timer);
|
|
114
|
+
onSettleRef.current(element);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const arm = () => {
|
|
118
|
+
clearTimeout(timer);
|
|
119
|
+
timer = setTimeout(settle, SCROLL_QUIET_MS);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const onScroll = () => {
|
|
123
|
+
// A fresh scroll re-opens the window: whatever we settle on now is
|
|
124
|
+
// stale, and the settle that matters is the one after this gesture.
|
|
125
|
+
// Momentum keeps arriving here, so the quiet period is measured from
|
|
126
|
+
// the last of it however long it runs.
|
|
127
|
+
hasSettled = false;
|
|
128
|
+
isAtRestRef.current = false;
|
|
129
|
+
arm();
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const onTouchStart = () => {
|
|
133
|
+
isTouching = true;
|
|
134
|
+
hasSettled = false;
|
|
135
|
+
isAtRestRef.current = false;
|
|
136
|
+
clearTimeout(timer);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const onTouchEnd = () => {
|
|
140
|
+
isTouching = false;
|
|
141
|
+
// Momentum may carry on from here and will re-arm this on its own; the
|
|
142
|
+
// timer covers the case where the finger lifted without a fling, which
|
|
143
|
+
// produces no further scroll events at all.
|
|
144
|
+
arm();
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
element.addEventListener('scroll', onScroll, {passive: true});
|
|
148
|
+
element.addEventListener('scrollend', settle);
|
|
149
|
+
element.addEventListener('touchstart', onTouchStart, {passive: true});
|
|
150
|
+
element.addEventListener('touchend', onTouchEnd, {passive: true});
|
|
151
|
+
element.addEventListener('touchcancel', onTouchEnd, {passive: true});
|
|
152
|
+
|
|
153
|
+
return () => {
|
|
154
|
+
clearTimeout(timer);
|
|
155
|
+
element.removeEventListener('scroll', onScroll);
|
|
156
|
+
element.removeEventListener('scrollend', settle);
|
|
157
|
+
element.removeEventListener('touchstart', onTouchStart);
|
|
158
|
+
element.removeEventListener('touchend', onTouchEnd);
|
|
159
|
+
element.removeEventListener('touchcancel', onTouchEnd);
|
|
160
|
+
};
|
|
161
|
+
}, [ref, isEnabled]);
|
|
162
|
+
|
|
163
|
+
return {isAtRestRef};
|
|
164
|
+
}
|
|
@@ -102,7 +102,7 @@ export const docs = {
|
|
|
102
102
|
name: 'maxRangeSpan',
|
|
103
103
|
type: 'number',
|
|
104
104
|
description:
|
|
105
|
-
'Maximum days a selected range may span, counting both endpoints (`7` = a 7-day window, start + 6). Once a start is picked, days beyond this distance are disabled so the range cannot stretch past the cap. Rolling window relative to the start
|
|
105
|
+
'Maximum days a selected range may span, counting both endpoints (`7` = a 7-day window, start + 6). Once a start is picked, days beyond this distance are disabled so the range cannot stretch past the cap. Rolling window relative to the start; for fixed calendar bounds use `min`/`max`. Constrains selection only; it never rewrites a `value` already wider than the cap (flag that with `status`).',
|
|
106
106
|
},
|
|
107
107
|
{
|
|
108
108
|
name: 'minRangeSpan',
|