@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
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
* @position Core implementation; consumed by index.ts, tested by NumberInput.test.tsx
|
|
10
10
|
*
|
|
11
11
|
* SYNC: When modified, update these files to stay in sync:
|
|
12
|
+
* - /packages/core/src/NumberInput/numberParser.ts (locale-aware parsing)
|
|
13
|
+
* - /packages/core/src/NumberInput/numberInputCommit.ts (draft commit policy)
|
|
12
14
|
* - /packages/core/src/NumberInput/NumberInput.doc.mjs (props table, features, implementation notes)
|
|
13
15
|
* - /packages/core/src/NumberInput/NumberInput.test.tsx (tests for new/changed behavior)
|
|
14
16
|
* - /packages/core/src/NumberInput/index.ts (exports if types change)
|
|
@@ -213,7 +215,9 @@ import {isImeKeyEvent, mergeProps, mergeRefs} from '../utils';
|
|
|
213
215
|
import type {BaseProps} from '../BaseProps';
|
|
214
216
|
import type {SizeValue} from '../utils/types';
|
|
215
217
|
import {themeProps} from '../utils/themeProps';
|
|
216
|
-
import {useTranslator} from '../i18n';
|
|
218
|
+
import {useTranslator, useLocale} from '../i18n';
|
|
219
|
+
import {formatEditableNumber} from './numberParser';
|
|
220
|
+
import {parseNumberInput, resolveNumberInputCommit} from './numberInputCommit';
|
|
217
221
|
|
|
218
222
|
interface NumberInputPropsBase extends Omit<
|
|
219
223
|
BaseProps,
|
|
@@ -348,10 +352,12 @@ interface NumberInputPropsBase extends Omit<
|
|
|
348
352
|
autoComplete?: string;
|
|
349
353
|
/**
|
|
350
354
|
* The minimum value allowed.
|
|
355
|
+
* A smaller entry commits at this value on blur or Enter.
|
|
351
356
|
*/
|
|
352
357
|
min?: number | null;
|
|
353
358
|
/**
|
|
354
359
|
* The maximum value allowed.
|
|
360
|
+
* A larger entry commits at this value on blur or Enter.
|
|
355
361
|
*/
|
|
356
362
|
max?: number | null;
|
|
357
363
|
/**
|
|
@@ -425,46 +431,6 @@ type NumberInputPropsClearable = NumberInputPropsBase & {
|
|
|
425
431
|
export type NumberInputProps =
|
|
426
432
|
NumberInputPropsNonClearable | NumberInputPropsClearable;
|
|
427
433
|
|
|
428
|
-
/**
|
|
429
|
-
* Parse and validate a string input as a number.
|
|
430
|
-
* Returns null if the input is not a valid number or fails validation.
|
|
431
|
-
*/
|
|
432
|
-
function parseNumberInput(
|
|
433
|
-
input: string,
|
|
434
|
-
options: {
|
|
435
|
-
min?: number | null;
|
|
436
|
-
max?: number | null;
|
|
437
|
-
isIntegerOnly?: boolean;
|
|
438
|
-
},
|
|
439
|
-
): number | null {
|
|
440
|
-
const trimmed = input.trim();
|
|
441
|
-
if (trimmed === '' || trimmed === '-') {
|
|
442
|
-
return null;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
const num = Number(trimmed);
|
|
446
|
-
if (!Number.isFinite(num)) {
|
|
447
|
-
return null;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// Check integer constraint
|
|
451
|
-
if (options.isIntegerOnly && !Number.isInteger(num)) {
|
|
452
|
-
return null;
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
// Check min constraint
|
|
456
|
-
if (options.min != null && num < options.min) {
|
|
457
|
-
return null;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
// Check max constraint
|
|
461
|
-
if (options.max != null && num > options.max) {
|
|
462
|
-
return null;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
return num;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
434
|
type StepDirection = -1 | 1;
|
|
469
435
|
|
|
470
436
|
function getDecimalPlaces(value: number): number {
|
|
@@ -549,7 +515,8 @@ function getSteppedValue({
|
|
|
549
515
|
|
|
550
516
|
/**
|
|
551
517
|
* A number input component for collecting numeric user input.
|
|
552
|
-
*
|
|
518
|
+
* Commits text edits on blur or Enter and only calls onChange when the whole
|
|
519
|
+
* draft passes validation.
|
|
553
520
|
*
|
|
554
521
|
* @example
|
|
555
522
|
* ```
|
|
@@ -599,6 +566,7 @@ export function NumberInput({
|
|
|
599
566
|
...rest
|
|
600
567
|
}: NumberInputProps) {
|
|
601
568
|
const t = useTranslator();
|
|
569
|
+
const locale = useLocale();
|
|
602
570
|
const isEffectivelyRequired = useResolvedRequired({isRequired, isOptional});
|
|
603
571
|
const size = useSize(sizeProp, 'md');
|
|
604
572
|
const id = useId();
|
|
@@ -653,6 +621,11 @@ export function NumberInput({
|
|
|
653
621
|
inputGroup,
|
|
654
622
|
);
|
|
655
623
|
|
|
624
|
+
const parseInput = useCallback(
|
|
625
|
+
(text: string) => parseNumberInput(text, {min, max, isIntegerOnly, locale}),
|
|
626
|
+
[isIntegerOnly, locale, max, min],
|
|
627
|
+
);
|
|
628
|
+
|
|
656
629
|
const formattedValue = useMemo(() => {
|
|
657
630
|
if (value == null) {
|
|
658
631
|
return '';
|
|
@@ -669,36 +642,29 @@ export function NumberInput({
|
|
|
669
642
|
if (value == null) {
|
|
670
643
|
return '';
|
|
671
644
|
}
|
|
672
|
-
return isFocused ?
|
|
673
|
-
}, [formattedValue, isFocused, pendingInput, value]);
|
|
645
|
+
return isFocused ? formatEditableNumber(value, locale) : formattedValue;
|
|
646
|
+
}, [formattedValue, isFocused, locale, pendingInput, value]);
|
|
674
647
|
|
|
675
648
|
// Check if current pending input is valid (for styling purposes)
|
|
676
649
|
const isInputValid = useMemo(() => {
|
|
677
650
|
if (pendingInput === null || !pendingInput.trim()) {
|
|
678
651
|
return true;
|
|
679
652
|
}
|
|
680
|
-
return
|
|
681
|
-
}, [pendingInput,
|
|
653
|
+
return parseInput(pendingInput) !== null;
|
|
654
|
+
}, [pendingInput, parseInput]);
|
|
682
655
|
|
|
683
|
-
//
|
|
656
|
+
// Keep the whole text edit as a draft until an explicit commit boundary.
|
|
684
657
|
const handleInputChange = useCallback(
|
|
685
658
|
(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
686
659
|
// Value can't change while showing a disabled message (the field is
|
|
687
660
|
// read-only and non-native-disabled), but guard the handler too so the
|
|
688
|
-
// pending value
|
|
661
|
+
// pending value never changes.
|
|
689
662
|
if (isDisabled || isReadOnly) {
|
|
690
663
|
return;
|
|
691
664
|
}
|
|
692
|
-
|
|
693
|
-
setPendingInput(newValue);
|
|
694
|
-
|
|
695
|
-
// If the input is valid, update immediately
|
|
696
|
-
const parsed = parseNumberInput(newValue, {min, max, isIntegerOnly});
|
|
697
|
-
if (parsed !== null && parsed !== value) {
|
|
698
|
-
onChange(parsed);
|
|
699
|
-
}
|
|
665
|
+
setPendingInput(e.target.value);
|
|
700
666
|
},
|
|
701
|
-
[
|
|
667
|
+
[isDisabled, isReadOnly],
|
|
702
668
|
);
|
|
703
669
|
|
|
704
670
|
// Handle focus
|
|
@@ -710,34 +676,45 @@ export function NumberInput({
|
|
|
710
676
|
[onFocus],
|
|
711
677
|
);
|
|
712
678
|
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
679
|
+
const commitPendingInput = useCallback(
|
|
680
|
+
(trigger: 'blur' | 'Enter') => {
|
|
681
|
+
if (pendingInput === null) {
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
const decision = resolveNumberInputCommit(pendingInput, {
|
|
686
|
+
min,
|
|
687
|
+
max,
|
|
688
|
+
isIntegerOnly,
|
|
689
|
+
locale,
|
|
690
|
+
hasClear: !!hasClear,
|
|
691
|
+
});
|
|
692
|
+
if (
|
|
693
|
+
trigger === 'blur' ||
|
|
694
|
+
(decision.type === 'commit' && decision.didClamp)
|
|
695
|
+
) {
|
|
696
|
+
setPendingInput(null);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
if (decision.type === 'clear') {
|
|
700
|
+
if (hasClear && value != null) {
|
|
701
|
+
onChange(null);
|
|
732
702
|
}
|
|
703
|
+
} else if (decision.type === 'commit' && decision.value !== value) {
|
|
704
|
+
onChange(decision.value);
|
|
733
705
|
}
|
|
706
|
+
},
|
|
707
|
+
[hasClear, isIntegerOnly, locale, max, min, onChange, pendingInput, value],
|
|
708
|
+
);
|
|
734
709
|
|
|
735
|
-
|
|
736
|
-
|
|
710
|
+
// Blur ends the edit and displays the resulting committed value.
|
|
711
|
+
const handleBlur = useCallback(
|
|
712
|
+
(e: FocusEvent<HTMLInputElement>) => {
|
|
713
|
+
commitPendingInput('blur');
|
|
737
714
|
setIsFocused(false);
|
|
738
715
|
onBlur?.(e);
|
|
739
716
|
},
|
|
740
|
-
[
|
|
717
|
+
[commitPendingInput, onBlur],
|
|
741
718
|
);
|
|
742
719
|
|
|
743
720
|
const valueForStepping = useMemo(() => {
|
|
@@ -747,10 +724,8 @@ export function NumberInput({
|
|
|
747
724
|
if (pendingInput.trim() === '') {
|
|
748
725
|
return null;
|
|
749
726
|
}
|
|
750
|
-
return (
|
|
751
|
-
|
|
752
|
-
);
|
|
753
|
-
}, [isIntegerOnly, max, min, pendingInput, value]);
|
|
727
|
+
return parseInput(pendingInput) ?? value ?? null;
|
|
728
|
+
}, [parseInput, pendingInput, value]);
|
|
754
729
|
|
|
755
730
|
const getNextValue = useCallback(
|
|
756
731
|
(direction: StepDirection) =>
|
|
@@ -806,41 +781,12 @@ export function NumberInput({
|
|
|
806
781
|
return;
|
|
807
782
|
}
|
|
808
783
|
if (e.key === 'Enter') {
|
|
809
|
-
|
|
810
|
-
if (pendingInput !== null) {
|
|
811
|
-
if (hasClear && pendingInput.trim() === '') {
|
|
812
|
-
// Same clearable contract as blur: Enter on an emptied input
|
|
813
|
-
// commits null instead of reverting.
|
|
814
|
-
if (value != null) {
|
|
815
|
-
onChange(null);
|
|
816
|
-
}
|
|
817
|
-
} else {
|
|
818
|
-
const parsed = parseNumberInput(pendingInput, {
|
|
819
|
-
min,
|
|
820
|
-
max,
|
|
821
|
-
isIntegerOnly,
|
|
822
|
-
});
|
|
823
|
-
if (parsed !== null && parsed !== value) {
|
|
824
|
-
onChange(parsed);
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
}
|
|
784
|
+
commitPendingInput('Enter');
|
|
828
785
|
onEnter?.();
|
|
829
786
|
}
|
|
830
787
|
onKeyDown?.(e);
|
|
831
788
|
},
|
|
832
|
-
[
|
|
833
|
-
pendingInput,
|
|
834
|
-
value,
|
|
835
|
-
onChange,
|
|
836
|
-
min,
|
|
837
|
-
max,
|
|
838
|
-
isIntegerOnly,
|
|
839
|
-
onEnter,
|
|
840
|
-
onKeyDown,
|
|
841
|
-
hasClear,
|
|
842
|
-
stepValue,
|
|
843
|
-
],
|
|
789
|
+
[commitPendingInput, onEnter, onKeyDown, stepValue],
|
|
844
790
|
);
|
|
845
791
|
|
|
846
792
|
// React's delegated wheel listener can be passive, so use a native,
|
|
@@ -963,6 +909,8 @@ export function NumberInput({
|
|
|
963
909
|
data-autofocus={hasAutoFocus || undefined}
|
|
964
910
|
aria-valuemin={min ?? undefined}
|
|
965
911
|
aria-valuemax={max ?? undefined}
|
|
912
|
+
// The ARIA value and hidden form input expose the committed value;
|
|
913
|
+
// pendingInput is still an uncommitted edit and may be invalid.
|
|
966
914
|
aria-valuenow={value ?? undefined}
|
|
967
915
|
aria-valuetext={
|
|
968
916
|
value == null || !formatValue ? undefined : formattedValue
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file numberInputCommit.test.ts
|
|
5
|
+
* @input Uses Vitest and the NumberInput draft commit policy
|
|
6
|
+
* @output Focused tests for commit, clear, clamp, and revert decisions
|
|
7
|
+
* @position Testing; validates numberInputCommit.ts independently of React
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {describe, expect, it} from 'vitest';
|
|
11
|
+
import {parseNumberInput, resolveNumberInputCommit} from './numberInputCommit';
|
|
12
|
+
|
|
13
|
+
describe('parseNumberInput', () => {
|
|
14
|
+
it('validates the complete localized draft', () => {
|
|
15
|
+
expect(parseNumberInput('1.234.567', {locale: 'de-DE'})).toBe(1234567);
|
|
16
|
+
expect(parseNumberInput('1·234·567', {locale: 'de-DE'})).toBeNull();
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('resolveNumberInputCommit', () => {
|
|
21
|
+
it('commits one valid localized draft', () => {
|
|
22
|
+
expect(
|
|
23
|
+
resolveNumberInputCommit('1.234.567', {
|
|
24
|
+
locale: 'de-DE',
|
|
25
|
+
hasClear: false,
|
|
26
|
+
}),
|
|
27
|
+
).toEqual({
|
|
28
|
+
type: 'commit',
|
|
29
|
+
value: 1234567,
|
|
30
|
+
didClamp: false,
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('reverts the whole draft when parsing fails', () => {
|
|
35
|
+
expect(
|
|
36
|
+
resolveNumberInputCommit('1·234·567', {
|
|
37
|
+
locale: 'en-US',
|
|
38
|
+
hasClear: false,
|
|
39
|
+
}),
|
|
40
|
+
).toEqual({type: 'revert'});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('clamps an out-of-range draft and requests normalization', () => {
|
|
44
|
+
expect(
|
|
45
|
+
resolveNumberInputCommit('100', {
|
|
46
|
+
min: 1,
|
|
47
|
+
max: 2,
|
|
48
|
+
isIntegerOnly: true,
|
|
49
|
+
hasClear: false,
|
|
50
|
+
}),
|
|
51
|
+
).toEqual({type: 'commit', value: 2, didClamp: true});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('distinguishes a clearable empty draft from a revert', () => {
|
|
55
|
+
expect(resolveNumberInputCommit('', {hasClear: true})).toEqual({
|
|
56
|
+
type: 'clear',
|
|
57
|
+
});
|
|
58
|
+
expect(resolveNumberInputCommit('', {hasClear: false})).toEqual({
|
|
59
|
+
type: 'revert',
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('reverts when no value can satisfy the bounds', () => {
|
|
64
|
+
expect(
|
|
65
|
+
resolveNumberInputCommit('10', {
|
|
66
|
+
min: 5,
|
|
67
|
+
max: 2,
|
|
68
|
+
hasClear: false,
|
|
69
|
+
}),
|
|
70
|
+
).toEqual({type: 'revert'});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file numberInputCommit.ts
|
|
5
|
+
* @input A NumberInput draft and the field's validation constraints
|
|
6
|
+
* @output A single commit, clear, or revert decision for the whole draft
|
|
7
|
+
* @position Shared internal draft validation and commit policy for NumberInput and its live consumers
|
|
8
|
+
*
|
|
9
|
+
* SYNC: When modified, update these files to stay in sync:
|
|
10
|
+
* - /packages/core/src/NumberInput/NumberInput.tsx
|
|
11
|
+
* - /packages/core/src/NumberInput/numberInputCommit.test.ts
|
|
12
|
+
* - /packages/core/src/Table/plugins/filtering/useTableFiltering.tsx
|
|
13
|
+
* - /packages/core/src/PowerSearch/PowerSearchValueEditor.tsx
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type {Locale} from '../i18n';
|
|
17
|
+
import {parseLocaleNumber} from './numberParser';
|
|
18
|
+
|
|
19
|
+
interface NumberInputValidationOptions {
|
|
20
|
+
min?: number | null;
|
|
21
|
+
max?: number | null;
|
|
22
|
+
isIntegerOnly?: boolean;
|
|
23
|
+
locale?: Locale;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type NumberInputCommitDecision =
|
|
27
|
+
| {type: 'commit'; value: number; didClamp: boolean}
|
|
28
|
+
| {type: 'clear'}
|
|
29
|
+
| {type: 'revert'};
|
|
30
|
+
|
|
31
|
+
function parseNumericInput(
|
|
32
|
+
input: string,
|
|
33
|
+
options: Pick<NumberInputValidationOptions, 'isIntegerOnly' | 'locale'>,
|
|
34
|
+
): number | null {
|
|
35
|
+
const trimmed = input.trim();
|
|
36
|
+
if (trimmed === '') {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const value = parseLocaleNumber(trimmed, options.locale);
|
|
41
|
+
if (value == null || !Number.isFinite(value)) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
if (options.isIntegerOnly && !Number.isInteger(value)) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function parseNumberInput(
|
|
51
|
+
input: string,
|
|
52
|
+
options: NumberInputValidationOptions,
|
|
53
|
+
): number | null {
|
|
54
|
+
const value = parseNumericInput(input, options);
|
|
55
|
+
if (value === null) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
if (options.min != null && value < options.min) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
if (options.max != null && value > options.max) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function resolveNumberInputCommit(
|
|
68
|
+
input: string,
|
|
69
|
+
options: NumberInputValidationOptions & {hasClear: boolean},
|
|
70
|
+
): NumberInputCommitDecision {
|
|
71
|
+
if (input.trim() === '') {
|
|
72
|
+
return options.hasClear ? {type: 'clear'} : {type: 'revert'};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const value = parseNumericInput(input, options);
|
|
76
|
+
if (value === null) {
|
|
77
|
+
return {type: 'revert'};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const min =
|
|
81
|
+
options.min == null
|
|
82
|
+
? null
|
|
83
|
+
: options.isIntegerOnly
|
|
84
|
+
? Math.ceil(options.min)
|
|
85
|
+
: options.min;
|
|
86
|
+
const max =
|
|
87
|
+
options.max == null
|
|
88
|
+
? null
|
|
89
|
+
: options.isIntegerOnly
|
|
90
|
+
? Math.floor(options.max)
|
|
91
|
+
: options.max;
|
|
92
|
+
if (min != null && max != null && min > max) {
|
|
93
|
+
return {type: 'revert'};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const committedValue =
|
|
97
|
+
min != null && value < min ? min : max != null && value > max ? max : value;
|
|
98
|
+
return {
|
|
99
|
+
type: 'commit',
|
|
100
|
+
value: committedValue,
|
|
101
|
+
didClamp: committedValue !== value,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file numberParser.docblock.test.ts
|
|
5
|
+
* @input The rule paragraph in numberParser.ts, and the parser itself
|
|
6
|
+
* @output Nothing; fails when the two disagree
|
|
7
|
+
* @position Test-only; grades the prose in numberParser.ts against the code
|
|
8
|
+
*
|
|
9
|
+
* The file docblock of numberParser.ts states the rule the parser follows.
|
|
10
|
+
* A sentence like that rots silently — the code moves and the words stay —
|
|
11
|
+
* and the next person to change this file reads the words. So the paragraph
|
|
12
|
+
* is executable here: `predict` implements it, clause by clause, and every
|
|
13
|
+
* input is graded prose-against-parser.
|
|
14
|
+
*
|
|
15
|
+
* The one discipline that makes the number mean anything: `predict` may use
|
|
16
|
+
* ONLY what the paragraph says. Every branch below quotes the clause it
|
|
17
|
+
* implements, and those quotes are asserted verbatim against the source, so
|
|
18
|
+
* a paragraph that loses a clause fails here instead of drifting. If the
|
|
19
|
+
* parser grows a rule the words do not mention, this test goes red — that is
|
|
20
|
+
* the point, not a defect.
|
|
21
|
+
*
|
|
22
|
+
* SYNC: When modified, update these files to stay in sync:
|
|
23
|
+
* - /packages/core/src/NumberInput/numberParser.ts
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import {describe, it, expect} from 'vitest';
|
|
27
|
+
import {parseLocaleNumber} from './numberParser';
|
|
28
|
+
import SOURCE from './numberParser.ts?raw';
|
|
29
|
+
|
|
30
|
+
/** The docblock as running prose: comment markers and line breaks removed. */
|
|
31
|
+
const PROSE = SOURCE.replace(/^\s*\*\s?/gm, '').replace(/\s+/g, ' ');
|
|
32
|
+
|
|
33
|
+
/** Every clause `predict` is allowed to know, quoted from the docblock. */
|
|
34
|
+
const CLAUSES = {
|
|
35
|
+
'defer to the alphabet': 'SEPARATOR_CHARS below is a bounded alphabet',
|
|
36
|
+
'fold the space family': 'the whole space family folds to it under NFKC',
|
|
37
|
+
'refuse what no reading fits': 'text that no reading fits returns null',
|
|
38
|
+
'prefer the locale': 'Where both readings are well formed the locale',
|
|
39
|
+
'size every group':
|
|
40
|
+
"Grouping is well formed only when every group fits the locale's own " +
|
|
41
|
+
'sizes: the last exactly the primary size, each inner one exactly the ' +
|
|
42
|
+
'secondary, the first no longer than that.',
|
|
43
|
+
'fall back to the decimal point': 'keep the full stop as a decimal point',
|
|
44
|
+
'read three-digit groups anywhere':
|
|
45
|
+
'Groups of three fit every locale, so a separator repeated with ' +
|
|
46
|
+
'three-digit groups is grouping in all of them',
|
|
47
|
+
'need only one group-only occurrence':
|
|
48
|
+
'for a space or apostrophe, which no locale writes as a decimal point, ' +
|
|
49
|
+
'one occurrence is enough',
|
|
50
|
+
} as const;
|
|
51
|
+
|
|
52
|
+
/** The bounded alphabet the docblock defers to, read out of the source. */
|
|
53
|
+
const ALPHABET: ReadonlySet<string> = new Set(
|
|
54
|
+
JSON.parse(
|
|
55
|
+
`"${/const SEPARATOR_CHARS = "([^"]*)"/.exec(SOURCE)?.[1] ?? ''}"`,
|
|
56
|
+
),
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const symbolsCache = new Map<string, ReturnType<typeof readSymbols>>();
|
|
60
|
+
function readSymbols(locale: string) {
|
|
61
|
+
const parts = new Intl.NumberFormat(locale).formatToParts(12345.6);
|
|
62
|
+
const sizes = new Intl.NumberFormat(locale)
|
|
63
|
+
.formatToParts(11111111111)
|
|
64
|
+
.filter(p => p.type === 'integer')
|
|
65
|
+
.map(p => p.value.length);
|
|
66
|
+
const grouped = sizes.length > 1;
|
|
67
|
+
return {
|
|
68
|
+
group: (parts.find(p => p.type === 'group')?.value ?? ',').normalize(
|
|
69
|
+
'NFKC',
|
|
70
|
+
),
|
|
71
|
+
decimal: parts.find(p => p.type === 'decimal')?.value ?? '.',
|
|
72
|
+
primary: grouped ? sizes[sizes.length - 1] : 3,
|
|
73
|
+
secondary: grouped ? sizes[sizes.length - 2] : 3,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function symbolsOf(locale: string) {
|
|
77
|
+
let s = symbolsCache.get(locale);
|
|
78
|
+
if (s === undefined) {
|
|
79
|
+
s = readSymbols(locale);
|
|
80
|
+
symbolsCache.set(locale, s);
|
|
81
|
+
}
|
|
82
|
+
return s;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** CLAUSES['size every group'], with the sizes it is handed. */
|
|
86
|
+
function groupsFit(chunks: string[], primary: number, secondary: number) {
|
|
87
|
+
return (
|
|
88
|
+
chunks[chunks.length - 1].length === primary &&
|
|
89
|
+
chunks[0].length <= secondary &&
|
|
90
|
+
chunks.slice(1, -1).every(chunk => chunk.length === secondary)
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** CLAUSES['need only one group-only occurrence']: "a space or apostrophe". */
|
|
95
|
+
const isSpaceOrApostrophe = (char: string) => /^[\s'\u2019]$/.test(char);
|
|
96
|
+
|
|
97
|
+
const SKIPPED = Symbol('outside the paragraph');
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The paragraph, as code. Digits with one separator character repeated
|
|
101
|
+
* between them is the shape it describes; anything else is not this
|
|
102
|
+
* paragraph's subject and is skipped rather than guessed at.
|
|
103
|
+
*/
|
|
104
|
+
function predict(input: string, locale: string): number | null | symbol {
|
|
105
|
+
// CLAUSES['fold the space family'].
|
|
106
|
+
const text = input.normalize('NFKC');
|
|
107
|
+
if (!/^\d/.test(text) || !/\d$/.test(text)) {
|
|
108
|
+
return SKIPPED;
|
|
109
|
+
}
|
|
110
|
+
const separators = [...new Set(text.replace(/\d/g, ''))];
|
|
111
|
+
if (separators.length !== 1) {
|
|
112
|
+
return SKIPPED;
|
|
113
|
+
}
|
|
114
|
+
const [separator] = separators;
|
|
115
|
+
|
|
116
|
+
// CLAUSES['defer to the alphabet'].
|
|
117
|
+
if (!ALPHABET.has(separator)) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const chunks = text.split(separator);
|
|
122
|
+
const {group, decimal, primary, secondary} = symbolsOf(locale);
|
|
123
|
+
|
|
124
|
+
// CLAUSES['prefer the locale'] + CLAUSES['size every group']: of the two
|
|
125
|
+
// locale readings, grouping wins wherever it is well formed.
|
|
126
|
+
if (separator === group && groupsFit(chunks, primary, secondary)) {
|
|
127
|
+
return Number(chunks.join(''));
|
|
128
|
+
}
|
|
129
|
+
// The locale's other reading. A decimal point appears once.
|
|
130
|
+
if (separator === decimal && chunks.length === 2) {
|
|
131
|
+
return Number(`${chunks[0]}.${chunks[1]}`);
|
|
132
|
+
}
|
|
133
|
+
// CLAUSES['fall back to the decimal point'], for a full stop the locale
|
|
134
|
+
// could read neither way.
|
|
135
|
+
if (separator === '.' && chunks.length === 2) {
|
|
136
|
+
return Number(text);
|
|
137
|
+
}
|
|
138
|
+
// CLAUSES['read three-digit groups anywhere'], and CLAUSES['need only one
|
|
139
|
+
// group-only occurrence'] for how many occurrences it takes.
|
|
140
|
+
const enough = isSpaceOrApostrophe(separator) ? 1 : 2;
|
|
141
|
+
if (chunks.length - 1 >= enough && groupsFit(chunks, 3, 3)) {
|
|
142
|
+
return Number(chunks.join(''));
|
|
143
|
+
}
|
|
144
|
+
// CLAUSES['refuse what no reading fits'].
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const LOCALES = ['en-US', 'de-DE', 'fr-FR', 'de-CH', 'en-IN', 'ar-SA'];
|
|
149
|
+
|
|
150
|
+
/** The shapes the paragraph names, plus the seams around each clause. */
|
|
151
|
+
const NAMED: [string, string][] = [
|
|
152
|
+
['1.234', 'de-DE'],
|
|
153
|
+
['1.2345', 'de-DE'],
|
|
154
|
+
['1234.567', 'de-DE'],
|
|
155
|
+
['12345.678', 'de-DE'],
|
|
156
|
+
['12.345', 'de-DE'],
|
|
157
|
+
['1.23', 'de-DE'],
|
|
158
|
+
['1,234,567', 'en-US'],
|
|
159
|
+
['1,234,567', 'de-DE'],
|
|
160
|
+
['1.234.567', 'en-US'],
|
|
161
|
+
['1234.567.890', 'de-DE'],
|
|
162
|
+
['1 234', 'en-US'],
|
|
163
|
+
['1\u00A0234', 'en-US'],
|
|
164
|
+
['1 234 567', 'en-US'],
|
|
165
|
+
['1234 567', 'fr-FR'],
|
|
166
|
+
["1'234", 'de-CH'],
|
|
167
|
+
["1'234", 'en-US'],
|
|
168
|
+
['1\u2019234', 'de-CH'],
|
|
169
|
+
['1,234', 'en-IN'],
|
|
170
|
+
['12,34,567', 'en-IN'],
|
|
171
|
+
['123,45,678', 'en-IN'],
|
|
172
|
+
['1,2,345', 'en-IN'],
|
|
173
|
+
['1\u066C234', 'ar-SA'],
|
|
174
|
+
['1\u066B5', 'ar-SA'],
|
|
175
|
+
['1\u00B7234\u00B7567', 'en-US'],
|
|
176
|
+
['123-456-789', 'en-US'],
|
|
177
|
+
];
|
|
178
|
+
|
|
179
|
+
/** Every one-, two- and three-separator shape over short digit runs. */
|
|
180
|
+
function* generated() {
|
|
181
|
+
const runs = ['1', '12', '123', '1234', '12345'];
|
|
182
|
+
for (const locale of LOCALES) {
|
|
183
|
+
for (const separator of ALPHABET) {
|
|
184
|
+
for (const a of runs) {
|
|
185
|
+
for (const b of runs) {
|
|
186
|
+
yield [`${a}${separator}${b}`, locale] as [string, string];
|
|
187
|
+
for (const c of runs) {
|
|
188
|
+
yield [`${a}${separator}${b}${separator}${c}`, locale] as [
|
|
189
|
+
string,
|
|
190
|
+
string,
|
|
191
|
+
];
|
|
192
|
+
for (const d of runs) {
|
|
193
|
+
yield [
|
|
194
|
+
`${a}${separator}${b}${separator}${c}${separator}${d}`,
|
|
195
|
+
locale,
|
|
196
|
+
] as [string, string];
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
describe('the numberParser docblock', () => {
|
|
206
|
+
it.each(Object.entries(CLAUSES))('still says: %s', (_name, quote) => {
|
|
207
|
+
expect(PROSE).toContain(quote);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it.each(NAMED)('reads %j in %s the way the parser does', (input, locale) => {
|
|
211
|
+
expect(predict(input, locale)).toBe(parseLocaleNumber(input, locale));
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('reads every generated shape the way the parser does', () => {
|
|
215
|
+
const disagreements: string[] = [];
|
|
216
|
+
let checked = 0;
|
|
217
|
+
for (const [input, locale] of generated()) {
|
|
218
|
+
const said = predict(input, locale);
|
|
219
|
+
if (said === SKIPPED) {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
checked++;
|
|
223
|
+
const did = parseLocaleNumber(input, locale);
|
|
224
|
+
if (!Object.is(said, did)) {
|
|
225
|
+
disagreements.push(
|
|
226
|
+
`${JSON.stringify(input)} in ${locale}: docblock ${String(said)}, parser ${String(did)}`,
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// The sweep is the evidence; a shrunken one silently proves nothing.
|
|
231
|
+
expect(checked).toBeGreaterThan(30000);
|
|
232
|
+
expect(disagreements.slice(0, 10)).toEqual([]);
|
|
233
|
+
});
|
|
234
|
+
});
|