@astryxdesign/core 0.4.7 → 0.5.0-canary.009bcb3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +161 -1
- package/dist/AlertDialog/AlertDialog.d.ts +1 -1
- package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
- package/dist/AlertDialog/AlertDialog.js +45 -20
- package/dist/AppShell/AppShell.d.ts.map +1 -1
- package/dist/AppShell/AppShell.js +3 -2
- package/dist/AspectRatio/AspectRatio.d.ts +24 -0
- package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
- package/dist/AspectRatio/AspectRatio.js +23 -4
- package/dist/Avatar/Avatar.d.ts +25 -1
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +58 -19
- package/dist/Avatar/index.d.ts +2 -2
- package/dist/Avatar/index.d.ts.map +1 -1
- package/dist/Avatar/index.js +1 -1
- package/dist/AvatarGroup/AvatarGroup.d.ts +9 -2
- package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroup.js +8 -4
- package/dist/AvatarGroup/AvatarGroupContext.d.ts +2 -1
- package/dist/AvatarGroup/AvatarGroupContext.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.js +10 -6
- package/dist/Banner/Banner.d.ts +49 -15
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +62 -20
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheet.js +17 -4
- package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetSwitcher.js +3 -2
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +11 -10
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +11 -6
- package/dist/ButtonGroup/ButtonGroup.d.ts +5 -1
- package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
- package/dist/ButtonGroup/ButtonGroup.js +26 -7
- package/dist/Calendar/Calendar.d.ts +7 -3
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +21 -23
- package/dist/Calendar/getInitialFocusDate.d.ts +46 -0
- package/dist/Calendar/getInitialFocusDate.d.ts.map +1 -0
- package/dist/Calendar/getInitialFocusDate.js +62 -0
- package/dist/Calendar/styles.d.ts +5 -14
- package/dist/Calendar/styles.d.ts.map +1 -1
- package/dist/Calendar/styles.js +15 -15
- package/dist/Card/Card.d.ts +12 -12
- package/dist/Card/Card.d.ts.map +1 -1
- package/dist/Card/Card.js +17 -9
- package/dist/Card/index.d.ts +35 -1
- package/dist/Card/index.d.ts.map +1 -1
- package/dist/Card/index.js +21 -1
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +3 -3
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +2 -3
- package/dist/Chat/ChatLayout.d.ts.map +1 -1
- package/dist/Chat/ChatLayout.js +3 -3
- package/dist/Chat/ChatMessageList.d.ts +22 -3
- package/dist/Chat/ChatMessageList.d.ts.map +1 -1
- package/dist/Chat/ChatMessageList.js +6 -3
- package/dist/Chat/ChatTokenizedText.d.ts +2 -0
- package/dist/Chat/ChatTokenizedText.d.ts.map +1 -1
- package/dist/Chat/ChatTokenizedText.js +11 -6
- package/dist/Chat/useChatComposerTokens.js +1 -1
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +10 -3
- package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
- package/dist/ClickableCard/ClickableCard.js +3 -3
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +15 -4
- package/dist/CodeBlock/highlightRanges.js +1 -1
- package/dist/CodeBlock/highlightStyles.js +13 -13
- package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteInput.js +3 -2
- package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteItem.js +3 -2
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +1 -3
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +4 -3
- package/dist/DateInput/DateInput.d.ts +43 -0
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +22 -2
- package/dist/DateInput/NativeDateField.d.ts +10 -0
- package/dist/DateInput/NativeDateField.d.ts.map +1 -0
- package/dist/DateInput/NativeDateField.js +494 -0
- package/dist/DateInput/TouchDateField.d.ts.map +1 -1
- package/dist/DateInput/TouchDateField.js +82 -23
- package/dist/DateInput/index.d.ts +1 -1
- package/dist/DateInput/index.d.ts.map +1 -1
- package/dist/DateInput/nativeDateSegments.d.ts +22 -0
- package/dist/DateInput/nativeDateSegments.d.ts.map +1 -0
- package/dist/DateInput/nativeDateSegments.js +111 -0
- package/dist/DateRangeInput/DateRangeInput.d.ts +4 -2
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts +21 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +406 -12
- package/dist/DateTimeInput/index.d.ts +1 -1
- package/dist/DateTimeInput/index.d.ts.map +1 -1
- package/dist/Dialog/Dialog.d.ts +21 -1
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +110 -58
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +4 -13
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.js +6 -5
- package/dist/Field/FieldLabel.d.ts.map +1 -1
- package/dist/Field/FieldLabel.js +9 -1
- package/dist/Field/InputClearButton.d.ts +9 -3
- package/dist/Field/InputClearButton.d.ts.map +1 -1
- package/dist/Field/InputClearButton.js +14 -3
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +3 -2
- package/dist/Heading/Heading.d.ts.map +1 -1
- package/dist/Heading/Heading.js +6 -3
- package/dist/HoverCard/HoverCard.d.ts +6 -0
- package/dist/HoverCard/HoverCard.d.ts.map +1 -1
- package/dist/HoverCard/HoverCard.js +104 -3
- package/dist/HoverCard/useHoverCard.d.ts +19 -2
- package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +74 -26
- package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
- package/dist/Indicator/CheckboxIndicator.js +6 -2
- package/dist/Item/Item.d.ts.map +1 -1
- package/dist/Item/Item.js +4 -2
- package/dist/Kbd/Kbd.d.ts.map +1 -1
- package/dist/Kbd/Kbd.js +2 -19
- package/dist/Layer/LayerDepthContext.d.ts +52 -0
- package/dist/Layer/LayerDepthContext.d.ts.map +1 -0
- package/dist/Layer/LayerDepthContext.js +66 -0
- package/dist/Layer/gestureCounter.d.ts +8 -0
- package/dist/Layer/gestureCounter.d.ts.map +1 -0
- package/dist/Layer/gestureCounter.js +53 -0
- package/dist/Layer/index.d.ts +3 -0
- package/dist/Layer/index.d.ts.map +1 -1
- package/dist/Layer/index.js +4 -0
- package/dist/Layer/layerStack.d.ts +98 -0
- package/dist/Layer/layerStack.d.ts.map +1 -0
- package/dist/Layer/layerStack.js +306 -0
- package/dist/Layer/useLayer.d.ts +23 -13
- package/dist/Layer/useLayer.d.ts.map +1 -1
- package/dist/Layer/useLayer.js +128 -8
- package/dist/Layer/useLayerDismissal.d.ts +93 -0
- package/dist/Layer/useLayerDismissal.d.ts.map +1 -0
- package/dist/Layer/useLayerDismissal.js +91 -0
- package/dist/Lightbox/Lightbox.d.ts +1 -0
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +103 -81
- package/dist/List/List.d.ts +1 -1
- package/dist/List/List.d.ts.map +1 -1
- package/dist/List/List.js +15 -4
- package/dist/Markdown/Markdown.d.ts +1 -1
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.js +20 -7
- package/dist/Markdown/index.d.ts +1 -1
- package/dist/Markdown/index.d.ts.map +1 -1
- package/dist/Markdown/parser.d.ts +38 -1
- package/dist/Markdown/parser.d.ts.map +1 -1
- package/dist/Markdown/parser.js +182 -32
- package/dist/MetadataList/MetadataListItem.d.ts +1 -1
- package/dist/MetadataList/MetadataListItem.d.ts.map +1 -1
- package/dist/MetadataList/MetadataListItem.js +4 -1
- package/dist/MobileNav/MobileNav.d.ts +6 -1
- package/dist/MobileNav/MobileNav.d.ts.map +1 -1
- package/dist/MobileNav/MobileNav.js +61 -36
- package/dist/MultiSelector/MultiSelector.d.ts +32 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +73 -15
- package/dist/MultiSelector/hooks.d.ts +7 -1
- package/dist/MultiSelector/hooks.d.ts.map +1 -1
- package/dist/MultiSelector/hooks.js +3 -2
- package/dist/MultiSelector/index.d.ts +1 -1
- package/dist/MultiSelector/index.d.ts.map +1 -1
- package/dist/NavIcon/NavIcon.d.ts.map +1 -1
- package/dist/NavIcon/NavIcon.js +5 -1
- package/dist/NavMenu/NavHeadingMenu.d.ts +1 -1
- package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
- package/dist/NavMenu/NavHeadingMenu.js +9 -5
- package/dist/NumberInput/NumberInput.d.ts +6 -1
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +54 -104
- package/dist/NumberInput/numberInputCommit.d.ts +34 -0
- package/dist/NumberInput/numberInputCommit.d.ts.map +1 -0
- package/dist/NumberInput/numberInputCommit.js +71 -0
- package/dist/NumberInput/numberParser.d.ts +64 -0
- package/dist/NumberInput/numberParser.d.ts.map +1 -0
- package/dist/NumberInput/numberParser.js +350 -0
- package/dist/Outline/Outline.d.ts.map +1 -1
- package/dist/Outline/Outline.js +3 -2
- package/dist/OverflowList/OverflowList.d.ts.map +1 -1
- package/dist/OverflowList/OverflowList.js +3 -2
- package/dist/Overlay/Overlay.d.ts.map +1 -1
- package/dist/Overlay/Overlay.js +3 -2
- package/dist/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +1 -9
- package/dist/Popover/usePopover.d.ts +7 -41
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +40 -21
- package/dist/PowerSearch/PowerSearch.d.ts +8 -2
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +19 -6
- package/dist/PowerSearch/PowerSearchEditPopover.d.ts +3 -1
- package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchEditPopover.js +28 -6
- package/dist/PowerSearch/PowerSearchValueEditor.d.ts +3 -1
- package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchValueEditor.js +110 -27
- package/dist/PowerSearch/types.d.ts +2 -0
- package/dist/PowerSearch/types.d.ts.map +1 -1
- package/dist/PowerSearch/usePowerSearchSource.d.ts +6 -1
- package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
- package/dist/PowerSearch/usePowerSearchSource.js +14 -5
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +18 -5
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +3 -2
- package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControl.js +3 -2
- package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
- package/dist/SelectableCard/SelectableCard.js +12 -3
- package/dist/Selector/Selector.d.ts +18 -1
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +91 -18
- package/dist/Selector/hooks.d.ts +7 -1
- package/dist/Selector/hooks.d.ts.map +1 -1
- package/dist/Selector/hooks.js +3 -2
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +4 -3
- package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
- package/dist/SideNav/SideNavHeading.js +8 -7
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +4 -3
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +37 -5
- package/dist/Spinner/Spinner.d.ts +3 -3
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +130 -78
- package/dist/StatusDot/StatusDot.d.ts.map +1 -1
- package/dist/StatusDot/StatusDot.js +6 -1
- package/dist/Stepper/Step.d.ts +140 -0
- package/dist/Stepper/Step.d.ts.map +1 -0
- package/dist/Stepper/Step.js +1045 -0
- package/dist/Stepper/StepStatus.d.ts +24 -0
- package/dist/Stepper/StepStatus.d.ts.map +1 -0
- package/dist/Stepper/StepStatus.js +1 -0
- package/dist/Stepper/Stepper.d.ts +93 -0
- package/dist/Stepper/Stepper.d.ts.map +1 -0
- package/dist/Stepper/Stepper.js +184 -0
- package/dist/Stepper/StepperContext.d.ts +41 -0
- package/dist/Stepper/StepperContext.d.ts.map +1 -0
- package/dist/Stepper/StepperContext.js +34 -0
- package/dist/Stepper/index.d.ts +8 -0
- package/dist/Stepper/index.d.ts.map +1 -0
- package/dist/Stepper/index.js +7 -0
- package/dist/Stepper/stepper.stylex.d.ts +15 -0
- package/dist/Stepper/stepper.stylex.d.ts.map +1 -0
- package/dist/Stepper/stepper.stylex.js +20 -0
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +8 -3
- package/dist/TabList/Tab.d.ts +15 -2
- package/dist/TabList/Tab.d.ts.map +1 -1
- package/dist/TabList/Tab.js +32 -7
- package/dist/TabList/TabList.d.ts +46 -7
- package/dist/TabList/TabList.d.ts.map +1 -1
- package/dist/TabList/TabList.js +353 -23
- package/dist/TabList/TabListContext.d.ts +11 -1
- package/dist/TabList/TabListContext.d.ts.map +1 -1
- package/dist/TabList/TabListContext.js +11 -2
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +3 -2
- package/dist/TabList/index.d.ts +2 -2
- package/dist/TabList/index.d.ts.map +1 -1
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +10 -2
- package/dist/Table/Table.d.ts.map +1 -1
- package/dist/Table/Table.js +3 -6
- package/dist/Table/TableRow.d.ts +16 -5
- package/dist/Table/TableRow.d.ts.map +1 -1
- package/dist/Table/TableRow.js +16 -5
- package/dist/Table/plugins/filtering/useTableFiltering.d.ts.map +1 -1
- package/dist/Table/plugins/filtering/useTableFiltering.js +73 -4
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts +26 -0
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +54 -11
- package/dist/Table/plugins/pagination/useTablePagination.d.ts +3 -0
- package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
- package/dist/Table/plugins/pagination/useTablePagination.js +21 -4
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +7 -2
- package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -1
- package/dist/Table/plugins/rowStatus/useTableRowStatus.js +10 -3
- package/dist/Table/plugins/selection/useTableSelection.d.ts +16 -0
- package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
- package/dist/Table/plugins/selection/useTableSelection.js +43 -5
- package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
- package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +3 -4
- package/dist/Table/types.d.ts +22 -4
- package/dist/Table/types.d.ts.map +1 -1
- package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
- package/dist/Table/useBaseTablePlugins.js +5 -0
- package/dist/Text/Text.d.ts.map +1 -1
- package/dist/Text/Text.js +6 -3
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +16 -10
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +3 -2
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +3 -2
- package/dist/Timestamp/Timestamp.d.ts +1 -1
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +15 -21
- package/dist/Toast/Toast.d.ts.map +1 -1
- package/dist/Toast/Toast.js +22 -7
- package/dist/Toast/ToastViewport.d.ts +1 -0
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +71 -18
- package/dist/Token/Token.d.ts +1 -1
- package/dist/Token/Token.d.ts.map +1 -1
- package/dist/Token/Token.js +20 -9
- package/dist/Token/TokenLink.d.ts +1 -1
- package/dist/Token/TokenLink.d.ts.map +1 -1
- package/dist/Token/TokenLink.js +7 -4
- package/dist/Tokenizer/Tokenizer.d.ts +14 -1
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +33 -21
- package/dist/Tooltip/Tooltip.d.ts +5 -0
- package/dist/Tooltip/Tooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.d.ts +5 -0
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +39 -24
- package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
- package/dist/TopNav/TopNavHeading.js +5 -4
- package/dist/TopNav/TopNavMegaMenu.d.ts +1 -1
- package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenu.js +31 -11
- package/dist/TopNav/TopNavMenu.d.ts +1 -1
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +22 -7
- package/dist/TreeList/TreeList.d.ts +1 -1
- package/dist/TreeList/TreeList.d.ts.map +1 -1
- package/dist/TreeList/TreeList.js +7 -2
- package/dist/Typeahead/BaseTypeahead.d.ts +35 -0
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +119 -37
- package/dist/Typeahead/Typeahead.d.ts +8 -1
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +5 -2
- package/dist/Typeahead/TypeaheadItem.d.ts +1 -1
- package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
- package/dist/Typeahead/TypeaheadItem.js +7 -2
- package/dist/astryx.css +113 -11
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useFocusTrap.d.ts +7 -4
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +60 -95
- package/dist/hooks/useHotkeys.d.ts.map +1 -1
- package/dist/hooks/useHotkeys.js +3 -18
- package/dist/hooks/useMergedRefs.d.ts +18 -0
- package/dist/hooks/useMergedRefs.d.ts.map +1 -0
- package/dist/hooks/useMergedRefs.js +25 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/theme/Theme.d.ts.map +1 -1
- package/dist/theme/Theme.js +46 -10
- package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
- package/dist/theme/derivedVarRegistry.js +24 -4
- package/dist/theme/generateThemeRules.d.ts +21 -0
- package/dist/theme/generateThemeRules.d.ts.map +1 -1
- package/dist/theme/generateThemeRules.js +26 -0
- package/dist/theme/syntax/tokens.d.ts +1 -1
- package/dist/theme/syntax/tokens.d.ts.map +1 -1
- package/dist/theme/syntax/tokens.js +5 -2
- package/dist/utils/groupItems.d.ts +5 -1
- package/dist/utils/groupItems.d.ts.map +1 -1
- package/dist/utils/groupItems.js +9 -9
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/isApplePlatform.d.ts +18 -0
- package/dist/utils/isApplePlatform.d.ts.map +1 -0
- package/dist/utils/isApplePlatform.js +37 -0
- package/dist/utils/sharedResizeObserver.d.ts.map +1 -1
- package/dist/utils/sharedResizeObserver.js +10 -1
- package/dist/utils/timeParser.d.ts +1 -1
- package/dist/utils/timeParser.d.ts.map +1 -1
- package/dist/utils/timeParser.js +26 -12
- package/locales/af-ZA.json +6 -6
- package/locales/ar-SA.json +6 -6
- package/locales/ca-ES.json +5 -5
- package/locales/cs-CZ.json +6 -6
- package/locales/da-DK.json +6 -6
- package/locales/de-DE.json +6 -6
- package/locales/el-GR.json +6 -6
- package/locales/en.json +163 -11
- package/locales/es-ES.json +5 -5
- package/locales/fi-FI.json +6 -6
- package/locales/fr-FR.json +6 -6
- package/locales/he-IL.json +6 -6
- package/locales/hu-HU.json +6 -6
- package/locales/it-IT.json +6 -6
- package/locales/ja-JP.json +6 -6
- package/locales/ko-KR.json +6 -6
- package/locales/nl-NL.json +6 -6
- package/locales/no-NO.json +6 -6
- package/locales/pl-PL.json +6 -6
- package/locales/pseudo.json +114 -0
- package/locales/pt-BR.json +6 -6
- package/locales/pt-PT.json +6 -6
- package/locales/ro-RO.json +6 -6
- package/locales/ru-RU.json +6 -6
- package/locales/sr-SP.json +6 -6
- package/locales/sv-SE.json +6 -6
- package/locales/tr-TR.json +6 -6
- package/locales/uk-UA.json +6 -6
- package/locales/vi-VN.json +6 -6
- package/locales/zh-CN.json +6 -6
- package/locales/zh-TW.json +6 -6
- package/package.json +8 -3
- package/src/AlertDialog/AlertDialog.doc.mjs +114 -30
- package/src/AlertDialog/AlertDialog.test.tsx +211 -2
- package/src/AlertDialog/AlertDialog.tsx +62 -20
- package/src/AppShell/AppShell.tsx +3 -2
- package/src/AspectRatio/AspectRatio.doc.mjs +6 -3
- package/src/AspectRatio/AspectRatio.test.tsx +57 -10
- package/src/AspectRatio/AspectRatio.tsx +40 -1
- package/src/Avatar/Avatar.doc.mjs +14 -3
- package/src/Avatar/Avatar.test.tsx +97 -0
- package/src/Avatar/Avatar.tsx +125 -52
- package/src/Avatar/index.ts +2 -2
- package/src/AvatarGroup/AvatarGroup.doc.mjs +8 -2
- package/src/AvatarGroup/AvatarGroup.test.tsx +24 -0
- package/src/AvatarGroup/AvatarGroup.tsx +15 -6
- package/src/AvatarGroup/AvatarGroupContext.ts +5 -3
- package/src/AvatarGroup/AvatarGroupOverflow.tsx +10 -4
- package/src/Banner/Banner.doc.mjs +22 -11
- package/src/Banner/Banner.test.tsx +177 -25
- package/src/Banner/Banner.tsx +93 -27
- package/src/BottomSheet/BottomSheet.test.tsx +123 -0
- package/src/BottomSheet/BottomSheet.tsx +17 -4
- package/src/BottomSheet/BottomSheetSwitcher.tsx +3 -2
- package/src/Breadcrumbs/BreadcrumbItem.tsx +9 -5
- package/src/Button/Button.doc.mjs +24 -1
- package/src/Button/Button.test.tsx +32 -4
- package/src/Button/Button.tsx +12 -5
- package/src/ButtonGroup/ButtonGroup.doc.mjs +10 -1
- package/src/ButtonGroup/ButtonGroup.test.tsx +404 -1
- package/src/ButtonGroup/ButtonGroup.tsx +23 -5
- package/src/Calendar/Calendar.doc.mjs +7 -6
- package/src/Calendar/Calendar.test.tsx +143 -0
- package/src/Calendar/Calendar.tsx +30 -26
- package/src/Calendar/getInitialFocusDate.test.ts +94 -0
- package/src/Calendar/getInitialFocusDate.ts +93 -0
- package/src/Calendar/styles.ts +49 -31
- package/src/Card/Card.doc.mjs +10 -7
- package/src/Card/Card.test.tsx +113 -19
- package/src/Card/Card.tsx +67 -89
- package/src/Card/index.ts +36 -1
- package/src/Carousel/Carousel.tsx +3 -2
- package/src/Chat/ChatComposerInput.tsx +5 -1
- package/src/Chat/ChatLayout.tsx +3 -2
- package/src/Chat/ChatMessageList.doc.mjs +9 -1
- package/src/Chat/ChatMessageList.test.tsx +46 -0
- package/src/Chat/ChatMessageList.tsx +28 -4
- package/src/Chat/ChatTokenizedText.tsx +17 -10
- package/src/Chat/useChatComposerTokens.ts +1 -1
- package/src/CheckboxInput/CheckboxInput.doc.mjs +2 -0
- package/src/CheckboxInput/CheckboxInput.test.tsx +15 -0
- package/src/CheckboxInput/CheckboxInput.tsx +8 -2
- package/src/ClickableCard/ClickableCard.tsx +3 -2
- package/src/CodeBlock/CodeBlock.doc.mjs +16 -4
- package/src/CodeBlock/CodeBlock.test.tsx +37 -0
- package/src/CodeBlock/CodeBlock.tsx +20 -4
- package/src/CodeBlock/highlightRanges.ts +1 -1
- package/src/CodeBlock/highlightStyles.ts +13 -13
- package/src/CommandPalette/CommandPaletteInput.tsx +3 -2
- package/src/CommandPalette/CommandPaletteItem.tsx +3 -2
- package/src/ComplexSelector/ComplexSelector.test.tsx +32 -0
- package/src/ComplexSelector/ComplexSelector.tsx +1 -3
- package/src/ContextMenu/ContextMenu.doc.mjs +5 -0
- package/src/ContextMenu/ContextMenu.tsx +4 -3
- package/src/DateInput/DateInput.doc.mjs +16 -0
- package/src/DateInput/DateInput.tsx +55 -2
- package/src/DateInput/DateInputTouch.test.tsx +146 -32
- package/src/DateInput/NativeDateField.test.tsx +635 -0
- package/src/DateInput/NativeDateField.tsx +641 -0
- package/src/DateInput/TouchDateField.tsx +108 -24
- package/src/DateInput/index.ts +1 -0
- package/src/DateInput/nativeDateSegments.ts +127 -0
- package/src/DateRangeInput/DateRangeInput.doc.mjs +2 -2
- package/src/DateRangeInput/DateRangeInput.test.tsx +22 -0
- package/src/DateRangeInput/DateRangeInput.tsx +4 -2
- package/src/DateTimeInput/DateTimeInput.doc.mjs +18 -0
- package/src/DateTimeInput/DateTimeInput.test.tsx +1088 -0
- package/src/DateTimeInput/DateTimeInput.tsx +556 -24
- package/src/DateTimeInput/index.ts +1 -0
- package/src/Dialog/Dialog.doc.mjs +8 -3
- package/src/Dialog/Dialog.test.tsx +429 -2
- package/src/Dialog/Dialog.tsx +133 -58
- package/src/DropdownMenu/DropdownMenu.doc.mjs +6 -0
- package/src/DropdownMenu/DropdownMenu.test.tsx +44 -10
- package/src/DropdownMenu/DropdownMenu.tsx +4 -13
- package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +8 -5
- package/src/DropdownMenu/DropdownMenuSubMenu.tsx +8 -11
- package/src/Field/Field.doc.mjs +13 -2
- package/src/Field/FieldLabel.test.tsx +29 -0
- package/src/Field/FieldLabel.tsx +7 -0
- package/src/Field/InputClearButton.test.tsx +15 -1
- package/src/Field/InputClearButton.tsx +28 -4
- package/src/FileInput/FileInput.tsx +3 -2
- package/src/Heading/Heading.test.tsx +23 -1
- package/src/Heading/Heading.tsx +6 -3
- package/src/HoverCard/HoverCard.doc.mjs +13 -2
- package/src/HoverCard/HoverCard.test.tsx +154 -4
- package/src/HoverCard/HoverCard.tsx +143 -3
- package/src/HoverCard/useHoverCard.tsx +104 -33
- package/src/Indicator/CheckboxIndicator.tsx +14 -8
- package/src/Indicator/Indicator.doc.mjs +2 -0
- package/src/Item/Item.tsx +5 -4
- package/src/Kbd/Kbd.tsx +2 -19
- package/src/Layer/LayerDepthContext.tsx +62 -0
- package/src/Layer/gestureCounter.ts +57 -0
- package/src/Layer/index.ts +9 -0
- package/src/Layer/layerDismissalFamilies.test.tsx +359 -0
- package/src/Layer/layerDismissalInvariants.test.tsx +417 -0
- package/src/Layer/layerStack.ts +362 -0
- package/src/Layer/useLayer.test.tsx +324 -15
- package/src/Layer/useLayer.tsx +164 -8
- package/src/Layer/useLayerDismissal.test.tsx +469 -0
- package/src/Layer/useLayerDismissal.ts +173 -0
- package/src/Layout/Layout.doc.mjs +2 -2
- package/src/Layout/__tests__/edgeCompensation.test.tsx +1 -1
- package/src/Lightbox/Lightbox.doc.mjs +12 -0
- package/src/Lightbox/Lightbox.tsx +124 -102
- package/src/Link/Link.doc.mjs +39 -0
- package/src/List/List.test.tsx +38 -0
- package/src/List/List.tsx +8 -1
- package/src/Markdown/Markdown.doc.mjs +5 -0
- package/src/Markdown/Markdown.test.tsx +37 -0
- package/src/Markdown/Markdown.tsx +16 -5
- package/src/Markdown/incremental.test.ts +16 -2
- package/src/Markdown/index.ts +1 -0
- package/src/Markdown/parser.perf.test.ts +96 -1
- package/src/Markdown/parser.test.ts +178 -2
- package/src/Markdown/parser.ts +253 -32
- package/src/MetadataList/MetadataList.test.tsx +75 -2
- package/src/MetadataList/MetadataListItem.tsx +5 -2
- package/src/MobileNav/MobileNav.tsx +62 -36
- package/src/MoreMenu/MoreMenu.doc.mjs +7 -1
- package/src/MultiSelector/MultiSelector.doc.mjs +49 -1
- package/src/MultiSelector/MultiSelector.test.tsx +311 -0
- package/src/MultiSelector/MultiSelector.tsx +151 -15
- package/src/MultiSelector/hooks.ts +9 -2
- package/src/MultiSelector/index.ts +1 -0
- package/src/NavIcon/NavIcon.doc.mjs +8 -2
- package/src/NavIcon/NavIcon.test.tsx +9 -0
- package/src/NavIcon/NavIcon.tsx +5 -1
- package/src/NavMenu/NavHeadingMenu.test.tsx +30 -0
- package/src/NavMenu/NavHeadingMenu.tsx +9 -5
- package/src/NumberInput/NumberInput.doc.mjs +31 -7
- package/src/NumberInput/NumberInput.test.tsx +436 -8
- package/src/NumberInput/NumberInput.tsx +62 -114
- package/src/NumberInput/numberInputCommit.test.ts +72 -0
- package/src/NumberInput/numberInputCommit.ts +103 -0
- package/src/NumberInput/numberParser.docblock.test.ts +234 -0
- package/src/NumberInput/numberParser.test.ts +222 -0
- package/src/NumberInput/numberParser.ts +403 -0
- package/src/Outline/Outline.tsx +3 -2
- package/src/OverflowList/OverflowList.tsx +3 -2
- package/src/Overlay/Overlay.tsx +3 -2
- package/src/Pagination/Pagination.test.tsx +3 -2
- package/src/Popover/Popover.test.tsx +14 -0
- package/src/Popover/Popover.tsx +1 -9
- package/src/Popover/usePopover.tsx +57 -32
- package/src/PowerSearch/PowerSearch.doc.mjs +160 -28
- package/src/PowerSearch/PowerSearch.test.tsx +240 -1
- package/src/PowerSearch/PowerSearch.tsx +34 -5
- package/src/PowerSearch/PowerSearchEditPopover.test.tsx +288 -0
- package/src/PowerSearch/PowerSearchEditPopover.tsx +29 -2
- package/src/PowerSearch/PowerSearchValueEditor.test.tsx +111 -1
- package/src/PowerSearch/PowerSearchValueEditor.tsx +124 -10
- package/src/PowerSearch/types.ts +2 -0
- package/src/PowerSearch/usePowerSearchSource.test.ts +82 -2
- package/src/PowerSearch/usePowerSearchSource.ts +13 -3
- package/src/ProgressBar/ProgressBar.doc.mjs +42 -6
- package/src/ProgressBar/ProgressBar.test.tsx +24 -0
- package/src/ProgressBar/ProgressBar.tsx +28 -8
- package/src/Resizable/ResizeHandle.tsx +3 -2
- package/src/SegmentedControl/SegmentedControl.tsx +3 -2
- package/src/SelectableCard/SelectableCard.doc.mjs +3 -0
- package/src/SelectableCard/SelectableCard.tsx +19 -2
- package/src/Selector/Selector.doc.mjs +26 -1
- package/src/Selector/Selector.test.tsx +346 -1
- package/src/Selector/Selector.tsx +148 -21
- package/src/Selector/hooks.ts +17 -2
- package/src/SideNav/SideNav.doc.mjs +6 -6
- package/src/SideNav/SideNav.tsx +4 -2
- package/src/SideNav/SideNavHeading.tsx +12 -12
- package/src/SideNav/SideNavItem.tsx +4 -2
- package/src/Slider/Slider.test.tsx +106 -1
- package/src/Slider/Slider.tsx +55 -3
- package/src/Spinner/Spinner.test.tsx +147 -1
- package/src/Spinner/Spinner.tsx +120 -101
- package/src/StatusDot/StatusDot.doc.mjs +8 -2
- package/src/StatusDot/StatusDot.test.tsx +9 -0
- package/src/StatusDot/StatusDot.tsx +7 -1
- package/src/Stepper/Step.doc.mjs +98 -0
- package/src/Stepper/Step.tsx +1638 -0
- package/src/Stepper/StepStatus.ts +26 -0
- package/src/Stepper/Stepper.doc.mjs +370 -0
- package/src/Stepper/Stepper.test.tsx +1024 -0
- package/src/Stepper/Stepper.tsx +258 -0
- package/src/Stepper/StepperContext.ts +71 -0
- package/src/Stepper/index.ts +18 -0
- package/src/Stepper/stepper.stylex.ts +19 -0
- package/src/Switch/Switch.doc.mjs +2 -0
- package/src/Switch/Switch.test.tsx +10 -0
- package/src/Switch/Switch.tsx +6 -2
- package/src/TabList/Tab.doc.mjs +13 -1
- package/src/TabList/Tab.tsx +67 -7
- package/src/TabList/TabList.doc.mjs +22 -6
- package/src/TabList/TabList.test.tsx +559 -0
- package/src/TabList/TabList.tsx +490 -22
- package/src/TabList/TabListContext.ts +13 -2
- package/src/TabList/TabMenu.tsx +3 -2
- package/src/TabList/index.ts +6 -2
- package/src/Table/BaseTable.tsx +12 -4
- package/src/Table/Table.doc.mjs +9 -2
- package/src/Table/Table.test.tsx +132 -14
- package/src/Table/Table.tsx +3 -8
- package/src/Table/TableBody.doc.mjs +40 -0
- package/src/Table/TableFooter.doc.mjs +40 -0
- package/src/Table/TableHeader.doc.mjs +40 -0
- package/src/Table/TableRow.tsx +16 -5
- package/src/Table/plugins/filtering/useTableFiltering.test.tsx +44 -6
- package/src/Table/plugins/filtering/useTableFiltering.tsx +100 -4
- package/src/Table/plugins/groupedRows/useTableGroupedRows-perf.test.tsx +112 -0
- package/src/Table/plugins/groupedRows/useTableGroupedRows.test.tsx +100 -0
- package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +67 -12
- package/src/Table/plugins/pagination/useTablePagination.test.tsx +34 -4
- package/src/Table/plugins/pagination/useTablePagination.tsx +35 -3
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +9 -2
- package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +13 -7
- package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +12 -3
- package/src/Table/plugins/selection/useTableSelection.test.tsx +138 -0
- package/src/Table/plugins/selection/useTableSelection.tsx +63 -7
- package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +1 -2
- package/src/Table/types.ts +22 -4
- package/src/Table/useBaseTablePlugins.ts +5 -0
- package/src/Table/useTableGroupedRows.doc.mjs +5 -4
- package/src/Table/useTablePagination.doc.mjs +7 -0
- package/src/Table/useTableRowStatus.doc.mjs +1 -1
- package/src/Table/useTableSelection.doc.mjs +31 -0
- package/src/Text/Text.test.tsx +67 -1
- package/src/Text/Text.tsx +6 -3
- package/src/TextArea/TextArea.doc.mjs +12 -2
- package/src/TextArea/TextArea.test.tsx +11 -0
- package/src/TextArea/TextArea.tsx +37 -24
- package/src/TextInput/TextInput.doc.mjs +10 -0
- package/src/TextInput/TextInput.tsx +3 -2
- package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
- package/src/TimeInput/TimeInput.tsx +2 -2
- package/src/Timestamp/Timestamp.test.tsx +58 -0
- package/src/Timestamp/Timestamp.tsx +12 -10
- package/src/Toast/Toast.doc.mjs +26 -12
- package/src/Toast/Toast.tsx +28 -4
- package/src/Toast/ToastViewport.test.tsx +536 -8
- package/src/Toast/ToastViewport.tsx +81 -12
- package/src/Token/Token.test.tsx +95 -0
- package/src/Token/Token.tsx +15 -10
- package/src/Token/TokenLink.tsx +14 -5
- package/src/Tokenizer/Tokenizer.doc.mjs +43 -3
- package/src/Tokenizer/Tokenizer.test.tsx +161 -0
- package/src/Tokenizer/Tokenizer.tsx +53 -24
- package/src/Toolbar/Toolbar.doc.mjs +5 -0
- package/src/Tooltip/Tooltip.doc.mjs +5 -0
- package/src/Tooltip/Tooltip.test.tsx +32 -0
- package/src/Tooltip/Tooltip.tsx +5 -0
- package/src/Tooltip/useTooltip.tsx +47 -24
- package/src/TopNav/TopNavHeading.tsx +5 -4
- package/src/TopNav/TopNavMegaMenu.test.tsx +104 -0
- package/src/TopNav/TopNavMegaMenu.tsx +47 -5
- package/src/TopNav/TopNavMenu.test.tsx +133 -1
- package/src/TopNav/TopNavMenu.tsx +38 -7
- package/src/TreeList/TreeList.test.tsx +49 -0
- package/src/TreeList/TreeList.tsx +7 -2
- package/src/Typeahead/BaseTypeahead.doc.mjs +24 -0
- package/src/Typeahead/BaseTypeahead.tsx +181 -39
- package/src/Typeahead/Typeahead.doc.mjs +11 -0
- package/src/Typeahead/Typeahead.test.tsx +203 -0
- package/src/Typeahead/Typeahead.tsx +12 -2
- package/src/Typeahead/TypeaheadItem.test.tsx +43 -0
- package/src/Typeahead/TypeaheadItem.tsx +8 -1
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useAnnounce.doc.mjs +96 -0
- package/src/hooks/useContainerReveal.doc.mjs +2 -2
- package/src/hooks/useDevWarning.doc.mjs +96 -0
- package/src/hooks/useFocusTrap.test.tsx +14 -0
- package/src/hooks/useFocusTrap.ts +62 -111
- package/src/hooks/useFocusTrapEscapeShim.test.tsx +165 -0
- package/src/hooks/useHotkeys.ts +3 -18
- package/src/hooks/useIndicatorFocusRing.doc.mjs +104 -0
- package/src/hooks/useInputStatusIcon.doc.mjs +142 -0
- package/src/hooks/useInteractiveRole.doc.mjs +119 -0
- package/src/hooks/useLongPress.doc.mjs +123 -0
- package/src/hooks/useMergedRefs.doc.mjs +79 -0
- package/src/hooks/useMergedRefs.test.tsx +62 -0
- package/src/hooks/useMergedRefs.ts +36 -0
- package/src/hooks/useTypeahead.doc.mjs +153 -0
- package/src/index.ts +1 -0
- package/src/raw-import.d.ts +14 -0
- package/src/theme/MediaTheme.doc.mjs +5 -5
- package/src/theme/Theme.test.tsx +104 -2
- package/src/theme/Theme.tsx +49 -14
- package/src/theme/defineTheme.test.ts +23 -0
- package/src/theme/derivedVarRegistry.test.ts +26 -3
- package/src/theme/derivedVarRegistry.ts +23 -4
- package/src/theme/generateThemeRules.test.ts +99 -1
- package/src/theme/generateThemeRules.ts +28 -0
- package/src/theme/syntax/tokens.ts +5 -2
- package/src/utils/groupItems.ts +12 -6
- package/src/utils/index.ts +1 -1
- package/src/utils/isApplePlatform.test.ts +61 -0
- package/src/utils/isApplePlatform.ts +37 -0
- package/src/utils/sharedResizeObserver.ts +10 -2
- package/src/utils/timeParser.test.ts +6 -1
- package/src/utils/timeParser.ts +35 -13
- package/src/__tests__/inputWidthContract.test.tsx +0 -69
|
@@ -0,0 +1,1638 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file Step.tsx
|
|
7
|
+
* @input Uses React, stylex, theme tokens (including the motion duration token
|
|
8
|
+
* the connector fill animates on), StepperContext
|
|
9
|
+
* @output Exports Step component and StepProps
|
|
10
|
+
* @position Individual step item; used inside Stepper
|
|
11
|
+
*
|
|
12
|
+
* Every connector this file draws — the separated progress bars and the
|
|
13
|
+
* on-track segments alike — paints its accent fill as a scaled ::before rather
|
|
14
|
+
* than by swapping a background color, so a step the flow advances into grows
|
|
15
|
+
* its line along the track. See the CONNECTOR FILL block in the styles below
|
|
16
|
+
* for the axis/RTL/reduced-motion rules that go with it.
|
|
17
|
+
*
|
|
18
|
+
* Exactly one change animates: advancing a single step. Going back, jumping
|
|
19
|
+
* forward by more than one, and mounting mid-flow all land at once. The rule
|
|
20
|
+
* and the reasoning behind it sit above the styles; the arithmetic that keeps
|
|
21
|
+
* the one animated span reading as a single front — an on-track span is drawn
|
|
22
|
+
* by two steps, three where a content slot splits it — is in the component.
|
|
23
|
+
* Nothing about it is public API: Stepper hands each Step the step it came
|
|
24
|
+
* from through context, and the rest is derived.
|
|
25
|
+
*
|
|
26
|
+
* SYNC: When modified, update these files to stay in sync:
|
|
27
|
+
* - /packages/core/src/Stepper/Stepper.doc.mjs
|
|
28
|
+
* - /packages/core/src/Stepper/Stepper.test.tsx
|
|
29
|
+
* - /packages/core/src/Stepper/index.ts
|
|
30
|
+
* - /apps/storybook/stories/Stepper.stories.tsx
|
|
31
|
+
* - /packages/cli/assets/templates/blocks/components/Stepper/ (showcase blocks)
|
|
32
|
+
* - /packages/cli/assets/templates/blocks/components/Step/ (showcase blocks)
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import {useEffect, type ReactNode} from 'react';
|
|
36
|
+
import * as stylex from '@stylexjs/stylex';
|
|
37
|
+
|
|
38
|
+
import {
|
|
39
|
+
colorVars,
|
|
40
|
+
spacingVars,
|
|
41
|
+
radiusVars,
|
|
42
|
+
fontWeightVars,
|
|
43
|
+
typeScaleVars,
|
|
44
|
+
textSizeVars,
|
|
45
|
+
durationVars,
|
|
46
|
+
easeVars,
|
|
47
|
+
} from '../theme/tokens.stylex';
|
|
48
|
+
import {
|
|
49
|
+
focusOutlineStyles,
|
|
50
|
+
mergeProps,
|
|
51
|
+
isRenderable,
|
|
52
|
+
themeProps,
|
|
53
|
+
} from '../utils';
|
|
54
|
+
import type {BaseProps} from '../BaseProps';
|
|
55
|
+
import {Icon} from '../Icon';
|
|
56
|
+
import {VisuallyHidden} from '../VisuallyHidden';
|
|
57
|
+
import {useTranslator} from '../i18n';
|
|
58
|
+
import {useStepperContext} from './StepperContext';
|
|
59
|
+
import {stepMarker} from './stepper.stylex';
|
|
60
|
+
import type {StepStatus} from './StepStatus';
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Built-in indicator presets. Anything other than these strings passed to
|
|
64
|
+
* `indicator` is treated as a custom ReactNode (e.g. an `<Icon />`).
|
|
65
|
+
* - 'auto': numbered badge for not-yet-reached steps, a check once completed
|
|
66
|
+
* (default)
|
|
67
|
+
* - 'number': always a numbered badge
|
|
68
|
+
* - 'none': no indicator — just the progress bar and label
|
|
69
|
+
*/
|
|
70
|
+
export type StepIndicatorPreset = 'auto' | 'number' | 'none';
|
|
71
|
+
export type StepDensity = 'compact' | 'balanced' | 'spacious';
|
|
72
|
+
|
|
73
|
+
export interface StepProps extends BaseProps<HTMLLIElement> {
|
|
74
|
+
/** Ref forwarded to the root element */
|
|
75
|
+
ref?: React.Ref<HTMLLIElement>;
|
|
76
|
+
/**
|
|
77
|
+
* Zero-based index of this step. Used to derive progress (completed /
|
|
78
|
+
* active / not-started) relative to the parent's `activeStep`.
|
|
79
|
+
*/
|
|
80
|
+
step: number;
|
|
81
|
+
/**
|
|
82
|
+
* Step label text.
|
|
83
|
+
*/
|
|
84
|
+
label: string;
|
|
85
|
+
/**
|
|
86
|
+
* Optional description shown below the label.
|
|
87
|
+
*/
|
|
88
|
+
description?: string;
|
|
89
|
+
/**
|
|
90
|
+
* Content rendered below the label and description. Useful in vertical
|
|
91
|
+
* steppers to show form fields or detailed content for each step.
|
|
92
|
+
*/
|
|
93
|
+
children?: ReactNode;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Semantic status for the step, mapped to the global Astryx semantic tokens
|
|
97
|
+
* (`accent`, `success`, `warning`, `error`). In the default `auto` indicator
|
|
98
|
+
* mode it sets both the indicator color and a matching glyph: `success` shows
|
|
99
|
+
* a green check-circle, `warning`/`error` show the shared Input status icons.
|
|
100
|
+
* `accent` is color-only. The current (in-progress) step always keeps its
|
|
101
|
+
* current-step indicator regardless of `status`. Never recolors the
|
|
102
|
+
* connector/track.
|
|
103
|
+
*
|
|
104
|
+
* Because the indicator glyphs are decorative (aria-hidden), the status also
|
|
105
|
+
* reaches assistive technology as text: visually hidden "completed" /
|
|
106
|
+
* "warning" / "error" next to the label, and composed into the accessible
|
|
107
|
+
* name of clickable steps.
|
|
108
|
+
*/
|
|
109
|
+
status?: StepStatus;
|
|
110
|
+
/**
|
|
111
|
+
* Disable interaction for this step.
|
|
112
|
+
* @default false
|
|
113
|
+
*/
|
|
114
|
+
isDisabled?: boolean;
|
|
115
|
+
/**
|
|
116
|
+
* Marks the step as optional, appending an "Optional" affordance after the
|
|
117
|
+
* label.
|
|
118
|
+
* @default false
|
|
119
|
+
*/
|
|
120
|
+
isOptional?: boolean;
|
|
121
|
+
/**
|
|
122
|
+
* Trailing content rendered at the end of the label row (e.g. a timestamp
|
|
123
|
+
* or status chip).
|
|
124
|
+
*/
|
|
125
|
+
endContent?: ReactNode;
|
|
126
|
+
/**
|
|
127
|
+
* What to show as the step indicator. Accepts a preset string or any
|
|
128
|
+
* ReactNode:
|
|
129
|
+
* - 'auto': numbered badge until completed, then a check (default)
|
|
130
|
+
* - 'number': always a numbered badge
|
|
131
|
+
* - 'none': no indicator, just the bar + label
|
|
132
|
+
* - ReactNode: any custom icon or element to render as the indicator
|
|
133
|
+
* @default 'auto'
|
|
134
|
+
*/
|
|
135
|
+
indicator?: StepIndicatorPreset | ReactNode;
|
|
136
|
+
/**
|
|
137
|
+
* Controls vertical padding of the step. Falls back to the stepper-level
|
|
138
|
+
* density when unset.
|
|
139
|
+
* - 'compact': minimal padding (4px block)
|
|
140
|
+
* - 'balanced': default (8px block)
|
|
141
|
+
* - 'spacious': generous (12px block, 12px inline)
|
|
142
|
+
*/
|
|
143
|
+
density?: StepDensity;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// --- Default progress icons (16px) ---
|
|
147
|
+
//
|
|
148
|
+
// The `completed` progress state and the current-step ring are drawn as local
|
|
149
|
+
// <svg> glyphs rather than sourced from the Icon registry. This is a deliberate
|
|
150
|
+
// exception to the "glyphs come from the registry" convention (audit rule T17),
|
|
151
|
+
// for two reasons:
|
|
152
|
+
//
|
|
153
|
+
// 1. `CurrentIcon` (a dot inside a ring) has no registry equivalent — it is a
|
|
154
|
+
// progress affordance, not a general-purpose icon.
|
|
155
|
+
// 2. `CheckCircleIcon` (a filled circle with a check) is intentionally NOT the
|
|
156
|
+
// registry `success` glyph, even though the two look similar. They mean
|
|
157
|
+
// different things and must stay independently restyleable: `completed`
|
|
158
|
+
// marks *progress through the sequence* (every step you've passed), while
|
|
159
|
+
// the semantic `success` status marks a step's *outcome*. A stepper can
|
|
160
|
+
// show a completed step that also carries a `warning`/`error` status, so
|
|
161
|
+
// collapsing the two glyphs would conflate progress with outcome. The
|
|
162
|
+
// semantic `success`/`warning`/`error` STATUS glyphs DO defer to the themed
|
|
163
|
+
// registry (see the status branch below), so those share one visual
|
|
164
|
+
// language; only the progress marks are local.
|
|
165
|
+
//
|
|
166
|
+
// Both glyphs paint via `currentColor`, so the indicator's color is still
|
|
167
|
+
// controlled by tokens on the wrapper (never hardcoded), and the wrapper
|
|
168
|
+
// carries the `astryx-step-indicator` theme target.
|
|
169
|
+
|
|
170
|
+
/** Filled circle with a check — shown for a completed step in 'auto' mode. */
|
|
171
|
+
function CheckCircleIcon() {
|
|
172
|
+
return (
|
|
173
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
|
174
|
+
<circle cx="8" cy="8" r="8" fill="currentColor" />
|
|
175
|
+
<path
|
|
176
|
+
d="M4.75 8.25 7 10.5l4.25-4.5"
|
|
177
|
+
stroke={colorVars['--color-background-surface']}
|
|
178
|
+
strokeWidth="1.75"
|
|
179
|
+
strokeLinecap="round"
|
|
180
|
+
strokeLinejoin="round"
|
|
181
|
+
/>
|
|
182
|
+
</svg>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Filled dot in a ring — shown for the active step in 'auto' mode. */
|
|
187
|
+
function CurrentIcon() {
|
|
188
|
+
return (
|
|
189
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
|
190
|
+
<circle cx="8" cy="8" r="7" stroke="currentColor" strokeWidth="2" />
|
|
191
|
+
<circle cx="8" cy="8" r="4" fill="currentColor" />
|
|
192
|
+
</svg>
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// --- Styles ---
|
|
197
|
+
|
|
198
|
+
const BAR_WIDTH = spacingVars['--spacing-1'];
|
|
199
|
+
// Every indicator — check, ring, custom icon, number badge — occupies the same
|
|
200
|
+
// box, so the `auto` mode swapping a number for a check as a step completes
|
|
201
|
+
// never nudges the label or the track.
|
|
202
|
+
const INDICATOR_SIZE = spacingVars['--spacing-4'];
|
|
203
|
+
// Indicator (16px) + the label-row gap (8px). The indent that lines a
|
|
204
|
+
// description or a content slot up with the label above it.
|
|
205
|
+
const INDICATOR_GUTTER = spacingVars['--spacing-6'];
|
|
206
|
+
|
|
207
|
+
// --- Connector fill motion ---
|
|
208
|
+
//
|
|
209
|
+
// One change animates: advancing exactly one step. Going back, jumping
|
|
210
|
+
// forward by more than one, and mounting mid-flow all apply at once.
|
|
211
|
+
//
|
|
212
|
+
// Animating the others was tried and read worse than not animating them. A
|
|
213
|
+
// retreat is the clearest case: run forward, the transition ends with a bar
|
|
214
|
+
// that is nearly full, and nearly full is indistinguishable from full, so the
|
|
215
|
+
// eye calls it arrived well before it settles. Run backward, the same curve
|
|
216
|
+
// ends with a shrinking stub of accent still on the track, and a remnant that
|
|
217
|
+
// is still there reads unmistakably as not finished. Identical arithmetic in
|
|
218
|
+
// both directions — measurably so — and the backward one still feels slow,
|
|
219
|
+
// because the tail is what is visible rather than what is hidden. No easing
|
|
220
|
+
// fixes that; only not animating it does. A multi-step jump fails differently:
|
|
221
|
+
// it is a navigation rather than a progression, and sweeping a front across
|
|
222
|
+
// four segments makes the user sit through a journey they asked to skip.
|
|
223
|
+
//
|
|
224
|
+
// What is left is the one gesture where the movement carries the meaning: the
|
|
225
|
+
// flow went forward one step, and the line grows to show it. That span takes
|
|
226
|
+
// the medium band — a fill crossing a whole segment is a spatial change, not a
|
|
227
|
+
// micro-interaction, so per `astryx docs motion` it sits above the fast band
|
|
228
|
+
// reserved for high-frequency states like hover.
|
|
229
|
+
const FILL_SPAN_TIME = durationVars['--duration-medium'];
|
|
230
|
+
|
|
231
|
+
// Share of one node-to-node span owned by the segment *arriving* at the far
|
|
232
|
+
// node; the segment leaving the near node takes the rest. On-track spans are
|
|
233
|
+
// drawn by two steps (see the ON-TRACK section), and the time has to be split
|
|
234
|
+
// the way the length is or the two halves sweep at visibly different speeds.
|
|
235
|
+
//
|
|
236
|
+
// Horizontal: both halves are `flex: 1` inside equal-width steps, so an even
|
|
237
|
+
// split is the proportional one.
|
|
238
|
+
//
|
|
239
|
+
// Vertical: the halves are nothing like equal, and no constant can be right
|
|
240
|
+
// for all of them. The arriving half is otSegLeadV, a cap fixed at one density
|
|
241
|
+
// space; the leaving half is otSegFlexV, grown to whatever is left of the step
|
|
242
|
+
// row, so its length turns on label and description lengths this file cannot
|
|
243
|
+
// see. Measured in the browser the cap is 40% of a plain balanced span (8 of
|
|
244
|
+
// 20px) but 21% once the step carries a description (8 of 38px). 0.3 is the
|
|
245
|
+
// value that minimises the worse of those two: it leaves the cap ~1.6x off the
|
|
246
|
+
// rail's speed either way, where tuning to one case puts the other past 2x.
|
|
247
|
+
// Matching each case exactly would mean measuring the rendered rail, a layout
|
|
248
|
+
// read this system avoids.
|
|
249
|
+
const OT_ARRIVAL_SHARE_H = 0.5;
|
|
250
|
+
const OT_ARRIVAL_SHARE_V = 0.3;
|
|
251
|
+
// A vertical step carrying a content slot draws a third segment down the side
|
|
252
|
+
// of it (otContentSegV) which continues the leaving half past the row, so the
|
|
253
|
+
// leaving half's time splits again. A slot runs several times the rail left
|
|
254
|
+
// above it — 34px of rail to 108px of slot in the spacious content story — and
|
|
255
|
+
// this is the middle of that range. Matching it exactly would mean measuring
|
|
256
|
+
// the rendered slot; the ordering is what keeps the front contiguous, and this
|
|
257
|
+
// keeps its speed in the right neighbourhood.
|
|
258
|
+
const OT_RAIL_SHARE_OF_LEAVING = 0.25;
|
|
259
|
+
|
|
260
|
+
/** Scale a CSS `<time>` by a unitless factor, collapsing the trivial cases. */
|
|
261
|
+
function fillTimeSlice(time: string, factor: number): string {
|
|
262
|
+
if (factor <= 0) {
|
|
263
|
+
return '0s';
|
|
264
|
+
}
|
|
265
|
+
if (factor === 1) {
|
|
266
|
+
return time;
|
|
267
|
+
}
|
|
268
|
+
// Rounded because the factors are sums of shares, and float noise would
|
|
269
|
+
// otherwise reach the emitted custom property as `calc(... * 0.7000000001)`.
|
|
270
|
+
return `calc(${time} * ${Number(factor.toFixed(4))})`;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const styles = stylex.create({
|
|
274
|
+
// ===================== VERTICAL LAYOUT =====================
|
|
275
|
+
verticalRoot: {
|
|
276
|
+
display: 'flex',
|
|
277
|
+
flexDirection: 'row',
|
|
278
|
+
alignItems: 'stretch',
|
|
279
|
+
position: 'relative',
|
|
280
|
+
gap: spacingVars['--spacing-0-5'],
|
|
281
|
+
},
|
|
282
|
+
|
|
283
|
+
// 4px progress bar segment. Its filled/unfilled paint comes from the shared
|
|
284
|
+
// connector styles below, not from here.
|
|
285
|
+
verticalBar: {
|
|
286
|
+
width: BAR_WIDTH,
|
|
287
|
+
borderRadius: radiusVars['--radius-full'],
|
|
288
|
+
flexShrink: 0,
|
|
289
|
+
alignSelf: 'stretch',
|
|
290
|
+
},
|
|
291
|
+
|
|
292
|
+
// Step body — the selectable area
|
|
293
|
+
verticalBody: {
|
|
294
|
+
display: 'flex',
|
|
295
|
+
flexDirection: 'column',
|
|
296
|
+
flex: 1,
|
|
297
|
+
},
|
|
298
|
+
|
|
299
|
+
// ===================== HORIZONTAL LAYOUT =====================
|
|
300
|
+
horizontalStep: {
|
|
301
|
+
display: 'flex',
|
|
302
|
+
flexDirection: 'column',
|
|
303
|
+
// Stretch, not flex-start: a flex-start child is sized to fit-content,
|
|
304
|
+
// which is floored at its own min-content and would spill the label past
|
|
305
|
+
// the step's slice of the track and into the next step. Stretched, the
|
|
306
|
+
// label row is exactly as wide as the step and the label wraps inside it.
|
|
307
|
+
alignItems: 'stretch',
|
|
308
|
+
flex: 1,
|
|
309
|
+
// `flex: 1` alone does not make the steps equal: a flex item's default
|
|
310
|
+
// `min-width: auto` floors it at its own min-content width, so a step
|
|
311
|
+
// labelled "Integrations" claims a wider slice of the track than one
|
|
312
|
+
// labelled "Team". Zeroing the floor lets the even distribution actually
|
|
313
|
+
// apply, which is what keeps every progress segment the same width.
|
|
314
|
+
minWidth: 0,
|
|
315
|
+
// density padding applied via density styles below
|
|
316
|
+
},
|
|
317
|
+
|
|
318
|
+
// Full-width progress bar segment sitting above each horizontal step.
|
|
319
|
+
// Each step owns its own segment (filled from its derived progress) so the
|
|
320
|
+
// parent never has to introspect children to build the bar.
|
|
321
|
+
horizontalBar: {
|
|
322
|
+
width: '100%',
|
|
323
|
+
height: BAR_WIDTH,
|
|
324
|
+
borderRadius: radiusVars['--radius-full'],
|
|
325
|
+
flexShrink: 0,
|
|
326
|
+
marginBlockEnd: spacingVars['--spacing-0-5'],
|
|
327
|
+
},
|
|
328
|
+
|
|
329
|
+
// ===================== CONNECTOR FILL =====================
|
|
330
|
+
// Shared by every connector the four layout combinations draw: the separated
|
|
331
|
+
// bars (verticalBar, horizontalBar) and the on-track segments (otSegBaseV,
|
|
332
|
+
// otSegH, otContentSegV).
|
|
333
|
+
//
|
|
334
|
+
// The segment element is the *unfilled* track and never changes color; the
|
|
335
|
+
// accent fill is a ::before scaled along the track axis. Advancing a step
|
|
336
|
+
// grows it out of the segment's leading edge, so the line reads as progress
|
|
337
|
+
// travelling rather than a bar changing state. The obvious alternative,
|
|
338
|
+
// transitioning `background-color` between accent and border on the segment
|
|
339
|
+
// itself, lights the whole segment at once: a flash, with no direction in
|
|
340
|
+
// it. Going the other way the scale still returns to zero, but instantly —
|
|
341
|
+
// only a single step forward is given a duration to cross (see the motion
|
|
342
|
+
// block above).
|
|
343
|
+
//
|
|
344
|
+
// A pseudo-element rather than a real child element, because the connector is
|
|
345
|
+
// emitted from seven places and several of them size themselves in ways a
|
|
346
|
+
// child would have to re-derive (otSegFlexV grows, otSegLeadV is fixed,
|
|
347
|
+
// otContentSegV is absolutely positioned). ::before inherits the box for
|
|
348
|
+
// free, so one style covers all of them.
|
|
349
|
+
//
|
|
350
|
+
// Mount is deliberately not animated, and falls out of how transitions work:
|
|
351
|
+
// interpolation needs a previous computed value and a freshly rendered
|
|
352
|
+
// element has none. A stepper that opens on step 3 paints the segments behind
|
|
353
|
+
// it filled, instantly, instead of playing its whole history back.
|
|
354
|
+
connectorTrack: {
|
|
355
|
+
position: 'relative',
|
|
356
|
+
backgroundColor: colorVars['--color-border'],
|
|
357
|
+
'::before': {
|
|
358
|
+
content: '""',
|
|
359
|
+
position: 'absolute',
|
|
360
|
+
inset: 0,
|
|
361
|
+
// Follows whatever the segment itself rounds to: the full pill of a
|
|
362
|
+
// separated bar, the square ends the on-track segments need in order to
|
|
363
|
+
// stack into one continuous line.
|
|
364
|
+
borderRadius: 'inherit',
|
|
365
|
+
backgroundColor: colorVars['--color-accent'],
|
|
366
|
+
transitionProperty: 'transform',
|
|
367
|
+
// Resting duration, for a segment moving on its own. connectorTiming
|
|
368
|
+
// below replaces it on every connector — each one is handed either its
|
|
369
|
+
// slice of the animated span or a zero — so this is the fallback that
|
|
370
|
+
// applies if a connector is ever rendered without it.
|
|
371
|
+
transitionDuration: {
|
|
372
|
+
default: FILL_SPAN_TIME,
|
|
373
|
+
'@media (prefers-reduced-motion: reduce)': '0s',
|
|
374
|
+
},
|
|
375
|
+
// Linear, deliberately, and not the `--ease-standard` the rest of the
|
|
376
|
+
// system transitions on. A node-to-node span is not always one element:
|
|
377
|
+
// in the on-track layouts the segment leaving one node and the segment
|
|
378
|
+
// arriving at the next draw it between them, and a vertical step with a
|
|
379
|
+
// content slot splits it three ways. A curve applied per segment
|
|
380
|
+
// restarts its deceleration at every seam, so the front lurches, stalls
|
|
381
|
+
// at the joint, and the joint becomes the most visible thing in the
|
|
382
|
+
// animation — the fill stops reading as one line growing and starts
|
|
383
|
+
// reading as two pieces taking turns. Linear is the only timing that
|
|
384
|
+
// stitches into a single constant-speed front. The separated bars run
|
|
385
|
+
// linear too: a lone segment gives up very little by it, and the two
|
|
386
|
+
// layouts should not animate with different character.
|
|
387
|
+
transitionTimingFunction: 'linear',
|
|
388
|
+
},
|
|
389
|
+
},
|
|
390
|
+
// This segment's slice of the animated span, or a pair of zeros for a
|
|
391
|
+
// segment that is not part of it (see the motion block above the styles).
|
|
392
|
+
// Dynamic because which segments animate, and over what part of the span,
|
|
393
|
+
// depends on where the active step just moved from — known only at render.
|
|
394
|
+
// StyleX routes the values through a custom property, so these are still
|
|
395
|
+
// generated rules and not inline styles.
|
|
396
|
+
//
|
|
397
|
+
// Restates the reduced-motion escape connectorTrack declares rather than
|
|
398
|
+
// relying on it: a StyleX style that redefines a property replaces the
|
|
399
|
+
// earlier definition wholesale, conditions included. Without it a
|
|
400
|
+
// reduced-motion user would get an on-track span as two instant snaps spaced
|
|
401
|
+
// apart by the delay — motion smuggled back in as timing. Zeroing the delay
|
|
402
|
+
// alongside the duration lands the whole span at once instead.
|
|
403
|
+
connectorTiming: (duration: string, delay: string) => ({
|
|
404
|
+
'::before': {
|
|
405
|
+
transitionDuration: {
|
|
406
|
+
default: duration,
|
|
407
|
+
'@media (prefers-reduced-motion: reduce)': '0s',
|
|
408
|
+
},
|
|
409
|
+
transitionDelay: {
|
|
410
|
+
default: delay,
|
|
411
|
+
'@media (prefers-reduced-motion: reduce)': '0s',
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
}),
|
|
415
|
+
// Vertical track: progress runs down the block axis under both LTR and RTL
|
|
416
|
+
// (direction only mirrors the inline axis), so the growth anchors at the
|
|
417
|
+
// physical top either way.
|
|
418
|
+
connectorFillV: {
|
|
419
|
+
'::before': {transformOrigin: 'center top', transform: 'scaleY(1)'},
|
|
420
|
+
},
|
|
421
|
+
connectorEmptyV: {
|
|
422
|
+
'::before': {transformOrigin: 'center top', transform: 'scaleY(0)'},
|
|
423
|
+
},
|
|
424
|
+
// Horizontal track: progress runs along the reading direction, so the growth
|
|
425
|
+
// anchors at the inline-start edge — physically left in LTR, right in RTL.
|
|
426
|
+
// `transform-origin` accepts no logical keywords, so the flip is spelled out
|
|
427
|
+
// with the same `:is([dir="rtl"] *)` hook Switch's thumb travel and the
|
|
428
|
+
// shared rtlStyles.mirror use. Stated on both states rather than hoisted onto
|
|
429
|
+
// connectorTrack, which is axis-agnostic and shared with the vertical
|
|
430
|
+
// segments.
|
|
431
|
+
connectorFillH: {
|
|
432
|
+
'::before': {
|
|
433
|
+
transformOrigin: {
|
|
434
|
+
default: 'left center',
|
|
435
|
+
':is([dir="rtl"] *)': 'right center',
|
|
436
|
+
},
|
|
437
|
+
transform: 'scaleX(1)',
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
connectorEmptyH: {
|
|
441
|
+
'::before': {
|
|
442
|
+
transformOrigin: {
|
|
443
|
+
default: 'left center',
|
|
444
|
+
':is([dir="rtl"] *)': 'right center',
|
|
445
|
+
},
|
|
446
|
+
transform: 'scaleX(0)',
|
|
447
|
+
},
|
|
448
|
+
},
|
|
449
|
+
|
|
450
|
+
// ===================== SHARED =====================
|
|
451
|
+
|
|
452
|
+
// Icon + Label in one row
|
|
453
|
+
iconLabelRow: {
|
|
454
|
+
display: 'flex',
|
|
455
|
+
flexDirection: 'row',
|
|
456
|
+
alignItems: 'center',
|
|
457
|
+
gap: spacingVars['--spacing-2'],
|
|
458
|
+
},
|
|
459
|
+
|
|
460
|
+
// Indicator icon container
|
|
461
|
+
icon: {
|
|
462
|
+
display: 'flex',
|
|
463
|
+
alignItems: 'center',
|
|
464
|
+
justifyContent: 'center',
|
|
465
|
+
width: INDICATOR_SIZE,
|
|
466
|
+
height: INDICATOR_SIZE,
|
|
467
|
+
flexShrink: 0,
|
|
468
|
+
},
|
|
469
|
+
iconCompleted: {
|
|
470
|
+
color: colorVars['--color-accent'],
|
|
471
|
+
},
|
|
472
|
+
iconInProgress: {
|
|
473
|
+
color: colorVars['--color-accent'],
|
|
474
|
+
},
|
|
475
|
+
iconNotStarted: {
|
|
476
|
+
color: colorVars['--color-icon-secondary'],
|
|
477
|
+
},
|
|
478
|
+
iconDisabled: {
|
|
479
|
+
color: colorVars['--color-icon-disabled'],
|
|
480
|
+
opacity: 0.5,
|
|
481
|
+
},
|
|
482
|
+
// Semantic status overrides for the icon — color only.
|
|
483
|
+
iconAccent: {
|
|
484
|
+
color: colorVars['--color-accent'],
|
|
485
|
+
},
|
|
486
|
+
iconSuccess: {
|
|
487
|
+
color: colorVars['--color-success'],
|
|
488
|
+
},
|
|
489
|
+
iconWarning: {
|
|
490
|
+
color: colorVars['--color-warning'],
|
|
491
|
+
},
|
|
492
|
+
iconError: {
|
|
493
|
+
color: colorVars['--color-error'],
|
|
494
|
+
},
|
|
495
|
+
|
|
496
|
+
// Number badge — same box as every other indicator (see INDICATOR_SIZE).
|
|
497
|
+
numberBadge: {
|
|
498
|
+
display: 'grid',
|
|
499
|
+
placeItems: 'center',
|
|
500
|
+
width: INDICATOR_SIZE,
|
|
501
|
+
height: INDICATOR_SIZE,
|
|
502
|
+
borderRadius: radiusVars['--radius-full'],
|
|
503
|
+
// The digit has to read at the same optical weight as the check glyph it
|
|
504
|
+
// alternates with inside a 16px circle, which the supporting size (12px)
|
|
505
|
+
// overfills — it leaves no room for a two-digit step. This is the scale
|
|
506
|
+
// step below it, still a token so themes rescale it. The digit is centered
|
|
507
|
+
// by the grid (placeItems), so no explicit line-height is needed — this
|
|
508
|
+
// matches how Avatar/AvatarGroup center a single glyph in a circle.
|
|
509
|
+
fontSize: textSizeVars['--font-size-xs'],
|
|
510
|
+
fontWeight: fontWeightVars['--font-weight-semibold'],
|
|
511
|
+
flexShrink: 0,
|
|
512
|
+
textAlign: 'center',
|
|
513
|
+
},
|
|
514
|
+
numberCompleted: {
|
|
515
|
+
backgroundColor: colorVars['--color-accent'],
|
|
516
|
+
color: colorVars['--color-background-surface'],
|
|
517
|
+
},
|
|
518
|
+
numberInProgress: {
|
|
519
|
+
backgroundColor: colorVars['--color-accent'],
|
|
520
|
+
color: colorVars['--color-background-surface'],
|
|
521
|
+
},
|
|
522
|
+
numberNotStarted: {
|
|
523
|
+
backgroundColor: colorVars['--color-background-muted'],
|
|
524
|
+
color: colorVars['--color-text-secondary'],
|
|
525
|
+
},
|
|
526
|
+
numberDisabled: {
|
|
527
|
+
backgroundColor: colorVars['--color-background-muted'],
|
|
528
|
+
color: colorVars['--color-text-disabled'],
|
|
529
|
+
opacity: 0.5,
|
|
530
|
+
},
|
|
531
|
+
// Semantic status overrides for the number badge — color only.
|
|
532
|
+
numberAccent: {
|
|
533
|
+
backgroundColor: colorVars['--color-accent'],
|
|
534
|
+
color: colorVars['--color-on-accent'],
|
|
535
|
+
},
|
|
536
|
+
numberSuccess: {
|
|
537
|
+
backgroundColor: colorVars['--color-success'],
|
|
538
|
+
color: colorVars['--color-on-success'],
|
|
539
|
+
},
|
|
540
|
+
numberWarning: {
|
|
541
|
+
backgroundColor: colorVars['--color-warning'],
|
|
542
|
+
color: colorVars['--color-on-warning'],
|
|
543
|
+
},
|
|
544
|
+
numberError: {
|
|
545
|
+
backgroundColor: colorVars['--color-error'],
|
|
546
|
+
color: colorVars['--color-on-error'],
|
|
547
|
+
},
|
|
548
|
+
|
|
549
|
+
// Label
|
|
550
|
+
label: {
|
|
551
|
+
fontSize: typeScaleVars['--text-body-size'],
|
|
552
|
+
lineHeight: typeScaleVars['--text-body-leading'],
|
|
553
|
+
fontWeight: fontWeightVars['--font-weight-normal'],
|
|
554
|
+
color: colorVars['--color-text-primary'],
|
|
555
|
+
// Horizontal steps divide the track evenly, so a label can end up with
|
|
556
|
+
// less room than it wants. Let it shrink past its longest word and break
|
|
557
|
+
// that word rather than overlap the neighbouring step. Inert wherever
|
|
558
|
+
// there is room, which is every vertical stepper.
|
|
559
|
+
minWidth: 0,
|
|
560
|
+
overflowWrap: 'break-word',
|
|
561
|
+
},
|
|
562
|
+
labelInProgress: {
|
|
563
|
+
fontWeight: fontWeightVars['--font-weight-semibold'],
|
|
564
|
+
},
|
|
565
|
+
labelNotStarted: {
|
|
566
|
+
color: colorVars['--color-text-secondary'],
|
|
567
|
+
},
|
|
568
|
+
labelDisabled: {
|
|
569
|
+
color: colorVars['--color-text-disabled'],
|
|
570
|
+
},
|
|
571
|
+
|
|
572
|
+
// Optional tag
|
|
573
|
+
optionalDot: {
|
|
574
|
+
fontSize: typeScaleVars['--text-body-size'],
|
|
575
|
+
color: colorVars['--color-text-secondary'],
|
|
576
|
+
},
|
|
577
|
+
optionalText: {
|
|
578
|
+
fontSize: typeScaleVars['--text-body-size'],
|
|
579
|
+
color: colorVars['--color-text-secondary'],
|
|
580
|
+
},
|
|
581
|
+
|
|
582
|
+
// Description
|
|
583
|
+
descriptionRow: {
|
|
584
|
+
paddingInlineStart: spacingVars['--spacing-0'],
|
|
585
|
+
},
|
|
586
|
+
descriptionRowWithIndicator: {
|
|
587
|
+
paddingInlineStart: INDICATOR_GUTTER,
|
|
588
|
+
},
|
|
589
|
+
description: {
|
|
590
|
+
// Block, not inline. An inline span is laid out in a line box belonging to
|
|
591
|
+
// its parent, and that box is floored by the parent's strut — the invisible
|
|
592
|
+
// box every block container reserves from its own font and line-height. The
|
|
593
|
+
// row inherits the page's 16px/24px, so an inline description sat in a 24px
|
|
594
|
+
// line box no matter how tight its own leading was, padding the gap under
|
|
595
|
+
// the label. Blockifying moves the strut onto this span, where the metrics
|
|
596
|
+
// below apply and the box hugs the text at 16px.
|
|
597
|
+
display: 'block',
|
|
598
|
+
fontSize: typeScaleVars['--text-supporting-size'],
|
|
599
|
+
// The supporting-leading token (1.667 → 20px at 12px) reads too loose for
|
|
600
|
+
// a caption sitting under its label; a fixed 16px line box (the --spacing-4
|
|
601
|
+
// step) is tighter without collapsing the text the way capsize trim did,
|
|
602
|
+
// and stays on the spacing scale rather than a raw literal.
|
|
603
|
+
lineHeight: spacingVars['--spacing-4'],
|
|
604
|
+
color: colorVars['--color-text-secondary'],
|
|
605
|
+
},
|
|
606
|
+
|
|
607
|
+
// Step content (children / flex slot)
|
|
608
|
+
stepContent: {
|
|
609
|
+
paddingBlockStart: spacingVars['--spacing-2'],
|
|
610
|
+
},
|
|
611
|
+
// The label row sits inside the density-padded hover target, but the content
|
|
612
|
+
// slot is that target's *sibling* — a <button> cannot contain the buttons and
|
|
613
|
+
// inputs a content slot usually holds, so the slot has to live outside it.
|
|
614
|
+
// Left alone the slot therefore starts at the step edge while the label above
|
|
615
|
+
// starts one density pad in. Reproducing that pad here is what keeps the two
|
|
616
|
+
// flush; the gutter on top of it is what lines the content up with the label
|
|
617
|
+
// rather than with the indicator.
|
|
618
|
+
contentIndent: (inlinePad: string, gutter: string) => ({
|
|
619
|
+
paddingInlineStart: `calc(${inlinePad} + ${gutter})`,
|
|
620
|
+
paddingInlineEnd: inlinePad,
|
|
621
|
+
}),
|
|
622
|
+
|
|
623
|
+
// Density. Block padding only — the inline half comes from densityInline,
|
|
624
|
+
// which is passed in so the content slot outside this wrapper can apply the
|
|
625
|
+
// same value.
|
|
626
|
+
densityCompact: {
|
|
627
|
+
paddingBlock: spacingVars['--spacing-1'],
|
|
628
|
+
},
|
|
629
|
+
densityBalanced: {
|
|
630
|
+
paddingBlock: spacingVars['--spacing-2'],
|
|
631
|
+
},
|
|
632
|
+
densitySpacious: {
|
|
633
|
+
paddingBlock: spacingVars['--spacing-3'],
|
|
634
|
+
},
|
|
635
|
+
densityInline: (value: string) => ({
|
|
636
|
+
paddingInline: value,
|
|
637
|
+
}),
|
|
638
|
+
|
|
639
|
+
// Button reset for clickable steps
|
|
640
|
+
buttonReset: {
|
|
641
|
+
all: 'unset',
|
|
642
|
+
textAlign: 'start',
|
|
643
|
+
alignItems: 'stretch',
|
|
644
|
+
display: 'flex',
|
|
645
|
+
flexDirection: 'column',
|
|
646
|
+
width: '100%',
|
|
647
|
+
cursor: {
|
|
648
|
+
default: 'pointer',
|
|
649
|
+
':is(:disabled,[aria-disabled="true"])': 'default',
|
|
650
|
+
},
|
|
651
|
+
borderRadius: radiusVars['--radius-element'],
|
|
652
|
+
transitionProperty: 'background-color',
|
|
653
|
+
transitionDuration: {
|
|
654
|
+
default: durationVars['--duration-fast-min'],
|
|
655
|
+
'@media (prefers-reduced-motion: reduce)': '0s',
|
|
656
|
+
},
|
|
657
|
+
transitionTimingFunction: easeVars['--ease-standard'],
|
|
658
|
+
backgroundColor: {
|
|
659
|
+
default: 'transparent',
|
|
660
|
+
':hover:where(:not(:disabled,[aria-disabled="true"]))': {
|
|
661
|
+
'@media (hover: hover)': colorVars['--color-overlay-hover'],
|
|
662
|
+
},
|
|
663
|
+
':active': colorVars['--color-overlay-pressed'],
|
|
664
|
+
},
|
|
665
|
+
},
|
|
666
|
+
|
|
667
|
+
// ===================== ON-TRACK LAYOUT =====================
|
|
668
|
+
// Indicator is slotted into the connector line as a node on the track.
|
|
669
|
+
// Each step draws the connector segments that flank its own indicator; the
|
|
670
|
+
// segment *before* the indicator is hidden on the first step (step === 0) so
|
|
671
|
+
// the track starts at the first node. Segments abut the indicator directly,
|
|
672
|
+
// so no child introspection or total-count is needed.
|
|
673
|
+
|
|
674
|
+
otVerticalRoot: {
|
|
675
|
+
display: 'flex',
|
|
676
|
+
flexDirection: 'column',
|
|
677
|
+
position: 'relative',
|
|
678
|
+
},
|
|
679
|
+
otHorizontalRoot: {
|
|
680
|
+
display: 'flex',
|
|
681
|
+
flexDirection: 'column',
|
|
682
|
+
flex: 1,
|
|
683
|
+
minWidth: 0,
|
|
684
|
+
},
|
|
685
|
+
|
|
686
|
+
// Interactive wrapper (indicator + label act as one click target).
|
|
687
|
+
otInteractive: {
|
|
688
|
+
all: 'unset',
|
|
689
|
+
boxSizing: 'border-box',
|
|
690
|
+
// `all: unset` leaves the <button> UA default of centered text; force
|
|
691
|
+
// start so vertical labels/descriptions read left-aligned. Horizontal
|
|
692
|
+
// labels re-center via otLabelWrapH (a deeper element).
|
|
693
|
+
textAlign: 'start',
|
|
694
|
+
cursor: {
|
|
695
|
+
default: 'pointer',
|
|
696
|
+
':is(:disabled,[aria-disabled="true"])': 'default',
|
|
697
|
+
},
|
|
698
|
+
borderRadius: radiusVars['--radius-element'],
|
|
699
|
+
transitionProperty: 'background-color',
|
|
700
|
+
transitionDuration: {
|
|
701
|
+
default: durationVars['--duration-fast-min'],
|
|
702
|
+
'@media (prefers-reduced-motion: reduce)': '0s',
|
|
703
|
+
},
|
|
704
|
+
transitionTimingFunction: easeVars['--ease-standard'],
|
|
705
|
+
backgroundColor: {
|
|
706
|
+
default: 'transparent',
|
|
707
|
+
':hover:where(:not(:disabled,[aria-disabled="true"]))': {
|
|
708
|
+
'@media (hover: hover)': colorVars['--color-overlay-hover'],
|
|
709
|
+
},
|
|
710
|
+
':active': colorVars['--color-overlay-pressed'],
|
|
711
|
+
},
|
|
712
|
+
},
|
|
713
|
+
|
|
714
|
+
otRowWrap: {
|
|
715
|
+
display: 'flex',
|
|
716
|
+
flexDirection: 'row',
|
|
717
|
+
alignItems: 'stretch',
|
|
718
|
+
gap: spacingVars['--spacing-2'],
|
|
719
|
+
width: '100%',
|
|
720
|
+
},
|
|
721
|
+
otColWrap: {
|
|
722
|
+
display: 'flex',
|
|
723
|
+
flexDirection: 'column',
|
|
724
|
+
alignItems: 'stretch',
|
|
725
|
+
width: '100%',
|
|
726
|
+
},
|
|
727
|
+
|
|
728
|
+
// Vertical indicator column: [segment] [indicator] [segment] stacked.
|
|
729
|
+
otIndicatorColV: {
|
|
730
|
+
display: 'flex',
|
|
731
|
+
flexDirection: 'column',
|
|
732
|
+
alignItems: 'center',
|
|
733
|
+
width: INDICATOR_SIZE,
|
|
734
|
+
flexShrink: 0,
|
|
735
|
+
alignSelf: 'stretch',
|
|
736
|
+
},
|
|
737
|
+
// Shared segment base: square ends so stacked segments read as one
|
|
738
|
+
// continuous line (no radius).
|
|
739
|
+
otSegBaseV: {
|
|
740
|
+
width: BAR_WIDTH,
|
|
741
|
+
flexShrink: 0,
|
|
742
|
+
borderRadius: 0,
|
|
743
|
+
},
|
|
744
|
+
// Flexible segment (below the node) — grows to fill the step height and
|
|
745
|
+
// meets the next node's leading segment.
|
|
746
|
+
otSegFlexV: {
|
|
747
|
+
flex: 1,
|
|
748
|
+
minHeight: spacingVars['--spacing-2'],
|
|
749
|
+
},
|
|
750
|
+
// Fixed leading segment (above the node) — its height offsets the node down
|
|
751
|
+
// so it aligns with the label's first line instead of the step's center.
|
|
752
|
+
otSegLeadV: (value: string) => ({
|
|
753
|
+
height: value,
|
|
754
|
+
}),
|
|
755
|
+
|
|
756
|
+
// Horizontal track row: [segment] [indicator] [segment] in a line.
|
|
757
|
+
otTrackRowH: {
|
|
758
|
+
display: 'flex',
|
|
759
|
+
flexDirection: 'row',
|
|
760
|
+
alignItems: 'center',
|
|
761
|
+
width: '100%',
|
|
762
|
+
},
|
|
763
|
+
otSegH: {
|
|
764
|
+
height: BAR_WIDTH,
|
|
765
|
+
flex: 1,
|
|
766
|
+
minWidth: spacingVars['--spacing-2'],
|
|
767
|
+
borderRadius: 0,
|
|
768
|
+
},
|
|
769
|
+
|
|
770
|
+
otSegHiddenIfFirst: {
|
|
771
|
+
// Structural first/last hiding keyed off the step's own <li> position via
|
|
772
|
+
// stepMarker, so it never depends on the parent counting children (which
|
|
773
|
+
// breaks when steps are grouped in a fragment). Scoped to stepMarker so only
|
|
774
|
+
// the parent <li> is checked, not the outer <ol> (also a :first/:last-child).
|
|
775
|
+
visibility: {
|
|
776
|
+
default: 'visible',
|
|
777
|
+
[stylex.when.ancestor(':first-child', stepMarker)]: 'hidden',
|
|
778
|
+
},
|
|
779
|
+
},
|
|
780
|
+
otSegHiddenIfLast: {
|
|
781
|
+
visibility: {
|
|
782
|
+
default: 'visible',
|
|
783
|
+
[stylex.when.ancestor(':last-child', stepMarker)]: 'hidden',
|
|
784
|
+
},
|
|
785
|
+
},
|
|
786
|
+
|
|
787
|
+
otBodyV: {
|
|
788
|
+
display: 'flex',
|
|
789
|
+
flexDirection: 'column',
|
|
790
|
+
justifyContent: 'flex-start',
|
|
791
|
+
flex: 1,
|
|
792
|
+
gap: spacingVars['--spacing-0-5'],
|
|
793
|
+
minWidth: 0,
|
|
794
|
+
},
|
|
795
|
+
otLabelWrapH: {
|
|
796
|
+
display: 'flex',
|
|
797
|
+
flexDirection: 'column',
|
|
798
|
+
// Stretch rather than center for the same reason as horizontalStep: a
|
|
799
|
+
// centered child is fit-content sized and would spill past its node.
|
|
800
|
+
// The label still reads centered under the node — otLabelRowCenter
|
|
801
|
+
// centers it inside the full-width row.
|
|
802
|
+
alignItems: 'stretch',
|
|
803
|
+
gap: spacingVars['--spacing-0-5'],
|
|
804
|
+
textAlign: 'center',
|
|
805
|
+
},
|
|
806
|
+
otLabelRowStart: {
|
|
807
|
+
display: 'flex',
|
|
808
|
+
flexDirection: 'row',
|
|
809
|
+
alignItems: 'center',
|
|
810
|
+
gap: spacingVars['--spacing-2'],
|
|
811
|
+
},
|
|
812
|
+
otLabelRowCenter: {
|
|
813
|
+
display: 'flex',
|
|
814
|
+
flexDirection: 'row',
|
|
815
|
+
alignItems: 'center',
|
|
816
|
+
justifyContent: 'center',
|
|
817
|
+
flexWrap: 'wrap',
|
|
818
|
+
gap: spacingVars['--spacing-1'],
|
|
819
|
+
},
|
|
820
|
+
|
|
821
|
+
// Inline offset comes from contentIndent, and only in the vertical layout —
|
|
822
|
+
// a horizontal on-track step pads its hover target on the block axis alone,
|
|
823
|
+
// so its content slot already starts flush with the column above it.
|
|
824
|
+
otContent: {
|
|
825
|
+
paddingBlockStart: spacingVars['--spacing-2'],
|
|
826
|
+
},
|
|
827
|
+
// A vertical on-track step draws its connector inside the row holding the
|
|
828
|
+
// indicator and label, and the content slot sits below that row rather than
|
|
829
|
+
// in it (the row is a <button> when the step is clickable, which cannot
|
|
830
|
+
// contain the controls a content slot usually holds). So the line stops at
|
|
831
|
+
// the label and the track breaks open wherever a step carries content.
|
|
832
|
+
// otContentSegV continues it: a segment spanning the slot's full height, on
|
|
833
|
+
// the same rail the row's segments run down, which closes the gap between
|
|
834
|
+
// this step's indicator and the next one's.
|
|
835
|
+
otContentWrapV: {
|
|
836
|
+
position: 'relative',
|
|
837
|
+
},
|
|
838
|
+
otContentSegV: (inlinePad: string) => ({
|
|
839
|
+
position: 'absolute',
|
|
840
|
+
insetBlock: 0,
|
|
841
|
+
// Center on the rail: the row pads itself by inlinePad, then centers a
|
|
842
|
+
// BAR_WIDTH line inside an INDICATOR_SIZE-wide column.
|
|
843
|
+
insetInlineStart: `calc(${inlinePad} + (${INDICATOR_SIZE} - ${BAR_WIDTH}) / 2)`,
|
|
844
|
+
width: BAR_WIDTH,
|
|
845
|
+
}),
|
|
846
|
+
|
|
847
|
+
// Density-driven spacing. The connector runs along the "track axis"
|
|
848
|
+
// (vertical = block, horizontal = inline), so density is only ever applied
|
|
849
|
+
// to the *cross* axis — padding the track axis would break the line into
|
|
850
|
+
// gapped segments.
|
|
851
|
+
//
|
|
852
|
+
// Vertical: pad the whole step row (indicator rail + label) so density adds
|
|
853
|
+
// breathing room *around the indicators*, not just the text. Applied to the
|
|
854
|
+
// hover target itself.
|
|
855
|
+
otRowPadV: (value: string) => ({
|
|
856
|
+
paddingBlock: value,
|
|
857
|
+
paddingInline: value,
|
|
858
|
+
}),
|
|
859
|
+
// The rail draws the connector, so it must span the row's full block extent
|
|
860
|
+
// to keep the line continuous. A negative block margin cancels the wrapper's
|
|
861
|
+
// block padding, letting the line bridge step-to-step while the label still
|
|
862
|
+
// sits inside the padding.
|
|
863
|
+
otRailBridgeV: (value: string) => ({
|
|
864
|
+
marginBlock: `calc(-1 * ${value})`,
|
|
865
|
+
}),
|
|
866
|
+
// Horizontal: block padding around the track+label column (inside the hover
|
|
867
|
+
// target) adds vertical breathing room while the inline track stays flush.
|
|
868
|
+
otPadBlock: (value: string) => ({
|
|
869
|
+
paddingBlock: value,
|
|
870
|
+
}),
|
|
871
|
+
otMarginTop: (value: string) => ({
|
|
872
|
+
marginBlockStart: value,
|
|
873
|
+
}),
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
/**
|
|
877
|
+
* An individual step within an Stepper. Renders a 4px progress-bar segment,
|
|
878
|
+
* an indicator (numbered badge, check, or any custom icon), a label with
|
|
879
|
+
* optional description, and an optional content slot.
|
|
880
|
+
*
|
|
881
|
+
* Progress (completed / active / not-started) is derived from the parent's
|
|
882
|
+
* `activeStep` and this step's `step` prop. The optional `status` prop layers a
|
|
883
|
+
* semantic meaning on top: in the default `auto` indicator mode it recolors the
|
|
884
|
+
* indicator and swaps in a matching glyph (`success` → green check-circle,
|
|
885
|
+
* `warning`/`error` → the shared Input status icons). The current step always
|
|
886
|
+
* keeps its current-step ring. `status` never recolors the connector/track.
|
|
887
|
+
*
|
|
888
|
+
* @example
|
|
889
|
+
* ```
|
|
890
|
+
* <Step step={0} label="Account details" description="Enter your email" />
|
|
891
|
+
* ```
|
|
892
|
+
*
|
|
893
|
+
* @example
|
|
894
|
+
* ```
|
|
895
|
+
* <Step step={1} label="Payment" status="error" />
|
|
896
|
+
* ```
|
|
897
|
+
*/
|
|
898
|
+
export function Step({
|
|
899
|
+
step,
|
|
900
|
+
label,
|
|
901
|
+
description,
|
|
902
|
+
children,
|
|
903
|
+
status,
|
|
904
|
+
isDisabled = false,
|
|
905
|
+
isOptional = false,
|
|
906
|
+
endContent,
|
|
907
|
+
indicator: indicatorProp,
|
|
908
|
+
density: densityProp,
|
|
909
|
+
xstyle,
|
|
910
|
+
className,
|
|
911
|
+
style,
|
|
912
|
+
ref,
|
|
913
|
+
'data-testid': dataTestId,
|
|
914
|
+
...rest
|
|
915
|
+
}: StepProps) {
|
|
916
|
+
const t = useTranslator();
|
|
917
|
+
const ctx = useStepperContext();
|
|
918
|
+
const {
|
|
919
|
+
activeStep,
|
|
920
|
+
previousActiveStep,
|
|
921
|
+
orientation,
|
|
922
|
+
onStepClick,
|
|
923
|
+
density: ctxDensity,
|
|
924
|
+
indicatorPosition,
|
|
925
|
+
registerStep,
|
|
926
|
+
} = ctx;
|
|
927
|
+
|
|
928
|
+
// Register this step index with the parent Stepper for duplicate detection.
|
|
929
|
+
useEffect(() => registerStep(step), [registerStep, step]);
|
|
930
|
+
|
|
931
|
+
const density = densityProp ?? ctxDensity;
|
|
932
|
+
// Inline padding of a separated step's hover target. Density varies the block
|
|
933
|
+
// padding freely, but the inline value has to be readable from out here: the
|
|
934
|
+
// content slot renders outside that target and re-applies it to stay aligned
|
|
935
|
+
// (see contentIndent), so keeping it in one place is what stops the two
|
|
936
|
+
// drifting apart.
|
|
937
|
+
const separatedDensityInline =
|
|
938
|
+
density === 'spacious'
|
|
939
|
+
? spacingVars['--spacing-3']
|
|
940
|
+
: spacingVars['--spacing-2'];
|
|
941
|
+
|
|
942
|
+
// Resolve indicator prop — may be a preset string or a custom ReactNode.
|
|
943
|
+
const isCustomIndicator =
|
|
944
|
+
indicatorProp != null && typeof indicatorProp !== 'string';
|
|
945
|
+
const indicator: StepIndicatorPreset = isCustomIndicator
|
|
946
|
+
? 'auto'
|
|
947
|
+
: ((indicatorProp as StepIndicatorPreset | undefined) ?? 'auto');
|
|
948
|
+
|
|
949
|
+
// Internal progress, derived from the parent's activeStep. This is NOT the
|
|
950
|
+
// public `status` prop — `status` controls semantic color only.
|
|
951
|
+
const progress: 'completed' | 'in-progress' | 'not-started' =
|
|
952
|
+
step === activeStep
|
|
953
|
+
? 'in-progress'
|
|
954
|
+
: step < activeStep
|
|
955
|
+
? 'completed'
|
|
956
|
+
: 'not-started';
|
|
957
|
+
|
|
958
|
+
const isVertical = orientation === 'vertical';
|
|
959
|
+
const isActive = progress === 'in-progress';
|
|
960
|
+
// Any non-disabled step is navigable when an onStepClick handler is provided,
|
|
961
|
+
// including not-started steps (free navigation across the flow).
|
|
962
|
+
const isClickable = !isDisabled && onStepClick != null;
|
|
963
|
+
|
|
964
|
+
const handleClick = () => {
|
|
965
|
+
if (isClickable && onStepClick) {
|
|
966
|
+
onStepClick(step);
|
|
967
|
+
}
|
|
968
|
+
};
|
|
969
|
+
|
|
970
|
+
// Bar fill is purely progress-based. `status` never recolors the bar — it
|
|
971
|
+
// only recolors the indicator (icon / number badge) below.
|
|
972
|
+
const isBarFilled = progress === 'completed' || progress === 'in-progress';
|
|
973
|
+
|
|
974
|
+
// --- This step's slice of the animated span ---
|
|
975
|
+
// Both layouts are measured in the same unit, the node-to-node span: on-track
|
|
976
|
+
// span k runs from node k to node k+1, and a separated step's lone bar is the
|
|
977
|
+
// span arriving at it (bar s fills on exactly the condition span s-1 does).
|
|
978
|
+
// So one rule serves both, and neither needs to know the step count.
|
|
979
|
+
//
|
|
980
|
+
// Only a single forward step animates, and the span it crosses is the one
|
|
981
|
+
// leaving the step the flow came from. Every other segment — the untouched
|
|
982
|
+
// ones, and every segment of a jump or a retreat — is handed a zero
|
|
983
|
+
// duration, so it lands at once and carries no stale delay into a later
|
|
984
|
+
// change.
|
|
985
|
+
const isSingleAdvance = activeStep === previousActiveStep + 1;
|
|
986
|
+
const animatedSpan = previousActiveStep;
|
|
987
|
+
|
|
988
|
+
/**
|
|
989
|
+
* Timing for the segment covering `[offset, offset + share)` of span
|
|
990
|
+
* `spanIndex`, both expressed as fractions of that span.
|
|
991
|
+
*/
|
|
992
|
+
const fillTiming = (spanIndex: number, offset: number, share: number) => {
|
|
993
|
+
if (!isSingleAdvance || spanIndex !== animatedSpan) {
|
|
994
|
+
return styles.connectorTiming('0s', '0s');
|
|
995
|
+
}
|
|
996
|
+
return styles.connectorTiming(
|
|
997
|
+
fillTimeSlice(FILL_SPAN_TIME, share),
|
|
998
|
+
fillTimeSlice(FILL_SPAN_TIME, offset),
|
|
999
|
+
);
|
|
1000
|
+
};
|
|
1001
|
+
|
|
1002
|
+
// A separated step draws the whole span arriving at it, so it fills for the
|
|
1003
|
+
// span's entire slice with nothing to sequence against.
|
|
1004
|
+
const barTiming = fillTiming(step - 1, 0, 1);
|
|
1005
|
+
|
|
1006
|
+
// --- Build indicator node ---
|
|
1007
|
+
// 'auto': number for not-started, check/dot icon once reached
|
|
1008
|
+
// 'number': always number badge
|
|
1009
|
+
// 'none': nothing
|
|
1010
|
+
// custom ReactNode (or `icon` prop): render as-is
|
|
1011
|
+
let indicatorNode: ReactNode = null;
|
|
1012
|
+
|
|
1013
|
+
const customIcon = isCustomIndicator ? indicatorProp : null;
|
|
1014
|
+
|
|
1015
|
+
// Semantic `status` drives a distinct indicator glyph (default 'auto' mode,
|
|
1016
|
+
// no custom icon), all sourced from the themed Icon registry so a step reads
|
|
1017
|
+
// the same as the rest of the system:
|
|
1018
|
+
// - success → the themed `success` glyph (same check-circle as a completed
|
|
1019
|
+
// step), tinted success — i.e. a green check
|
|
1020
|
+
// - warning → the themed `warning` glyph
|
|
1021
|
+
// - error → the themed `error` glyph
|
|
1022
|
+
// The current (in-progress) step always shows the current-step ring — its
|
|
1023
|
+
// indicator "replaces" any status glyph. `accent` has no distinct glyph and
|
|
1024
|
+
// falls through to the progress-derived default.
|
|
1025
|
+
const statusGlyph: 'success' | 'warning' | 'error' | null =
|
|
1026
|
+
indicator === 'auto' &&
|
|
1027
|
+
customIcon == null &&
|
|
1028
|
+
!isActive &&
|
|
1029
|
+
(status === 'success' || status === 'warning' || status === 'error')
|
|
1030
|
+
? status
|
|
1031
|
+
: null;
|
|
1032
|
+
|
|
1033
|
+
if (indicator !== 'none') {
|
|
1034
|
+
const showNumber =
|
|
1035
|
+
customIcon == null &&
|
|
1036
|
+
statusGlyph == null &&
|
|
1037
|
+
(indicator === 'number' ||
|
|
1038
|
+
(indicator === 'auto' && progress === 'not-started'));
|
|
1039
|
+
|
|
1040
|
+
if (showNumber) {
|
|
1041
|
+
// Status only fills the badge once the step is reached. A not-started
|
|
1042
|
+
// step stays neutral — otherwise `accent` (which has no glyph to swap in)
|
|
1043
|
+
// would paint an inverted accent badge on an upcoming step, making it
|
|
1044
|
+
// read as active/completed.
|
|
1045
|
+
const isReached = progress === 'completed' || progress === 'in-progress';
|
|
1046
|
+
const numberColorStyle = isDisabled
|
|
1047
|
+
? styles.numberDisabled
|
|
1048
|
+
: isReached && status === 'accent'
|
|
1049
|
+
? styles.numberAccent
|
|
1050
|
+
: isReached && status === 'success'
|
|
1051
|
+
? styles.numberSuccess
|
|
1052
|
+
: isReached && status === 'warning'
|
|
1053
|
+
? styles.numberWarning
|
|
1054
|
+
: isReached && status === 'error'
|
|
1055
|
+
? styles.numberError
|
|
1056
|
+
: progress === 'completed'
|
|
1057
|
+
? styles.numberCompleted
|
|
1058
|
+
: progress === 'in-progress'
|
|
1059
|
+
? styles.numberInProgress
|
|
1060
|
+
: styles.numberNotStarted;
|
|
1061
|
+
|
|
1062
|
+
indicatorNode = (
|
|
1063
|
+
<div
|
|
1064
|
+
aria-hidden="true"
|
|
1065
|
+
{...mergeProps(
|
|
1066
|
+
themeProps('step-indicator', {
|
|
1067
|
+
progress,
|
|
1068
|
+
status: status ?? undefined,
|
|
1069
|
+
}),
|
|
1070
|
+
stylex.props(styles.numberBadge, numberColorStyle),
|
|
1071
|
+
)}>
|
|
1072
|
+
{step + 1}
|
|
1073
|
+
</div>
|
|
1074
|
+
);
|
|
1075
|
+
} else {
|
|
1076
|
+
// Priority: explicit custom icon → status glyph (non-current steps) →
|
|
1077
|
+
// progress-derived default (check when completed, ring when current).
|
|
1078
|
+
const iconContent: ReactNode =
|
|
1079
|
+
customIcon != null ? (
|
|
1080
|
+
customIcon
|
|
1081
|
+
) : statusGlyph === 'success' ? (
|
|
1082
|
+
<Icon
|
|
1083
|
+
icon="success"
|
|
1084
|
+
size="sm"
|
|
1085
|
+
color={isDisabled ? 'disabled' : 'success'}
|
|
1086
|
+
/>
|
|
1087
|
+
) : statusGlyph === 'warning' ? (
|
|
1088
|
+
<Icon
|
|
1089
|
+
icon="warning"
|
|
1090
|
+
size="sm"
|
|
1091
|
+
color={isDisabled ? 'disabled' : 'warning'}
|
|
1092
|
+
/>
|
|
1093
|
+
) : statusGlyph === 'error' ? (
|
|
1094
|
+
<Icon
|
|
1095
|
+
icon="error"
|
|
1096
|
+
size="sm"
|
|
1097
|
+
color={isDisabled ? 'disabled' : 'error'}
|
|
1098
|
+
/>
|
|
1099
|
+
) : progress === 'completed' ? (
|
|
1100
|
+
<CheckCircleIcon />
|
|
1101
|
+
) : (
|
|
1102
|
+
<CurrentIcon />
|
|
1103
|
+
);
|
|
1104
|
+
|
|
1105
|
+
// Wrapper tint drives the currentColor glyphs (check-circle / ring /
|
|
1106
|
+
// custom icon). The <Icon> status glyphs set their own color, but we tint
|
|
1107
|
+
// the wrapper to match so the indicator's color reflects `status` too.
|
|
1108
|
+
const iconColorStyle = isDisabled
|
|
1109
|
+
? styles.iconDisabled
|
|
1110
|
+
: customIcon != null
|
|
1111
|
+
? status === 'accent'
|
|
1112
|
+
? styles.iconAccent
|
|
1113
|
+
: status === 'success'
|
|
1114
|
+
? styles.iconSuccess
|
|
1115
|
+
: status === 'warning'
|
|
1116
|
+
? styles.iconWarning
|
|
1117
|
+
: status === 'error'
|
|
1118
|
+
? styles.iconError
|
|
1119
|
+
: progress === 'completed'
|
|
1120
|
+
? styles.iconCompleted
|
|
1121
|
+
: progress === 'in-progress'
|
|
1122
|
+
? styles.iconInProgress
|
|
1123
|
+
: styles.iconNotStarted
|
|
1124
|
+
: statusGlyph === 'success'
|
|
1125
|
+
? styles.iconSuccess
|
|
1126
|
+
: statusGlyph === 'warning'
|
|
1127
|
+
? styles.iconWarning
|
|
1128
|
+
: statusGlyph === 'error'
|
|
1129
|
+
? styles.iconError
|
|
1130
|
+
: progress === 'completed'
|
|
1131
|
+
? styles.iconCompleted
|
|
1132
|
+
: progress === 'in-progress'
|
|
1133
|
+
? styles.iconInProgress
|
|
1134
|
+
: styles.iconNotStarted;
|
|
1135
|
+
|
|
1136
|
+
indicatorNode = (
|
|
1137
|
+
<div
|
|
1138
|
+
aria-hidden="true"
|
|
1139
|
+
{...mergeProps(
|
|
1140
|
+
themeProps('step-indicator', {
|
|
1141
|
+
progress,
|
|
1142
|
+
status: status ?? undefined,
|
|
1143
|
+
}),
|
|
1144
|
+
stylex.props(styles.icon, iconColorStyle),
|
|
1145
|
+
)}>
|
|
1146
|
+
{iconContent}
|
|
1147
|
+
</div>
|
|
1148
|
+
);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
// Every indicator glyph above is aria-hidden (pure decoration), so the
|
|
1153
|
+
// step's progress/status must also reach assistive tech as text
|
|
1154
|
+
// (WCAG 1.4.1 / 1.3.1). `error`/`warning` announce the semantic status;
|
|
1155
|
+
// `success` and a completed step both announce "completed". The current
|
|
1156
|
+
// step is announced via aria-current="step" instead, and not-started steps
|
|
1157
|
+
// stay silent (the default state needs no qualifier).
|
|
1158
|
+
const statusText: string | null =
|
|
1159
|
+
status === 'error'
|
|
1160
|
+
? t('@astryx.step.status.error')
|
|
1161
|
+
: status === 'warning'
|
|
1162
|
+
? t('@astryx.step.status.warning')
|
|
1163
|
+
: status === 'success' || progress === 'completed'
|
|
1164
|
+
? t('@astryx.step.status.completed')
|
|
1165
|
+
: null;
|
|
1166
|
+
|
|
1167
|
+
// Rendered next to the label: hidden text for static steps, and composed
|
|
1168
|
+
// into the button's accessible name for clickable ones (an aria-label on
|
|
1169
|
+
// the button would otherwise override the hidden text). Two separate keys
|
|
1170
|
+
// rather than string concatenation so translations control the joiner.
|
|
1171
|
+
const statusTextNode = isRenderable(statusText) ? (
|
|
1172
|
+
<VisuallyHidden>{statusText}</VisuallyHidden>
|
|
1173
|
+
) : null;
|
|
1174
|
+
const stepAriaLabel =
|
|
1175
|
+
statusText != null
|
|
1176
|
+
? t('@astryx.step.goToStepWithStatus', {
|
|
1177
|
+
stepNumber: step + 1,
|
|
1178
|
+
label,
|
|
1179
|
+
status: statusText,
|
|
1180
|
+
})
|
|
1181
|
+
: t('@astryx.step.goToStep', {stepNumber: step + 1, label});
|
|
1182
|
+
|
|
1183
|
+
const hasIndicator = indicator !== 'none';
|
|
1184
|
+
|
|
1185
|
+
const labelColorStyle = isDisabled
|
|
1186
|
+
? styles.labelDisabled
|
|
1187
|
+
: progress === 'not-started'
|
|
1188
|
+
? styles.labelNotStarted
|
|
1189
|
+
: isActive
|
|
1190
|
+
? styles.labelInProgress
|
|
1191
|
+
: undefined;
|
|
1192
|
+
|
|
1193
|
+
// Indicator + Label row
|
|
1194
|
+
const iconLabelNode = (
|
|
1195
|
+
<div {...stylex.props(styles.iconLabelRow)}>
|
|
1196
|
+
{indicatorNode}
|
|
1197
|
+
<span {...stylex.props(styles.label, labelColorStyle)}>{label}</span>
|
|
1198
|
+
{statusTextNode}
|
|
1199
|
+
{isOptional && (
|
|
1200
|
+
<>
|
|
1201
|
+
<span {...stylex.props(styles.optionalDot)}>•</span>
|
|
1202
|
+
<span {...stylex.props(styles.optionalText)}>
|
|
1203
|
+
{t('@astryx.step.optional')}
|
|
1204
|
+
</span>
|
|
1205
|
+
</>
|
|
1206
|
+
)}
|
|
1207
|
+
{endContent}
|
|
1208
|
+
</div>
|
|
1209
|
+
);
|
|
1210
|
+
|
|
1211
|
+
const descriptionNode = isRenderable(description) ? (
|
|
1212
|
+
<div
|
|
1213
|
+
{...stylex.props(
|
|
1214
|
+
hasIndicator
|
|
1215
|
+
? styles.descriptionRowWithIndicator
|
|
1216
|
+
: styles.descriptionRow,
|
|
1217
|
+
)}>
|
|
1218
|
+
<span {...stylex.props(styles.description)}>{description}</span>
|
|
1219
|
+
</div>
|
|
1220
|
+
) : null;
|
|
1221
|
+
|
|
1222
|
+
const contentNode = isRenderable(children) ? (
|
|
1223
|
+
<div
|
|
1224
|
+
{...stylex.props(
|
|
1225
|
+
styles.stepContent,
|
|
1226
|
+
styles.contentIndent(
|
|
1227
|
+
separatedDensityInline,
|
|
1228
|
+
hasIndicator ? INDICATOR_GUTTER : '0px',
|
|
1229
|
+
),
|
|
1230
|
+
)}>
|
|
1231
|
+
{children}
|
|
1232
|
+
</div>
|
|
1233
|
+
) : null;
|
|
1234
|
+
|
|
1235
|
+
// Theme data attributes reflect progress + optional semantic status.
|
|
1236
|
+
const stepThemeProps = themeProps('step', {
|
|
1237
|
+
progress,
|
|
1238
|
+
status: status ?? undefined,
|
|
1239
|
+
});
|
|
1240
|
+
|
|
1241
|
+
// ======= ON-TRACK: indicator is a node on the connector =======
|
|
1242
|
+
if (indicatorPosition === 'on-track') {
|
|
1243
|
+
// Connector fill is purely progress-based (matches the separated bar):
|
|
1244
|
+
// the segment before the indicator is "reached" once we're at/past this
|
|
1245
|
+
// step; the segment after is filled only once this step is completed.
|
|
1246
|
+
// `status` never recolors the connector — only the indicator.
|
|
1247
|
+
const beforeFilled = step <= activeStep;
|
|
1248
|
+
const afterFilled = step < activeStep;
|
|
1249
|
+
// Both segments animate their fill along the track (see connectorTrack), so
|
|
1250
|
+
// the pair is picked by orientation: the vertical column scales down its
|
|
1251
|
+
// block axis, the horizontal row along the reading direction.
|
|
1252
|
+
const segFilledStyle = isVertical
|
|
1253
|
+
? styles.connectorFillV
|
|
1254
|
+
: styles.connectorFillH;
|
|
1255
|
+
const segEmptyStyle = isVertical
|
|
1256
|
+
? styles.connectorEmptyV
|
|
1257
|
+
: styles.connectorEmptyH;
|
|
1258
|
+
const beforeSegStyle = beforeFilled ? segFilledStyle : segEmptyStyle;
|
|
1259
|
+
const afterSegStyle = afterFilled ? segFilledStyle : segEmptyStyle;
|
|
1260
|
+
// First/last connector visibility is decided structurally from the step's
|
|
1261
|
+
// own <li> position (see otSegHiddenIfFirst/Last), not by counting children
|
|
1262
|
+
// in the parent — so grouping steps in a fragment can't break it.
|
|
1263
|
+
|
|
1264
|
+
// A node-to-node span is drawn by two steps: this one's trailing segments
|
|
1265
|
+
// leave its own node, the next one's leading cap arrives at the next node.
|
|
1266
|
+
// Both flip on the same condition, so left to themselves they fill together
|
|
1267
|
+
// and one gap between two nodes reads as two dashes converging on the space
|
|
1268
|
+
// between them. Handing each its own slice of the span's time, in track
|
|
1269
|
+
// order — the leaving half first, then the arriving one — is what closes
|
|
1270
|
+
// that into a single line growing.
|
|
1271
|
+
const arrivalShare = isVertical ? OT_ARRIVAL_SHARE_V : OT_ARRIVAL_SHARE_H;
|
|
1272
|
+
const leavingShare = 1 - arrivalShare;
|
|
1273
|
+
// A content slot's segment continues the leaving half below the row, so the
|
|
1274
|
+
// two divide the leaving time between them; with no slot the rail segment
|
|
1275
|
+
// takes all of it. Each step can work this out for its own trailing side
|
|
1276
|
+
// alone: the arriving cap always takes the span's last `arrivalShare`, so
|
|
1277
|
+
// the step drawing it never has to know whether the step before it carries
|
|
1278
|
+
// a slot.
|
|
1279
|
+
const hasContentSeg = isVertical && isRenderable(children);
|
|
1280
|
+
const railShare =
|
|
1281
|
+
leavingShare * (hasContentSeg ? OT_RAIL_SHARE_OF_LEAVING : 1);
|
|
1282
|
+
const contentShare = leavingShare - railShare;
|
|
1283
|
+
|
|
1284
|
+
const beforeTiming = fillTiming(step - 1, leavingShare, arrivalShare);
|
|
1285
|
+
const railTiming = fillTiming(step, 0, railShare);
|
|
1286
|
+
const contentTiming = fillTiming(step, railShare, contentShare);
|
|
1287
|
+
|
|
1288
|
+
const densitySpace =
|
|
1289
|
+
density === 'compact'
|
|
1290
|
+
? spacingVars['--spacing-1']
|
|
1291
|
+
: density === 'spacious'
|
|
1292
|
+
? spacingVars['--spacing-3']
|
|
1293
|
+
: spacingVars['--spacing-2'];
|
|
1294
|
+
|
|
1295
|
+
const labelLineNode = (
|
|
1296
|
+
<div
|
|
1297
|
+
{...stylex.props(
|
|
1298
|
+
isVertical ? styles.otLabelRowStart : styles.otLabelRowCenter,
|
|
1299
|
+
)}>
|
|
1300
|
+
<span {...stylex.props(styles.label, labelColorStyle)}>{label}</span>
|
|
1301
|
+
{statusTextNode}
|
|
1302
|
+
{isOptional && (
|
|
1303
|
+
<>
|
|
1304
|
+
<span {...stylex.props(styles.optionalDot)}>•</span>
|
|
1305
|
+
<span {...stylex.props(styles.optionalText)}>
|
|
1306
|
+
{t('@astryx.step.optional')}
|
|
1307
|
+
</span>
|
|
1308
|
+
</>
|
|
1309
|
+
)}
|
|
1310
|
+
{endContent}
|
|
1311
|
+
</div>
|
|
1312
|
+
);
|
|
1313
|
+
|
|
1314
|
+
const otDescriptionNode = isRenderable(description) ? (
|
|
1315
|
+
<span {...stylex.props(styles.description)}>{description}</span>
|
|
1316
|
+
) : null;
|
|
1317
|
+
|
|
1318
|
+
const otContentNode = !isRenderable(children) ? null : isVertical ? (
|
|
1319
|
+
<div {...stylex.props(styles.otContentWrapV)}>
|
|
1320
|
+
<div
|
|
1321
|
+
aria-hidden="true"
|
|
1322
|
+
{...mergeProps(
|
|
1323
|
+
themeProps('step-connector'),
|
|
1324
|
+
stylex.props(
|
|
1325
|
+
// connectorTrack first: it declares `position: relative` to
|
|
1326
|
+
// anchor its fill layer, but this segment is absolutely placed
|
|
1327
|
+
// on the rail beside the content slot and must keep that. An
|
|
1328
|
+
// absolute box is a containing block too, so the fill still
|
|
1329
|
+
// pins to it.
|
|
1330
|
+
styles.connectorTrack,
|
|
1331
|
+
styles.otContentSegV(densitySpace),
|
|
1332
|
+
afterSegStyle,
|
|
1333
|
+
contentTiming,
|
|
1334
|
+
styles.otSegHiddenIfLast,
|
|
1335
|
+
),
|
|
1336
|
+
)}
|
|
1337
|
+
/>
|
|
1338
|
+
<div
|
|
1339
|
+
{...stylex.props(
|
|
1340
|
+
styles.otContent,
|
|
1341
|
+
styles.contentIndent(densitySpace, INDICATOR_GUTTER),
|
|
1342
|
+
)}>
|
|
1343
|
+
{children}
|
|
1344
|
+
</div>
|
|
1345
|
+
</div>
|
|
1346
|
+
) : (
|
|
1347
|
+
<div {...stylex.props(styles.otContent)}>{children}</div>
|
|
1348
|
+
);
|
|
1349
|
+
|
|
1350
|
+
if (isVertical) {
|
|
1351
|
+
const inner = (
|
|
1352
|
+
<>
|
|
1353
|
+
<div
|
|
1354
|
+
{...stylex.props(
|
|
1355
|
+
styles.otIndicatorColV,
|
|
1356
|
+
styles.otRailBridgeV(densitySpace),
|
|
1357
|
+
)}>
|
|
1358
|
+
<div
|
|
1359
|
+
aria-hidden="true"
|
|
1360
|
+
{...mergeProps(
|
|
1361
|
+
themeProps('step-connector'),
|
|
1362
|
+
stylex.props(
|
|
1363
|
+
styles.otSegBaseV,
|
|
1364
|
+
styles.otSegLeadV(densitySpace),
|
|
1365
|
+
styles.connectorTrack,
|
|
1366
|
+
beforeSegStyle,
|
|
1367
|
+
beforeTiming,
|
|
1368
|
+
styles.otSegHiddenIfFirst,
|
|
1369
|
+
),
|
|
1370
|
+
)}
|
|
1371
|
+
/>
|
|
1372
|
+
{indicatorNode}
|
|
1373
|
+
<div
|
|
1374
|
+
aria-hidden="true"
|
|
1375
|
+
{...mergeProps(
|
|
1376
|
+
themeProps('step-connector'),
|
|
1377
|
+
stylex.props(
|
|
1378
|
+
styles.otSegBaseV,
|
|
1379
|
+
styles.otSegFlexV,
|
|
1380
|
+
styles.connectorTrack,
|
|
1381
|
+
afterSegStyle,
|
|
1382
|
+
railTiming,
|
|
1383
|
+
styles.otSegHiddenIfLast,
|
|
1384
|
+
),
|
|
1385
|
+
)}
|
|
1386
|
+
/>
|
|
1387
|
+
</div>
|
|
1388
|
+
<div {...stylex.props(styles.otBodyV)}>
|
|
1389
|
+
{labelLineNode}
|
|
1390
|
+
{otDescriptionNode}
|
|
1391
|
+
</div>
|
|
1392
|
+
</>
|
|
1393
|
+
);
|
|
1394
|
+
|
|
1395
|
+
return (
|
|
1396
|
+
<li
|
|
1397
|
+
ref={ref}
|
|
1398
|
+
{...mergeProps(
|
|
1399
|
+
stepThemeProps,
|
|
1400
|
+
stylex.props(stepMarker, styles.otVerticalRoot, xstyle),
|
|
1401
|
+
className,
|
|
1402
|
+
style,
|
|
1403
|
+
)}
|
|
1404
|
+
aria-current={isActive ? 'step' : undefined}
|
|
1405
|
+
data-testid={dataTestId}
|
|
1406
|
+
{...rest}>
|
|
1407
|
+
{isClickable ? (
|
|
1408
|
+
<button
|
|
1409
|
+
type="button"
|
|
1410
|
+
onClick={handleClick}
|
|
1411
|
+
aria-label={stepAriaLabel}
|
|
1412
|
+
{...stylex.props(
|
|
1413
|
+
styles.otInteractive,
|
|
1414
|
+
styles.otRowWrap,
|
|
1415
|
+
styles.otRowPadV(densitySpace),
|
|
1416
|
+
focusOutlineStyles.focusVisible,
|
|
1417
|
+
)}>
|
|
1418
|
+
{inner}
|
|
1419
|
+
</button>
|
|
1420
|
+
) : (
|
|
1421
|
+
<div
|
|
1422
|
+
{...stylex.props(
|
|
1423
|
+
styles.otRowWrap,
|
|
1424
|
+
styles.otRowPadV(densitySpace),
|
|
1425
|
+
)}>
|
|
1426
|
+
{inner}
|
|
1427
|
+
</div>
|
|
1428
|
+
)}
|
|
1429
|
+
{otContentNode}
|
|
1430
|
+
</li>
|
|
1431
|
+
);
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
// Horizontal on-track
|
|
1435
|
+
const innerH = (
|
|
1436
|
+
<>
|
|
1437
|
+
<div {...stylex.props(styles.otTrackRowH)}>
|
|
1438
|
+
<div
|
|
1439
|
+
aria-hidden="true"
|
|
1440
|
+
{...mergeProps(
|
|
1441
|
+
themeProps('step-connector'),
|
|
1442
|
+
stylex.props(
|
|
1443
|
+
styles.otSegH,
|
|
1444
|
+
styles.connectorTrack,
|
|
1445
|
+
beforeSegStyle,
|
|
1446
|
+
beforeTiming,
|
|
1447
|
+
styles.otSegHiddenIfFirst,
|
|
1448
|
+
),
|
|
1449
|
+
)}
|
|
1450
|
+
/>
|
|
1451
|
+
{indicatorNode}
|
|
1452
|
+
<div
|
|
1453
|
+
aria-hidden="true"
|
|
1454
|
+
{...mergeProps(
|
|
1455
|
+
themeProps('step-connector'),
|
|
1456
|
+
stylex.props(
|
|
1457
|
+
styles.otSegH,
|
|
1458
|
+
styles.connectorTrack,
|
|
1459
|
+
afterSegStyle,
|
|
1460
|
+
railTiming,
|
|
1461
|
+
styles.otSegHiddenIfLast,
|
|
1462
|
+
),
|
|
1463
|
+
)}
|
|
1464
|
+
/>
|
|
1465
|
+
</div>
|
|
1466
|
+
<div
|
|
1467
|
+
{...stylex.props(
|
|
1468
|
+
styles.otLabelWrapH,
|
|
1469
|
+
styles.otMarginTop(densitySpace),
|
|
1470
|
+
)}>
|
|
1471
|
+
{labelLineNode}
|
|
1472
|
+
{otDescriptionNode}
|
|
1473
|
+
</div>
|
|
1474
|
+
</>
|
|
1475
|
+
);
|
|
1476
|
+
|
|
1477
|
+
return (
|
|
1478
|
+
<li
|
|
1479
|
+
ref={ref}
|
|
1480
|
+
{...mergeProps(
|
|
1481
|
+
stepThemeProps,
|
|
1482
|
+
stylex.props(stepMarker, styles.otHorizontalRoot, xstyle),
|
|
1483
|
+
className,
|
|
1484
|
+
style,
|
|
1485
|
+
)}
|
|
1486
|
+
aria-current={isActive ? 'step' : undefined}
|
|
1487
|
+
data-testid={dataTestId}
|
|
1488
|
+
{...rest}>
|
|
1489
|
+
{isClickable ? (
|
|
1490
|
+
<button
|
|
1491
|
+
type="button"
|
|
1492
|
+
onClick={handleClick}
|
|
1493
|
+
aria-label={stepAriaLabel}
|
|
1494
|
+
{...stylex.props(
|
|
1495
|
+
styles.otInteractive,
|
|
1496
|
+
styles.otColWrap,
|
|
1497
|
+
styles.otPadBlock(densitySpace),
|
|
1498
|
+
focusOutlineStyles.focusVisible,
|
|
1499
|
+
)}>
|
|
1500
|
+
{innerH}
|
|
1501
|
+
</button>
|
|
1502
|
+
) : (
|
|
1503
|
+
<div
|
|
1504
|
+
{...stylex.props(
|
|
1505
|
+
styles.otColWrap,
|
|
1506
|
+
styles.otPadBlock(densitySpace),
|
|
1507
|
+
)}>
|
|
1508
|
+
{innerH}
|
|
1509
|
+
</div>
|
|
1510
|
+
)}
|
|
1511
|
+
{otContentNode}
|
|
1512
|
+
</li>
|
|
1513
|
+
);
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
// ======= VERTICAL =======
|
|
1517
|
+
if (isVertical) {
|
|
1518
|
+
return (
|
|
1519
|
+
<li
|
|
1520
|
+
ref={ref}
|
|
1521
|
+
{...mergeProps(
|
|
1522
|
+
stepThemeProps,
|
|
1523
|
+
stylex.props(stepMarker, styles.verticalRoot, xstyle),
|
|
1524
|
+
className,
|
|
1525
|
+
style,
|
|
1526
|
+
)}
|
|
1527
|
+
aria-current={isActive ? 'step' : undefined}
|
|
1528
|
+
data-testid={dataTestId}
|
|
1529
|
+
{...rest}>
|
|
1530
|
+
{/* 4px progress bar */}
|
|
1531
|
+
<div
|
|
1532
|
+
{...mergeProps(
|
|
1533
|
+
themeProps('step-bar'),
|
|
1534
|
+
stylex.props(
|
|
1535
|
+
styles.verticalBar,
|
|
1536
|
+
styles.connectorTrack,
|
|
1537
|
+
isBarFilled ? styles.connectorFillV : styles.connectorEmptyV,
|
|
1538
|
+
barTiming,
|
|
1539
|
+
),
|
|
1540
|
+
)}
|
|
1541
|
+
aria-hidden="true"
|
|
1542
|
+
/>
|
|
1543
|
+
{/* Body: button wraps only label area, children render outside */}
|
|
1544
|
+
<div {...stylex.props(styles.verticalBody)}>
|
|
1545
|
+
{isClickable ? (
|
|
1546
|
+
<button
|
|
1547
|
+
type="button"
|
|
1548
|
+
onClick={handleClick}
|
|
1549
|
+
aria-label={stepAriaLabel}
|
|
1550
|
+
{...stylex.props(
|
|
1551
|
+
styles.buttonReset,
|
|
1552
|
+
focusOutlineStyles.focusVisible,
|
|
1553
|
+
density === 'compact' && styles.densityCompact,
|
|
1554
|
+
density === 'balanced' && styles.densityBalanced,
|
|
1555
|
+
density === 'spacious' && styles.densitySpacious,
|
|
1556
|
+
styles.densityInline(separatedDensityInline),
|
|
1557
|
+
)}>
|
|
1558
|
+
{iconLabelNode}
|
|
1559
|
+
{descriptionNode}
|
|
1560
|
+
</button>
|
|
1561
|
+
) : (
|
|
1562
|
+
<div
|
|
1563
|
+
{...stylex.props(
|
|
1564
|
+
density === 'compact' && styles.densityCompact,
|
|
1565
|
+
density === 'balanced' && styles.densityBalanced,
|
|
1566
|
+
density === 'spacious' && styles.densitySpacious,
|
|
1567
|
+
styles.densityInline(separatedDensityInline),
|
|
1568
|
+
)}>
|
|
1569
|
+
{iconLabelNode}
|
|
1570
|
+
{descriptionNode}
|
|
1571
|
+
</div>
|
|
1572
|
+
)}
|
|
1573
|
+
{contentNode}
|
|
1574
|
+
</div>
|
|
1575
|
+
</li>
|
|
1576
|
+
);
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
// ======= HORIZONTAL =======
|
|
1580
|
+
return (
|
|
1581
|
+
<li
|
|
1582
|
+
ref={ref}
|
|
1583
|
+
{...mergeProps(
|
|
1584
|
+
stepThemeProps,
|
|
1585
|
+
stylex.props(stepMarker, styles.horizontalStep, xstyle),
|
|
1586
|
+
className,
|
|
1587
|
+
style,
|
|
1588
|
+
)}
|
|
1589
|
+
aria-current={isActive ? 'step' : undefined}
|
|
1590
|
+
data-testid={dataTestId}
|
|
1591
|
+
{...rest}>
|
|
1592
|
+
{/* 4px progress bar segment for this step */}
|
|
1593
|
+
<div
|
|
1594
|
+
{...mergeProps(
|
|
1595
|
+
themeProps('step-bar'),
|
|
1596
|
+
stylex.props(
|
|
1597
|
+
styles.horizontalBar,
|
|
1598
|
+
styles.connectorTrack,
|
|
1599
|
+
isBarFilled ? styles.connectorFillH : styles.connectorEmptyH,
|
|
1600
|
+
barTiming,
|
|
1601
|
+
),
|
|
1602
|
+
)}
|
|
1603
|
+
aria-hidden="true"
|
|
1604
|
+
/>
|
|
1605
|
+
{isClickable ? (
|
|
1606
|
+
<button
|
|
1607
|
+
type="button"
|
|
1608
|
+
onClick={handleClick}
|
|
1609
|
+
aria-label={stepAriaLabel}
|
|
1610
|
+
{...stylex.props(
|
|
1611
|
+
styles.buttonReset,
|
|
1612
|
+
focusOutlineStyles.focusVisible,
|
|
1613
|
+
density === 'compact' && styles.densityCompact,
|
|
1614
|
+
density === 'balanced' && styles.densityBalanced,
|
|
1615
|
+
density === 'spacious' && styles.densitySpacious,
|
|
1616
|
+
styles.densityInline(separatedDensityInline),
|
|
1617
|
+
)}>
|
|
1618
|
+
{iconLabelNode}
|
|
1619
|
+
{descriptionNode}
|
|
1620
|
+
</button>
|
|
1621
|
+
) : (
|
|
1622
|
+
<div
|
|
1623
|
+
{...stylex.props(
|
|
1624
|
+
density === 'compact' && styles.densityCompact,
|
|
1625
|
+
density === 'balanced' && styles.densityBalanced,
|
|
1626
|
+
density === 'spacious' && styles.densitySpacious,
|
|
1627
|
+
styles.densityInline(separatedDensityInline),
|
|
1628
|
+
)}>
|
|
1629
|
+
{iconLabelNode}
|
|
1630
|
+
{descriptionNode}
|
|
1631
|
+
</div>
|
|
1632
|
+
)}
|
|
1633
|
+
{contentNode}
|
|
1634
|
+
</li>
|
|
1635
|
+
);
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
Step.displayName = 'Step';
|