@astryxdesign/core 0.1.8 → 0.1.9
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 +100 -0
- package/dist/AppShell/AppShell.d.ts.map +1 -1
- package/dist/AppShell/AppShell.js +6 -2
- package/dist/AppShell/AppShellMobileContext.d.ts +8 -0
- package/dist/AppShell/AppShellMobileContext.d.ts.map +1 -1
- package/dist/Avatar/Avatar.d.ts +18 -2
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +63 -7
- package/dist/Avatar/AvatarStatusDot.d.ts +33 -5
- package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
- package/dist/Avatar/AvatarStatusDot.js +144 -19
- package/dist/Banner/Banner.d.ts +9 -1
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +31 -2
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts +31 -2
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +208 -6
- package/dist/Breadcrumbs/index.d.ts +5 -0
- package/dist/Breadcrumbs/index.d.ts.map +1 -1
- package/dist/Breadcrumbs/index.js +9 -1
- package/dist/Button/Button.d.ts +8 -2
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +31 -2
- package/dist/ButtonGroup/ButtonGroup.d.ts +9 -1
- package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
- package/dist/ButtonGroup/ButtonGroup.js +30 -1
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +30 -2
- package/dist/Calendar/dayCellUtils.js +1 -1
- package/dist/Card/Card.d.ts +8 -2
- package/dist/Card/Card.d.ts.map +1 -1
- package/dist/Card/Card.js +28 -2
- package/dist/Carousel/Carousel.d.ts +4 -1
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +24 -4
- package/dist/Chat/ChatComposer.d.ts +8 -0
- package/dist/Chat/ChatComposer.d.ts.map +1 -1
- package/dist/Chat/ChatComposer.js +46 -7
- package/dist/Chat/ChatComposerDrawer.d.ts +2 -0
- package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
- package/dist/Chat/ChatComposerDrawer.js +11 -1
- package/dist/Chat/ChatComposerInput.d.ts +20 -3
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +35 -3
- package/dist/Chat/ChatContext.d.ts +33 -0
- package/dist/Chat/ChatContext.d.ts.map +1 -1
- package/dist/Chat/ChatContext.js +16 -2
- package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
- package/dist/Chat/ChatDictationButton.js +1 -1
- package/dist/Chat/ChatLayout.d.ts.map +1 -1
- package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
- package/dist/Chat/ChatLayoutScrollButton.js +4 -4
- package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
- package/dist/Chat/ChatSendButton.d.ts.map +1 -1
- package/dist/Chat/ChatSendButton.js +7 -1
- package/dist/Chat/ChatToolCalls.d.ts +5 -1
- package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.js +18 -9
- package/dist/Chat/index.d.ts +2 -1
- package/dist/Chat/index.d.ts.map +1 -1
- package/dist/Chat/index.js +1 -1
- package/dist/Chat/useChatStreamScroll.d.ts +2 -1
- package/dist/Chat/useChatStreamScroll.d.ts.map +1 -1
- package/dist/Chat/useChatStreamScroll.js +22 -8
- package/dist/Chat/useSpeechRecognition.d.ts +0 -1
- package/dist/Chat/useSpeechRecognition.d.ts.map +1 -1
- package/dist/Chat/useSpeechRecognition.js +1 -1
- package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +1 -1
- package/dist/CheckboxList/CheckboxListItem.d.ts +20 -1
- package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.js +11 -1
- package/dist/ClickableCard/ClickableCard.d.ts +9 -2
- package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
- package/dist/ClickableCard/ClickableCard.js +5 -2
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +8 -1
- package/dist/CommandPalette/CommandPalette.d.ts +1 -1
- package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPalette.js +3 -1
- package/dist/CommandPalette/CommandPaletteGroup.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteGroup.js +9 -2
- package/dist/CommandPalette/CommandPaletteInput.d.ts +7 -1
- package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteInput.js +6 -1
- package/dist/DateInput/DateInput.d.ts +47 -1
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +26 -2
- package/dist/DateInput/index.d.ts +1 -1
- package/dist/DateInput/index.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +10 -1
- package/dist/Dialog/Dialog.d.ts +3 -0
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +57 -6
- package/dist/Dialog/DialogContext.d.ts +7 -0
- package/dist/Dialog/DialogContext.d.ts.map +1 -1
- package/dist/Dialog/DialogContext.js +2 -2
- package/dist/Dialog/DialogHeader.d.ts +5 -2
- package/dist/Dialog/DialogHeader.d.ts.map +1 -1
- package/dist/Dialog/DialogHeader.js +6 -1
- package/dist/Divider/Divider.js +1 -1
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +1 -2
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +1 -2
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +1 -2
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioGroup.js +1 -2
- package/dist/Field/Field.d.ts.map +1 -1
- package/dist/Field/Field.js +2 -3
- package/dist/Field/FieldLabel.d.ts +1 -1
- package/dist/Field/FieldLabel.d.ts.map +1 -1
- package/dist/Field/FieldLabel.js +42 -15
- package/dist/FieldStatus/FieldStatus.d.ts +8 -0
- package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
- package/dist/FieldStatus/FieldStatus.js +22 -4
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +10 -2
- package/dist/HoverCard/HoverCard.d.ts +9 -1
- package/dist/HoverCard/HoverCard.d.ts.map +1 -1
- package/dist/HoverCard/HoverCard.js +2 -0
- package/dist/HoverCard/useHoverCard.d.ts +8 -0
- package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +7 -2
- package/dist/Icon/Icon.d.ts +7 -2
- package/dist/Icon/Icon.d.ts.map +1 -1
- package/dist/Item/Item.d.ts +6 -1
- package/dist/Item/Item.d.ts.map +1 -1
- package/dist/Item/Item.js +22 -1
- package/dist/Layer/useLayer.d.ts +6 -0
- package/dist/Layer/useLayer.d.ts.map +1 -1
- package/dist/Layer/useLayer.js +2 -0
- package/dist/MobileNav/MobileNav.d.ts.map +1 -1
- package/dist/MobileNav/MobileNav.js +6 -1
- package/dist/MobileNav/MobileNavToggle.d.ts.map +1 -1
- package/dist/MobileNav/MobileNavToggle.js +4 -0
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +39 -12
- package/dist/MultiSelector/hooks.d.ts.map +1 -1
- package/dist/MultiSelector/hooks.js +16 -0
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/Outline/Outline.d.ts +0 -1
- package/dist/Outline/Outline.d.ts.map +1 -1
- package/dist/Outline/Outline.js +0 -1
- package/dist/OverflowList/OverflowList.d.ts +20 -3
- package/dist/OverflowList/OverflowList.d.ts.map +1 -1
- package/dist/OverflowList/OverflowList.js +29 -4
- package/dist/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +3 -2
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +3 -9
- package/dist/PowerSearch/PowerSearchEditPopover.js +1 -1
- package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
- package/dist/SelectableCard/SelectableCard.d.ts +9 -2
- package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
- package/dist/SelectableCard/SelectableCard.js +16 -13
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +4 -2
- package/dist/Selector/hooks.d.ts.map +1 -1
- package/dist/Selector/hooks.js +16 -0
- package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
- package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
- package/dist/SideNav/SideNavHeading.js +21 -8
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +19 -2
- package/dist/Stack/stack.stylex.d.ts +9 -9
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +3 -2
- package/dist/Table/index.d.ts +2 -0
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +1 -0
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
- package/dist/Table/plugins/rowStatus/index.d.ts +3 -0
- package/dist/Table/plugins/rowStatus/index.d.ts.map +1 -0
- package/dist/Table/plugins/rowStatus/index.js +3 -0
- package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts +60 -0
- package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -0
- package/dist/Table/plugins/rowStatus/useTableRowStatus.js +145 -0
- package/dist/Table/plugins/selection/useTableSelection.d.ts +13 -0
- package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
- package/dist/Table/plugins/selection/useTableSelection.js +4 -1
- package/dist/Table/plugins/tree/useTableTreeData.d.ts +16 -0
- package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
- package/dist/Table/plugins/tree/useTableTreeData.js +87 -0
- package/dist/Table/plugins/tree/useTableTreeState.d.ts +6 -0
- package/dist/Table/plugins/tree/useTableTreeState.d.ts.map +1 -1
- package/dist/Table/plugins/tree/useTableTreeState.js +22 -1
- package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
- package/dist/Table/useBaseTablePlugins.js +5 -3
- package/dist/Thumbnail/Thumbnail.d.ts +26 -5
- package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
- package/dist/Thumbnail/Thumbnail.js +97 -54
- package/dist/Thumbnail/thumbnail.markers.stylex.d.ts +9 -0
- package/dist/Thumbnail/thumbnail.markers.stylex.d.ts.map +1 -0
- package/dist/Thumbnail/thumbnail.markers.stylex.js +14 -0
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +15 -4
- package/dist/Timestamp/Timestamp.d.ts +3 -1
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +8 -6
- package/dist/Toast/useToast.d.ts.map +1 -1
- package/dist/Toast/useToast.js +2 -5
- package/dist/ToggleButton/ToggleButtonGroup.d.ts +0 -1
- package/dist/ToggleButton/ToggleButtonGroup.d.ts.map +1 -1
- package/dist/ToggleButton/ToggleButtonGroup.js +1 -1
- package/dist/Tokenizer/Tokenizer.d.ts +1 -1
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +16 -4
- package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
- package/dist/TopNav/TopNavHeading.js +18 -8
- package/dist/TopNav/TopNavMegaMenu.js +10 -4
- package/dist/TopNav/TopNavMegaMenuFeaturedCard.d.ts +9 -1
- package/dist/TopNav/TopNavMegaMenuFeaturedCard.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenuFeaturedCard.js +8 -1
- package/dist/TopNav/TopNavMenu.d.ts +2 -1
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +68 -6
- package/dist/TreeList/TreeList.d.ts +12 -3
- package/dist/TreeList/TreeList.d.ts.map +1 -1
- package/dist/TreeList/TreeList.js +2 -0
- package/dist/TreeList/TreeListBranches.d.ts +5 -0
- package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
- package/dist/TreeList/TreeListBranches.js +10 -4
- package/dist/TreeList/TreeListItem.d.ts +8 -2
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +27 -7
- package/dist/TreeList/TreeListTypes.d.ts +25 -1
- package/dist/TreeList/TreeListTypes.d.ts.map +1 -1
- package/dist/TreeList/index.d.ts +2 -2
- package/dist/TreeList/index.d.ts.map +1 -1
- package/dist/astryx.css +28 -18
- package/dist/astryx.umd.js +51 -51
- package/dist/astryx.umd.js.map +4 -4
- package/dist/authoring/doc.d.ts.map +1 -1
- package/dist/docs-types.d.ts +8 -0
- package/dist/docs-types.d.ts.map +1 -1
- package/dist/hooks/computeOverflow.d.ts +47 -0
- package/dist/hooks/computeOverflow.d.ts.map +1 -0
- package/dist/hooks/computeOverflow.js +213 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +2 -1
- package/dist/hooks/useDevWarning.d.ts +18 -0
- package/dist/hooks/useDevWarning.d.ts.map +1 -0
- package/dist/hooks/useDevWarning.js +49 -0
- package/dist/hooks/useOverflow.d.ts +19 -0
- package/dist/hooks/useOverflow.d.ts.map +1 -1
- package/dist/hooks/useOverflow.js +36 -25
- package/dist/i18n/resolve.d.ts.map +1 -1
- package/dist/i18n/resolve.js +7 -18
- package/dist/theme/Theme.d.ts.map +1 -1
- package/dist/theme/Theme.js +2 -7
- package/dist/theme/generateThemeRules.d.ts.map +1 -1
- package/dist/theme/generateThemeRules.js +51 -0
- package/dist/theme/hct.d.ts +0 -1
- package/dist/theme/hct.d.ts.map +1 -1
- package/dist/theme/hct.js +1 -1
- package/dist/theme/syntax/defineSyntaxTheme.d.ts.map +1 -1
- package/dist/theme/syntax/defineSyntaxTheme.js +2 -1
- package/dist/utils/devWarning.d.ts +42 -0
- package/dist/utils/devWarning.d.ts.map +1 -0
- package/dist/utils/devWarning.js +88 -0
- package/dist/utils/index.d.ts +3 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -2
- package/dist/utils/plainDate.d.ts +30 -0
- package/dist/utils/plainDate.d.ts.map +1 -1
- package/dist/utils/plainDate.js +53 -0
- package/dist/utils/types.d.ts +13 -0
- package/dist/utils/types.d.ts.map +1 -1
- package/locales/en.json +24 -0
- package/locales/fr-FR.json +14 -0
- package/locales/pseudo.json +18 -0
- package/package.json +1 -1
- package/src/AppShell/AppShell.tsx +6 -0
- package/src/AppShell/AppShellMobileContext.tsx +8 -0
- package/src/AppShell/useAppShellMobile.doc.mjs +8 -0
- package/src/Avatar/Avatar.doc.mjs +15 -3
- package/src/Avatar/Avatar.test.tsx +156 -1
- package/src/Avatar/Avatar.tsx +96 -4
- package/src/Avatar/AvatarStatusDot.doc.mjs +23 -12
- package/src/Avatar/AvatarStatusDot.test.tsx +284 -0
- package/src/Avatar/AvatarStatusDot.tsx +178 -21
- package/src/Banner/Banner.doc.mjs +9 -0
- package/src/Banner/Banner.test.tsx +30 -0
- package/src/Banner/Banner.tsx +47 -3
- package/src/Breadcrumbs/BreadcrumbItem.doc.mjs +26 -0
- package/src/Breadcrumbs/BreadcrumbItem.tsx +333 -14
- package/src/Breadcrumbs/Breadcrumbs.doc.mjs +2 -0
- package/src/Breadcrumbs/Breadcrumbs.test.tsx +284 -2
- package/src/Breadcrumbs/index.ts +27 -0
- package/src/Button/Button.doc.mjs +9 -0
- package/src/Button/Button.test.tsx +49 -0
- package/src/Button/Button.tsx +25 -1
- package/src/ButtonGroup/ButtonGroup.doc.mjs +3 -0
- package/src/ButtonGroup/ButtonGroup.test.tsx +37 -0
- package/src/ButtonGroup/ButtonGroup.tsx +32 -0
- package/src/Calendar/Calendar.doc.mjs +12 -1
- package/src/Calendar/Calendar.test.tsx +193 -0
- package/src/Calendar/Calendar.tsx +38 -16
- package/src/Calendar/dayCellUtils.test.ts +5 -0
- package/src/Calendar/dayCellUtils.ts +1 -1
- package/src/Card/Card.doc.mjs +9 -0
- package/src/Card/Card.test.tsx +21 -3
- package/src/Card/Card.tsx +35 -2
- package/src/Carousel/Carousel.doc.mjs +1 -0
- package/src/Carousel/Carousel.test.tsx +53 -0
- package/src/Carousel/Carousel.tsx +34 -4
- package/src/Chat/ChatComposer.doc.mjs +12 -3
- package/src/Chat/ChatComposer.test.tsx +115 -0
- package/src/Chat/ChatComposer.tsx +73 -9
- package/src/Chat/ChatComposerDrawer.test.tsx +61 -0
- package/src/Chat/ChatComposerDrawer.tsx +11 -1
- package/src/Chat/ChatComposerInput.doc.mjs +7 -0
- package/src/Chat/ChatComposerInput.test.tsx +103 -0
- package/src/Chat/ChatComposerInput.tsx +49 -3
- package/src/Chat/ChatContext.tsx +37 -10
- package/src/Chat/ChatDictationButton.tsx +4 -1
- package/src/Chat/ChatLayout.tsx +3 -1
- package/src/Chat/ChatLayoutScrollButton.tsx +7 -2
- package/src/Chat/ChatMessageMetadata.tsx +1 -2
- package/src/Chat/ChatSendButton.tsx +11 -1
- package/src/Chat/ChatToolCalls.test.tsx +64 -0
- package/src/Chat/ChatToolCalls.tsx +25 -4
- package/src/Chat/index.ts +5 -1
- package/src/Chat/useChatStreamScroll.test.tsx +49 -0
- package/src/Chat/useChatStreamScroll.ts +27 -10
- package/src/Chat/useSpeechRecognition.ts +1 -1
- package/src/Chat/useTriggerMenu.tsx +3 -1
- package/src/CheckboxInput/CheckboxInput.test.tsx +32 -1
- package/src/CheckboxInput/CheckboxInput.tsx +1 -1
- package/src/CheckboxList/CheckboxList.test.tsx +81 -3
- package/src/CheckboxList/CheckboxListItem.doc.mjs +33 -5
- package/src/CheckboxList/CheckboxListItem.tsx +41 -5
- package/src/ClickableCard/ClickableCard.doc.mjs +2 -0
- package/src/ClickableCard/ClickableCard.test.tsx +34 -0
- package/src/ClickableCard/ClickableCard.tsx +13 -3
- package/src/CodeBlock/CodeBlock.test.tsx +56 -0
- package/src/CodeBlock/CodeBlock.tsx +9 -1
- package/src/CommandPalette/CommandPalette.doc.mjs +1 -0
- package/src/CommandPalette/CommandPalette.tsx +3 -1
- package/src/CommandPalette/CommandPaletteGroup.test.tsx +71 -0
- package/src/CommandPalette/CommandPaletteGroup.tsx +12 -1
- package/src/CommandPalette/CommandPaletteInput.doc.mjs +26 -9
- package/src/CommandPalette/CommandPaletteInput.test.tsx +34 -0
- package/src/CommandPalette/CommandPaletteInput.tsx +11 -0
- package/src/ContextMenu/ContextMenu.test.tsx +193 -0
- package/src/DateInput/DateInput.doc.mjs +16 -0
- package/src/DateInput/DateInput.test.tsx +108 -0
- package/src/DateInput/DateInput.tsx +68 -3
- package/src/DateInput/index.ts +1 -0
- package/src/DateTimeInput/DateTimeInput.test.tsx +35 -0
- package/src/DateTimeInput/DateTimeInput.tsx +14 -2
- package/src/Dialog/Dialog.doc.mjs +2 -2
- package/src/Dialog/Dialog.test.tsx +77 -4
- package/src/Dialog/Dialog.tsx +89 -5
- package/src/Dialog/DialogContext.ts +9 -2
- package/src/Dialog/DialogHeader.doc.mjs +2 -2
- package/src/Dialog/DialogHeader.tsx +9 -2
- package/src/Divider/Divider.tsx +2 -2
- package/src/DropdownMenu/DropdownMenu.test.tsx +169 -0
- package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +1 -2
- package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +1 -2
- package/src/Field/Field.test.tsx +57 -12
- package/src/Field/Field.tsx +6 -5
- package/src/Field/FieldLabel.tsx +8 -0
- package/src/FieldStatus/FieldStatus.doc.mjs +17 -5
- package/src/FieldStatus/FieldStatus.test.tsx +78 -40
- package/src/FieldStatus/FieldStatus.tsx +22 -4
- package/src/FileInput/FileInput.test.tsx +52 -3
- package/src/FileInput/FileInput.tsx +13 -2
- package/src/HoverCard/HoverCard.doc.mjs +13 -0
- package/src/HoverCard/HoverCard.test.tsx +21 -2
- package/src/HoverCard/HoverCard.tsx +11 -0
- package/src/HoverCard/useHoverCard.doc.mjs +2 -0
- package/src/HoverCard/useHoverCard.tsx +24 -2
- package/src/Icon/Icon.doc.mjs +3 -2
- package/src/Icon/Icon.tsx +7 -2
- package/src/IconButton/IconButton.doc.mjs +8 -0
- package/src/IconButton/IconButton.test.tsx +15 -18
- package/src/Item/Item.test.tsx +40 -18
- package/src/Item/Item.tsx +38 -2
- package/src/Layer/useLayer.tsx +8 -0
- package/src/List/List.test.tsx +9 -8
- package/src/MobileNav/MobileNav.tsx +12 -1
- package/src/MobileNav/MobileNavToggle.test.tsx +116 -0
- package/src/MobileNav/MobileNavToggle.tsx +9 -1
- package/src/MultiSelector/MultiSelector.doc.mjs +4 -2
- package/src/MultiSelector/MultiSelector.test.tsx +172 -3
- package/src/MultiSelector/MultiSelector.tsx +55 -12
- package/src/MultiSelector/hooks.ts +17 -0
- package/src/NumberInput/NumberInput.test.tsx +11 -7
- package/src/NumberInput/NumberInput.tsx +1 -2
- package/src/Outline/Outline.tsx +0 -1
- package/src/OverflowList/OverflowList.doc.mjs +39 -6
- package/src/OverflowList/OverflowList.test.tsx +111 -0
- package/src/OverflowList/OverflowList.tsx +48 -8
- package/src/Popover/Popover.tsx +7 -4
- package/src/Popover/usePopover.tsx +9 -13
- package/src/PowerSearch/PowerSearch.doc.mjs +14 -0
- package/src/PowerSearch/PowerSearch.test.tsx +20 -0
- package/src/PowerSearch/PowerSearchEditPopover.test.tsx +67 -0
- package/src/PowerSearch/PowerSearchEditPopover.tsx +1 -1
- package/src/PowerSearch/usePowerSearchSource.ts +1 -2
- package/src/SelectableCard/SelectableCard.doc.mjs +2 -0
- package/src/SelectableCard/SelectableCard.test.tsx +46 -4
- package/src/SelectableCard/SelectableCard.tsx +29 -16
- package/src/Selector/Selector.test.tsx +69 -0
- package/src/Selector/Selector.tsx +5 -1
- package/src/Selector/hooks.ts +17 -0
- package/src/SideNav/SideNav.test.tsx +119 -3
- package/src/SideNav/SideNavCollapseButton.tsx +6 -1
- package/src/SideNav/SideNavHeading.tsx +24 -8
- package/src/SideNav/SideNavItem.tsx +5 -1
- package/src/Slider/Slider.test.tsx +75 -2
- package/src/Slider/Slider.tsx +21 -5
- package/src/Switch/Switch.test.tsx +32 -1
- package/src/Table/BaseTable.tsx +3 -5
- package/src/Table/index.ts +5 -0
- package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +6 -2
- package/src/Table/plugins/rowStatus/index.ts +7 -0
- package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +138 -0
- package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +189 -0
- package/src/Table/plugins/selection/useTableSelection.test.tsx +16 -0
- package/src/Table/plugins/selection/useTableSelection.tsx +19 -1
- package/src/Table/plugins/tree/useTableTreeData.test.tsx +135 -3
- package/src/Table/plugins/tree/useTableTreeData.tsx +128 -1
- package/src/Table/plugins/tree/useTableTreeState.test.tsx +104 -0
- package/src/Table/plugins/tree/useTableTreeState.tsx +46 -2
- package/src/Table/useBaseTablePlugins.ts +10 -6
- package/src/Table/useTableRowStatus.doc.mjs +30 -0
- package/src/Table/useTableSelection.doc.mjs +65 -14
- package/src/Table/useTableTreeData.doc.mjs +29 -0
- package/src/Table/useTableTreeState.doc.mjs +2 -2
- package/src/TextArea/TextArea.test.tsx +9 -1
- package/src/Thumbnail/Thumbnail.doc.mjs +19 -10
- package/src/Thumbnail/Thumbnail.test.tsx +98 -7
- package/src/Thumbnail/Thumbnail.tsx +141 -41
- package/src/Thumbnail/thumbnail.markers.stylex.ts +12 -0
- package/src/TimeInput/TimeInput.test.tsx +76 -1
- package/src/TimeInput/TimeInput.tsx +19 -6
- package/src/Timestamp/Timestamp.doc.mjs +4 -4
- package/src/Timestamp/Timestamp.test.tsx +32 -0
- package/src/Timestamp/Timestamp.tsx +28 -8
- package/src/Toast/useToast.tsx +7 -8
- package/src/ToggleButton/ToggleButtonGroup.tsx +1 -1
- package/src/Tokenizer/Tokenizer.doc.mjs +15 -1
- package/src/Tokenizer/Tokenizer.test.tsx +183 -2
- package/src/Tokenizer/Tokenizer.tsx +16 -4
- package/src/TopNav/TopNav.test.tsx +125 -1
- package/src/TopNav/TopNavHeading.tsx +24 -8
- package/src/TopNav/TopNavMegaMenu.test.tsx +149 -1
- package/src/TopNav/TopNavMegaMenu.tsx +10 -4
- package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +3 -3
- package/src/TopNav/TopNavMegaMenuFeaturedCard.tsx +19 -2
- package/src/TopNav/TopNavMenu.test.tsx +118 -4
- package/src/TopNav/TopNavMenu.tsx +83 -5
- package/src/TreeList/TreeList.doc.mjs +22 -0
- package/src/TreeList/TreeList.test.tsx +232 -0
- package/src/TreeList/TreeList.tsx +18 -2
- package/src/TreeList/TreeListBranches.tsx +16 -2
- package/src/TreeList/TreeListItem.tsx +51 -11
- package/src/TreeList/TreeListTypes.ts +27 -1
- package/src/TreeList/index.ts +2 -2
- package/src/authoring/doc.ts +1 -3
- package/src/docs-types.ts +5 -0
- package/src/hooks/computeOverflow.test.ts +422 -0
- package/src/hooks/computeOverflow.ts +290 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useDevWarning.test.tsx +45 -0
- package/src/hooks/useDevWarning.ts +54 -0
- package/src/hooks/useOverflow.test.ts +96 -4
- package/src/hooks/useOverflow.ts +74 -33
- package/src/i18n/InternationalizationProvider.doc.mjs +99 -2
- package/src/i18n/resolve.ts +9 -21
- package/src/theme/Theme.tsx +11 -14
- package/src/theme/generateThemeRules.test.ts +65 -0
- package/src/theme/generateThemeRules.ts +56 -0
- package/src/theme/hct.ts +1 -1
- package/src/theme/syntax/defineSyntaxTheme.ts +5 -6
- package/src/utils/devWarning.test.ts +56 -0
- package/src/utils/devWarning.ts +102 -0
- package/src/utils/index.ts +6 -0
- package/src/utils/plainDate.test.ts +22 -0
- package/src/utils/plainDate.ts +61 -0
- package/src/utils/types.ts +14 -0
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
import {describe, it, expect} from 'vitest';
|
|
4
|
+
import {computeOverflow, type ComputeOverflowInput} from './computeOverflow';
|
|
5
|
+
|
|
6
|
+
// Convenience wrapper with sensible defaults so each test states only what it
|
|
7
|
+
// exercises. All widths/gaps are plain numbers — no DOM involved.
|
|
8
|
+
function compute(overrides: Partial<ComputeOverflowInput>) {
|
|
9
|
+
return computeOverflow({
|
|
10
|
+
widths: [],
|
|
11
|
+
gap: 0,
|
|
12
|
+
availableWidth: 0,
|
|
13
|
+
indicatorWidth: 0,
|
|
14
|
+
minVisibleItems: 0,
|
|
15
|
+
collapseFrom: 'end',
|
|
16
|
+
...overrides,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('computeOverflow — single line, no limits (parity with legacy loop)', () => {
|
|
21
|
+
it('shows all items when they fit', () => {
|
|
22
|
+
// 3×50, gap 10 → 170 ≤ 200
|
|
23
|
+
expect(
|
|
24
|
+
compute({widths: [50, 50, 50], gap: 10, availableWidth: 200}),
|
|
25
|
+
).toEqual({visibleCount: 3, rows: 1});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('hides items that do not fit and reserves indicator space', () => {
|
|
29
|
+
// 4×50, gap 10, indicator 30, container 150 → 2 fit
|
|
30
|
+
expect(
|
|
31
|
+
compute({
|
|
32
|
+
widths: [50, 50, 50, 50],
|
|
33
|
+
gap: 10,
|
|
34
|
+
indicatorWidth: 30,
|
|
35
|
+
availableWidth: 150,
|
|
36
|
+
}),
|
|
37
|
+
).toEqual({visibleCount: 2, rows: 1});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('shows zero when nothing fits and floor is 0', () => {
|
|
41
|
+
expect(
|
|
42
|
+
compute({
|
|
43
|
+
widths: [100, 100, 100],
|
|
44
|
+
indicatorWidth: 30,
|
|
45
|
+
availableWidth: 50,
|
|
46
|
+
}),
|
|
47
|
+
).toEqual({visibleCount: 0, rows: 0});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('exact fit with indicator not needed shows all', () => {
|
|
51
|
+
// 3×50, gap 10 → 170 == 170
|
|
52
|
+
expect(
|
|
53
|
+
compute({
|
|
54
|
+
widths: [50, 50, 50],
|
|
55
|
+
gap: 10,
|
|
56
|
+
indicatorWidth: 30,
|
|
57
|
+
availableWidth: 170,
|
|
58
|
+
}),
|
|
59
|
+
).toEqual({visibleCount: 3, rows: 1});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('off-by-one: one pixel short drops the last item', () => {
|
|
63
|
+
expect(
|
|
64
|
+
compute({
|
|
65
|
+
widths: [50, 50, 50],
|
|
66
|
+
gap: 10,
|
|
67
|
+
indicatorWidth: 30,
|
|
68
|
+
availableWidth: 169,
|
|
69
|
+
}),
|
|
70
|
+
).toEqual({visibleCount: 2, rows: 1});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('works without an indicator', () => {
|
|
74
|
+
// 3×50, gap 10, container 130 → 2 fit
|
|
75
|
+
expect(
|
|
76
|
+
compute({widths: [50, 50, 50], gap: 10, availableWidth: 130}),
|
|
77
|
+
).toEqual({visibleCount: 2, rows: 1});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('varied widths', () => {
|
|
81
|
+
expect(
|
|
82
|
+
compute({
|
|
83
|
+
widths: [30, 80, 40, 60],
|
|
84
|
+
gap: 10,
|
|
85
|
+
indicatorWidth: 25,
|
|
86
|
+
availableWidth: 200,
|
|
87
|
+
}),
|
|
88
|
+
).toEqual({visibleCount: 2, rows: 1});
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe('computeOverflow — floor (minVisibleItems)', () => {
|
|
93
|
+
it('respects the floor even when items do not fit', () => {
|
|
94
|
+
expect(
|
|
95
|
+
compute({
|
|
96
|
+
widths: [100, 100, 100],
|
|
97
|
+
indicatorWidth: 30,
|
|
98
|
+
availableWidth: 50,
|
|
99
|
+
minVisibleItems: 2,
|
|
100
|
+
}),
|
|
101
|
+
).toEqual({visibleCount: 2, rows: 1});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('floor larger than itemCount is clamped to itemCount', () => {
|
|
105
|
+
expect(
|
|
106
|
+
compute({
|
|
107
|
+
widths: [100, 100],
|
|
108
|
+
availableWidth: 10,
|
|
109
|
+
minVisibleItems: 5,
|
|
110
|
+
}),
|
|
111
|
+
).toEqual({visibleCount: 2, rows: 1});
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('computeOverflow — cap (maxVisibleItems)', () => {
|
|
116
|
+
it('caps below fit even when everything fits', () => {
|
|
117
|
+
// all 5 fit in a wide container, cap at 2
|
|
118
|
+
expect(
|
|
119
|
+
compute({
|
|
120
|
+
widths: [40, 40, 40, 40, 40],
|
|
121
|
+
gap: 8,
|
|
122
|
+
availableWidth: 10000,
|
|
123
|
+
maxVisibleItems: 2,
|
|
124
|
+
}),
|
|
125
|
+
).toEqual({visibleCount: 2, rows: 1});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('cap of 0 shows nothing', () => {
|
|
129
|
+
expect(
|
|
130
|
+
compute({
|
|
131
|
+
widths: [40, 40, 40],
|
|
132
|
+
availableWidth: 10000,
|
|
133
|
+
maxVisibleItems: 0,
|
|
134
|
+
}),
|
|
135
|
+
).toEqual({visibleCount: 0, rows: 0});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('cap greater than itemCount is a no-op', () => {
|
|
139
|
+
expect(
|
|
140
|
+
compute({
|
|
141
|
+
widths: [40, 40, 40],
|
|
142
|
+
gap: 8,
|
|
143
|
+
availableWidth: 10000,
|
|
144
|
+
maxVisibleItems: 99,
|
|
145
|
+
}),
|
|
146
|
+
).toEqual({visibleCount: 3, rows: 1});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('cap does not raise a fit-limited count', () => {
|
|
150
|
+
// container only fits 2, cap is 4 → still 2
|
|
151
|
+
expect(
|
|
152
|
+
compute({
|
|
153
|
+
widths: [50, 50, 50, 50],
|
|
154
|
+
gap: 10,
|
|
155
|
+
indicatorWidth: 30,
|
|
156
|
+
availableWidth: 150,
|
|
157
|
+
maxVisibleItems: 4,
|
|
158
|
+
}),
|
|
159
|
+
).toEqual({visibleCount: 2, rows: 1});
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe('computeOverflow — cap + floor composition', () => {
|
|
164
|
+
it('both honored when min <= max', () => {
|
|
165
|
+
// fit would be 2; floor 1, cap 3 → 2
|
|
166
|
+
expect(
|
|
167
|
+
compute({
|
|
168
|
+
widths: [50, 50, 50, 50],
|
|
169
|
+
gap: 10,
|
|
170
|
+
indicatorWidth: 30,
|
|
171
|
+
availableWidth: 150,
|
|
172
|
+
minVisibleItems: 1,
|
|
173
|
+
maxVisibleItems: 3,
|
|
174
|
+
}),
|
|
175
|
+
).toEqual({visibleCount: 2, rows: 1});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('D1: when max < min, min wins', () => {
|
|
179
|
+
// wide container fits all 5; floor 3, cap 1 → floor wins → 3
|
|
180
|
+
expect(
|
|
181
|
+
compute({
|
|
182
|
+
widths: [40, 40, 40, 40, 40],
|
|
183
|
+
gap: 8,
|
|
184
|
+
availableWidth: 10000,
|
|
185
|
+
minVisibleItems: 3,
|
|
186
|
+
maxVisibleItems: 1,
|
|
187
|
+
}),
|
|
188
|
+
).toEqual({visibleCount: 3, rows: 1});
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe('computeOverflow — collapseFrom start vs end + cap', () => {
|
|
193
|
+
it('single-line collapseFrom start keeps the tail', () => {
|
|
194
|
+
// Items: 30, 80, 40. gap 10, indicator 25, container 100.
|
|
195
|
+
// reversed: [40, 80, 30] → only 40 fits → visibleCount 1
|
|
196
|
+
expect(
|
|
197
|
+
compute({
|
|
198
|
+
widths: [30, 80, 40],
|
|
199
|
+
gap: 10,
|
|
200
|
+
indicatorWidth: 25,
|
|
201
|
+
availableWidth: 100,
|
|
202
|
+
collapseFrom: 'start',
|
|
203
|
+
}),
|
|
204
|
+
).toEqual({visibleCount: 1, rows: 1});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('collapseFrom start + cap trims from the correct end', () => {
|
|
208
|
+
expect(
|
|
209
|
+
compute({
|
|
210
|
+
widths: [40, 40, 40, 40],
|
|
211
|
+
gap: 8,
|
|
212
|
+
availableWidth: 10000,
|
|
213
|
+
collapseFrom: 'start',
|
|
214
|
+
maxVisibleItems: 2,
|
|
215
|
+
}),
|
|
216
|
+
).toEqual({visibleCount: 2, rows: 1});
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
describe('computeOverflow — multi-row (maxRows)', () => {
|
|
221
|
+
it('maxRows 1 is equivalent to single line', () => {
|
|
222
|
+
const single = compute({
|
|
223
|
+
widths: [50, 50, 50, 50],
|
|
224
|
+
gap: 10,
|
|
225
|
+
indicatorWidth: 30,
|
|
226
|
+
availableWidth: 150,
|
|
227
|
+
});
|
|
228
|
+
const withMaxRows1 = compute({
|
|
229
|
+
widths: [50, 50, 50, 50],
|
|
230
|
+
gap: 10,
|
|
231
|
+
indicatorWidth: 30,
|
|
232
|
+
availableWidth: 150,
|
|
233
|
+
maxRows: 1,
|
|
234
|
+
});
|
|
235
|
+
expect(withMaxRows1).toEqual(single);
|
|
236
|
+
expect(withMaxRows1).toEqual({visibleCount: 2, rows: 1});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('packs items onto exactly maxRows rows when all fit', () => {
|
|
240
|
+
// 6×50, gap 10 → each row of width 170 holds 3 (50+10+50+10+50=170).
|
|
241
|
+
// availableWidth 170, maxRows 2 → all 6 fit on 2 rows.
|
|
242
|
+
expect(
|
|
243
|
+
compute({
|
|
244
|
+
widths: [50, 50, 50, 50, 50, 50],
|
|
245
|
+
gap: 10,
|
|
246
|
+
availableWidth: 170,
|
|
247
|
+
maxRows: 2,
|
|
248
|
+
}),
|
|
249
|
+
).toEqual({visibleCount: 6, rows: 2});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('collapses overflow past maxRows into the indicator (reserved on last row)', () => {
|
|
253
|
+
// 8×50, gap 10, width 170 → 3 per row. maxRows 2.
|
|
254
|
+
// Row 1: 3 items (170). Row 2 (last): reserve indicator 40 + gap 10 = 50.
|
|
255
|
+
// available for items on row 2 = 170; place 50 (ok, rowWidth 50),
|
|
256
|
+
// next 50 → 50+10+50=110 + reserve 50 = 160 ≤ 170 ok (2 items),
|
|
257
|
+
// next 50 → 110+10+50=170 + 50 = 220 > 170 stop. Row 2 holds 2.
|
|
258
|
+
// → 3 + 2 = 5 visible, 2 rows.
|
|
259
|
+
expect(
|
|
260
|
+
compute({
|
|
261
|
+
widths: [50, 50, 50, 50, 50, 50, 50, 50],
|
|
262
|
+
gap: 10,
|
|
263
|
+
indicatorWidth: 40,
|
|
264
|
+
availableWidth: 170,
|
|
265
|
+
maxRows: 2,
|
|
266
|
+
}),
|
|
267
|
+
).toEqual({visibleCount: 5, rows: 2});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('all items on a single row still reports rows: 1 under maxRows 2', () => {
|
|
271
|
+
expect(
|
|
272
|
+
compute({
|
|
273
|
+
widths: [30, 30, 30],
|
|
274
|
+
gap: 5,
|
|
275
|
+
availableWidth: 500,
|
|
276
|
+
maxRows: 2,
|
|
277
|
+
}),
|
|
278
|
+
).toEqual({visibleCount: 3, rows: 1});
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('cap wins when it is stricter than the rows allow', () => {
|
|
282
|
+
// 6 items fit on 2 rows, but cap at 4 → 4 visible.
|
|
283
|
+
expect(
|
|
284
|
+
compute({
|
|
285
|
+
widths: [50, 50, 50, 50, 50, 50],
|
|
286
|
+
gap: 10,
|
|
287
|
+
indicatorWidth: 40,
|
|
288
|
+
availableWidth: 170,
|
|
289
|
+
maxRows: 2,
|
|
290
|
+
maxVisibleItems: 4,
|
|
291
|
+
}),
|
|
292
|
+
).toEqual({visibleCount: 4, rows: 2});
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('rows win when they are stricter than the cap', () => {
|
|
296
|
+
// maxRows 2 admits 5 (see above); cap 8 is looser → rows win → 5.
|
|
297
|
+
expect(
|
|
298
|
+
compute({
|
|
299
|
+
widths: [50, 50, 50, 50, 50, 50, 50, 50],
|
|
300
|
+
gap: 10,
|
|
301
|
+
indicatorWidth: 40,
|
|
302
|
+
availableWidth: 170,
|
|
303
|
+
maxRows: 2,
|
|
304
|
+
maxVisibleItems: 8,
|
|
305
|
+
}),
|
|
306
|
+
).toEqual({visibleCount: 5, rows: 2});
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it('multi-row collapseFrom start keeps the tail items', () => {
|
|
310
|
+
// 8 items, reversed packing; same geometry as the end case → 5 visible.
|
|
311
|
+
const res = compute({
|
|
312
|
+
widths: [50, 50, 50, 50, 50, 50, 50, 50],
|
|
313
|
+
gap: 10,
|
|
314
|
+
indicatorWidth: 40,
|
|
315
|
+
availableWidth: 170,
|
|
316
|
+
maxRows: 2,
|
|
317
|
+
collapseFrom: 'start',
|
|
318
|
+
});
|
|
319
|
+
expect(res).toEqual({visibleCount: 5, rows: 2});
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('maxRows taller than content does not add phantom rows', () => {
|
|
323
|
+
// 3 items fit on one row; maxRows 5 → rows 1, all visible.
|
|
324
|
+
expect(
|
|
325
|
+
compute({
|
|
326
|
+
widths: [40, 40, 40],
|
|
327
|
+
gap: 10,
|
|
328
|
+
availableWidth: 500,
|
|
329
|
+
maxRows: 5,
|
|
330
|
+
}),
|
|
331
|
+
).toEqual({visibleCount: 3, rows: 1});
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it('floor is honored in multi-row even when nothing fits', () => {
|
|
335
|
+
// Very narrow container, floor 2.
|
|
336
|
+
expect(
|
|
337
|
+
compute({
|
|
338
|
+
widths: [500, 500, 500],
|
|
339
|
+
gap: 10,
|
|
340
|
+
indicatorWidth: 40,
|
|
341
|
+
availableWidth: 100,
|
|
342
|
+
maxRows: 2,
|
|
343
|
+
minVisibleItems: 2,
|
|
344
|
+
}),
|
|
345
|
+
).toEqual({visibleCount: 2, rows: 2});
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
describe('computeOverflow — resize behavior (shrink/grow re-clamps, never exceeds cap)', () => {
|
|
350
|
+
const base: Partial<ComputeOverflowInput> = {
|
|
351
|
+
widths: [50, 50, 50, 50, 50],
|
|
352
|
+
gap: 10,
|
|
353
|
+
indicatorWidth: 30,
|
|
354
|
+
maxVisibleItems: 3,
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
it('grows back only up to the cap', () => {
|
|
358
|
+
// Huge container would fit all 5, cap holds at 3.
|
|
359
|
+
expect(compute({...base, availableWidth: 10000})).toEqual({
|
|
360
|
+
visibleCount: 3,
|
|
361
|
+
rows: 1,
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('shrinks below the cap when space is tight', () => {
|
|
366
|
+
// Narrow container fits fewer than the cap.
|
|
367
|
+
expect(compute({...base, availableWidth: 150})).toEqual({
|
|
368
|
+
visibleCount: 2,
|
|
369
|
+
rows: 1,
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it('never exceeds the cap across a shrink→grow cycle', () => {
|
|
374
|
+
const shrunk = compute({...base, availableWidth: 120});
|
|
375
|
+
const grown = compute({...base, availableWidth: 10000});
|
|
376
|
+
expect(grown.visibleCount).toBeLessThanOrEqual(3);
|
|
377
|
+
expect(shrunk.visibleCount).toBeLessThanOrEqual(grown.visibleCount);
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
describe('computeOverflow — degenerate inputs', () => {
|
|
382
|
+
it('empty list', () => {
|
|
383
|
+
expect(compute({widths: []})).toEqual({visibleCount: 0, rows: 0});
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it('single item that fits', () => {
|
|
387
|
+
expect(compute({widths: [50], gap: 10, availableWidth: 100})).toEqual({
|
|
388
|
+
visibleCount: 1,
|
|
389
|
+
rows: 1,
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
it('single item that does not fit, no floor', () => {
|
|
394
|
+
expect(
|
|
395
|
+
compute({widths: [200], indicatorWidth: 30, availableWidth: 100}),
|
|
396
|
+
).toEqual({visibleCount: 0, rows: 0});
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it('maxRows 0 falls back to single-line semantics', () => {
|
|
400
|
+
// maxRows 0 is degenerate; treated as single line (not multi-row).
|
|
401
|
+
expect(
|
|
402
|
+
compute({
|
|
403
|
+
widths: [50, 50, 50],
|
|
404
|
+
gap: 10,
|
|
405
|
+
availableWidth: 130,
|
|
406
|
+
maxRows: 0,
|
|
407
|
+
}),
|
|
408
|
+
).toEqual({visibleCount: 2, rows: 1});
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
it('cap 0 with floor 0 and multi-row shows nothing', () => {
|
|
412
|
+
expect(
|
|
413
|
+
compute({
|
|
414
|
+
widths: [50, 50],
|
|
415
|
+
gap: 10,
|
|
416
|
+
availableWidth: 500,
|
|
417
|
+
maxRows: 2,
|
|
418
|
+
maxVisibleItems: 0,
|
|
419
|
+
}),
|
|
420
|
+
).toEqual({visibleCount: 0, rows: 0});
|
|
421
|
+
});
|
|
422
|
+
});
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file computeOverflow.ts
|
|
5
|
+
* @input Plain measurements (item widths, gap, available width, indicator width, limits)
|
|
6
|
+
* @output Pure overflow computation: how many items are visible and their row layout
|
|
7
|
+
* @position Pure helper consumed by useOverflow; contains no DOM or React code so the
|
|
8
|
+
* fit/clamp math and the multi-row packing can be unit-tested in isolation.
|
|
9
|
+
*
|
|
10
|
+
* There is a single source of truth for `visibleCount`:
|
|
11
|
+
* visibleCount = clamp(fitCount, minVisibleItems, maxVisibleItems ?? itemCount)
|
|
12
|
+
*
|
|
13
|
+
* SYNC: When modified, update:
|
|
14
|
+
* - /packages/core/src/hooks/useOverflow.ts (the only caller)
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export interface ComputeOverflowInput {
|
|
18
|
+
/** Measured widths of each item, in original DOM order. */
|
|
19
|
+
widths: number[];
|
|
20
|
+
/** Gap between items, in pixels. */
|
|
21
|
+
gap: number;
|
|
22
|
+
/** Width available to lay items out, in pixels. */
|
|
23
|
+
availableWidth: number;
|
|
24
|
+
/** Measured width of the overflow indicator (0 if none). */
|
|
25
|
+
indicatorWidth: number;
|
|
26
|
+
/** Floor — always show at least this many items. */
|
|
27
|
+
minVisibleItems: number;
|
|
28
|
+
/** Ceiling — never show more than this many items. `undefined` = no cap. */
|
|
29
|
+
maxVisibleItems?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Bounded multi-row: wrap items across up to this many rows, then collapse
|
|
32
|
+
* the rest into the overflow indicator. `undefined` (or `1`) = single line.
|
|
33
|
+
*/
|
|
34
|
+
maxRows?: number;
|
|
35
|
+
/** Which end items collapse from. */
|
|
36
|
+
collapseFrom: 'start' | 'end';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ComputeOverflowResult {
|
|
40
|
+
/** Number of items that should be rendered in the visible container. */
|
|
41
|
+
visibleCount: number;
|
|
42
|
+
/** Number of rows the visible items occupy (always 1 for the single-line path). */
|
|
43
|
+
rows: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function clamp(value: number, min: number, max: number): number {
|
|
47
|
+
return Math.max(Math.min(value, max), min);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Resolve the ceiling and floor into a single [floor, ceiling] pair.
|
|
52
|
+
*
|
|
53
|
+
* `maxVisibleItems` is clamped to `itemCount` (a cap larger than the list is a
|
|
54
|
+
* no-op). When `maxVisibleItems < minVisibleItems`, the floor wins (D1) via the
|
|
55
|
+
* final clamp — the hook is responsible for the dev-only warning.
|
|
56
|
+
*/
|
|
57
|
+
function resolveBounds(
|
|
58
|
+
itemCount: number,
|
|
59
|
+
minVisibleItems: number,
|
|
60
|
+
maxVisibleItems: number | undefined,
|
|
61
|
+
): {floor: number; ceiling: number} {
|
|
62
|
+
const floor = Math.max(0, Math.min(minVisibleItems, itemCount));
|
|
63
|
+
const rawCeiling = maxVisibleItems == null ? itemCount : maxVisibleItems;
|
|
64
|
+
const ceiling = Math.max(0, Math.min(rawCeiling, itemCount));
|
|
65
|
+
return {floor, ceiling};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Single-line greedy fit: how many items fit in one row of `availableWidth`,
|
|
70
|
+
* reserving indicator space for every non-final admitted item. Mirrors the
|
|
71
|
+
* original in-hook loop so single-line behavior is unchanged.
|
|
72
|
+
*/
|
|
73
|
+
function computeSingleLineFit(
|
|
74
|
+
orderedWidths: number[],
|
|
75
|
+
gap: number,
|
|
76
|
+
availableWidth: number,
|
|
77
|
+
indicatorWidth: number,
|
|
78
|
+
floor: number,
|
|
79
|
+
ceiling: number,
|
|
80
|
+
): number {
|
|
81
|
+
let totalWidth = 0;
|
|
82
|
+
let count = 0;
|
|
83
|
+
|
|
84
|
+
for (let i = 0; i < orderedWidths.length; i++) {
|
|
85
|
+
if (count >= ceiling) {
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const itemWidth = orderedWidths[i];
|
|
90
|
+
const gapWidth = i > 0 ? gap : 0;
|
|
91
|
+
const candidateWidth = totalWidth + itemWidth + gapWidth;
|
|
92
|
+
|
|
93
|
+
const isLastItem = i === orderedWidths.length - 1;
|
|
94
|
+
const reservedWidth = isLastItem
|
|
95
|
+
? 0
|
|
96
|
+
: indicatorWidth + (count > 0 || indicatorWidth > 0 ? gap : 0);
|
|
97
|
+
|
|
98
|
+
if (candidateWidth + reservedWidth > availableWidth && count >= floor) {
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
totalWidth = candidateWidth;
|
|
103
|
+
count++;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return count;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Pack items into rows, wrapping when the next item would exceed
|
|
111
|
+
* `availableWidth`. On the last allowed row, keep `indicatorReserve` px free so
|
|
112
|
+
* the overflow indicator fits. Stops admitting once no further item can be
|
|
113
|
+
* placed within `maxRows`.
|
|
114
|
+
*/
|
|
115
|
+
function packRows(
|
|
116
|
+
orderedWidths: number[],
|
|
117
|
+
gap: number,
|
|
118
|
+
availableWidth: number,
|
|
119
|
+
indicatorReserve: number,
|
|
120
|
+
maxRows: number,
|
|
121
|
+
): {placed: number; rows: number} {
|
|
122
|
+
let placed = 0;
|
|
123
|
+
let row = 1;
|
|
124
|
+
let rowWidth = 0;
|
|
125
|
+
|
|
126
|
+
for (let i = 0; i < orderedWidths.length; i++) {
|
|
127
|
+
const w = orderedWidths[i];
|
|
128
|
+
const isFirstInRow = rowWidth === 0;
|
|
129
|
+
const candidate = isFirstInRow ? w : rowWidth + gap + w;
|
|
130
|
+
|
|
131
|
+
const onLastRow = row === maxRows;
|
|
132
|
+
const reserve =
|
|
133
|
+
onLastRow && indicatorReserve > 0 ? indicatorReserve + gap : 0;
|
|
134
|
+
|
|
135
|
+
if (candidate + reserve <= availableWidth) {
|
|
136
|
+
rowWidth = candidate;
|
|
137
|
+
placed++;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (isFirstInRow) {
|
|
142
|
+
// A single item wider than the row occupies this row alone (it will be
|
|
143
|
+
// clipped visually). But if it can't coexist with the reserved indicator
|
|
144
|
+
// on the last row, stop here.
|
|
145
|
+
if (onLastRow && reserve > 0) {
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
rowWidth = candidate;
|
|
149
|
+
placed++;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (row >= maxRows) {
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
row++;
|
|
157
|
+
rowWidth = 0;
|
|
158
|
+
i--; // re-attempt this item as the first on the new row
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return {placed, rows: row};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Count how many rows a set of items occupies when wrapped at availableWidth. */
|
|
165
|
+
function countRows(
|
|
166
|
+
orderedWidths: number[],
|
|
167
|
+
gap: number,
|
|
168
|
+
availableWidth: number,
|
|
169
|
+
): number {
|
|
170
|
+
if (orderedWidths.length === 0) {
|
|
171
|
+
return 0;
|
|
172
|
+
}
|
|
173
|
+
let rows = 1;
|
|
174
|
+
let rowWidth = 0;
|
|
175
|
+
for (let i = 0; i < orderedWidths.length; i++) {
|
|
176
|
+
const w = orderedWidths[i];
|
|
177
|
+
const isFirstInRow = rowWidth === 0;
|
|
178
|
+
const candidate = isFirstInRow ? w : rowWidth + gap + w;
|
|
179
|
+
if (candidate <= availableWidth || isFirstInRow) {
|
|
180
|
+
rowWidth = candidate;
|
|
181
|
+
} else {
|
|
182
|
+
rows++;
|
|
183
|
+
rowWidth = w;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return rows;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Multi-row packing (Strategy A): simulate wrapping items into rows of
|
|
191
|
+
* `availableWidth`, admitting items until a new item would require a row beyond
|
|
192
|
+
* `maxRows`, reserving indicator space on the final row when items overflow.
|
|
193
|
+
* Returns the admitted count and how many rows those items occupy. Assumes
|
|
194
|
+
* uniform row height.
|
|
195
|
+
*/
|
|
196
|
+
function computeMultiRowFit(
|
|
197
|
+
orderedWidths: number[],
|
|
198
|
+
gap: number,
|
|
199
|
+
availableWidth: number,
|
|
200
|
+
indicatorWidth: number,
|
|
201
|
+
maxRows: number,
|
|
202
|
+
): {count: number; rows: number} {
|
|
203
|
+
const n = orderedWidths.length;
|
|
204
|
+
if (n === 0) {
|
|
205
|
+
return {count: 0, rows: 0};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// If everything fits within maxRows without reserving indicator space, there
|
|
209
|
+
// is no overflow and no indicator is needed.
|
|
210
|
+
const packAll = packRows(orderedWidths, gap, availableWidth, 0, maxRows);
|
|
211
|
+
if (packAll.placed === n) {
|
|
212
|
+
return {count: n, rows: packAll.rows};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Overflow → reserve indicator space on the final row and re-pack.
|
|
216
|
+
const packWithIndicator = packRows(
|
|
217
|
+
orderedWidths,
|
|
218
|
+
gap,
|
|
219
|
+
availableWidth,
|
|
220
|
+
indicatorWidth,
|
|
221
|
+
maxRows,
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
const count = packWithIndicator.placed;
|
|
225
|
+
const rows = countRows(orderedWidths.slice(0, count), gap, availableWidth);
|
|
226
|
+
|
|
227
|
+
return {count, rows: Math.max(count > 0 ? 1 : 0, rows)};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Pure overflow computation. Given plain measurements and the configured
|
|
232
|
+
* limits, return how many items should be visible and how many rows they
|
|
233
|
+
* occupy. No DOM, no React — safe to unit-test directly.
|
|
234
|
+
*/
|
|
235
|
+
export function computeOverflow(
|
|
236
|
+
input: ComputeOverflowInput,
|
|
237
|
+
): ComputeOverflowResult {
|
|
238
|
+
const {
|
|
239
|
+
widths,
|
|
240
|
+
gap,
|
|
241
|
+
availableWidth,
|
|
242
|
+
indicatorWidth,
|
|
243
|
+
minVisibleItems,
|
|
244
|
+
maxVisibleItems,
|
|
245
|
+
maxRows,
|
|
246
|
+
collapseFrom,
|
|
247
|
+
} = input;
|
|
248
|
+
|
|
249
|
+
const itemCount = widths.length;
|
|
250
|
+
if (itemCount === 0) {
|
|
251
|
+
return {visibleCount: 0, rows: 0};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const {floor, ceiling} = resolveBounds(
|
|
255
|
+
itemCount,
|
|
256
|
+
minVisibleItems,
|
|
257
|
+
maxVisibleItems,
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
const orderedWidths = collapseFrom === 'end' ? widths : [...widths].reverse();
|
|
261
|
+
|
|
262
|
+
const multiRow = maxRows != null && maxRows > 1;
|
|
263
|
+
|
|
264
|
+
if (!multiRow) {
|
|
265
|
+
const fitCount = computeSingleLineFit(
|
|
266
|
+
orderedWidths,
|
|
267
|
+
gap,
|
|
268
|
+
availableWidth,
|
|
269
|
+
indicatorWidth,
|
|
270
|
+
floor,
|
|
271
|
+
ceiling,
|
|
272
|
+
);
|
|
273
|
+
const visibleCount = clamp(fitCount, floor, ceiling);
|
|
274
|
+
return {visibleCount, rows: visibleCount > 0 ? 1 : 0};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const {count, rows} = computeMultiRowFit(
|
|
278
|
+
orderedWidths,
|
|
279
|
+
gap,
|
|
280
|
+
availableWidth,
|
|
281
|
+
indicatorWidth,
|
|
282
|
+
maxRows,
|
|
283
|
+
);
|
|
284
|
+
const visibleCount = clamp(count, floor, ceiling);
|
|
285
|
+
const resolvedRows =
|
|
286
|
+
visibleCount === count
|
|
287
|
+
? rows
|
|
288
|
+
: countRows(orderedWidths.slice(0, visibleCount), gap, availableWidth);
|
|
289
|
+
return {visibleCount, rows: visibleCount > 0 ? Math.max(1, resolvedRows) : 0};
|
|
290
|
+
}
|