@astryxdesign/core 0.1.7 → 0.1.8-canary.0c4d8d7
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 +65 -0
- package/README.md +30 -19
- 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 +10 -5
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +12 -8
- package/dist/Avatar/AvatarSizeContext.d.ts +1 -1
- package/dist/Avatar/AvatarSizeContext.js +1 -1
- package/dist/Avatar/AvatarStatusDot.d.ts +2 -2
- package/dist/Avatar/AvatarStatusDot.js +2 -2
- package/dist/AvatarGroup/AvatarGroup.d.ts +2 -2
- package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroup.js +2 -2
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.js +16 -14
- 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/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/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 +6 -2
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +55 -5
- package/dist/Chat/ChatComposer.d.ts +8 -0
- package/dist/Chat/ChatComposer.d.ts.map +1 -1
- package/dist/Chat/ChatComposer.js +31 -6
- 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 +19 -3
- package/dist/Chat/ChatLayout.d.ts.map +1 -1
- package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
- package/dist/Chat/ChatMessage.d.ts +1 -1
- package/dist/Chat/ChatMessage.d.ts.map +1 -1
- package/dist/Chat/ChatMessage.js +1 -1
- package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
- package/dist/Chat/ChatSendButton.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.d.ts +5 -1
- package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.js +12 -3
- package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
- package/dist/Citation/Citation.d.ts.map +1 -1
- package/dist/Citation/Citation.js +10 -4
- package/dist/ClickableCard/ClickableCard.d.ts +9 -2
- package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
- package/dist/ClickableCard/ClickableCard.js +3 -0
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +8 -1
- package/dist/Collapsible/Collapsible.d.ts +11 -1
- package/dist/Collapsible/Collapsible.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.js +36 -4
- 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/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +4 -3
- package/dist/ContextMenu/index.d.ts +3 -0
- package/dist/ContextMenu/index.d.ts.map +1 -1
- package/dist/ContextMenu/index.js +7 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +10 -1
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +2 -1
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +8 -4
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +88 -0
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -0
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +169 -0
- package/dist/DropdownMenu/DropdownMenuContext.d.ts +17 -1
- package/dist/DropdownMenu/DropdownMenuContext.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuContext.js +18 -0
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +68 -0
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -0
- package/dist/DropdownMenu/DropdownMenuRadioGroup.js +69 -0
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +69 -0
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -0
- package/dist/DropdownMenu/DropdownMenuRadioItem.js +178 -0
- package/dist/DropdownMenu/index.d.ts +5 -1
- package/dist/DropdownMenu/index.d.ts.map +1 -1
- package/dist/DropdownMenu/index.js +10 -1
- package/dist/DropdownMenu/menuItemRoles.d.ts +13 -0
- package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -0
- package/dist/DropdownMenu/menuItemRoles.js +15 -0
- 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 +43 -13
- package/dist/Icon/Icon.d.ts.map +1 -1
- package/dist/Icon/Icon.js +62 -27
- 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 +68 -3
- package/dist/Outline/Outline.d.ts.map +1 -1
- package/dist/Outline/Outline.js +128 -26
- package/dist/Outline/useScrollSpy.d.ts +27 -7
- package/dist/Outline/useScrollSpy.d.ts.map +1 -1
- package/dist/Outline/useScrollSpy.js +202 -70
- 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/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 +14 -11
- 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 +15 -1
- package/dist/TabList/Tab.d.ts.map +1 -1
- package/dist/TabList/Tab.js +2 -2
- package/dist/TabList/TabList.d.ts.map +1 -1
- package/dist/TabList/TabList.js +2 -0
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +1 -1
- package/dist/Table/Table.d.ts +1 -1
- package/dist/Table/Table.js +1 -1
- package/dist/Table/index.d.ts +4 -0
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +2 -0
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +24 -2
- 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 +12 -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/sortable/useTableSortable.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortable.js +18 -7
- package/dist/Table/plugins/tree/index.d.ts +5 -0
- package/dist/Table/plugins/tree/index.d.ts.map +1 -0
- package/dist/Table/plugins/tree/index.js +4 -0
- package/dist/Table/plugins/tree/useTableTreeData.d.ts +38 -0
- package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -0
- package/dist/Table/plugins/tree/useTableTreeData.js +358 -0
- package/dist/Table/plugins/tree/useTableTreeState.d.ts +51 -0
- package/dist/Table/plugins/tree/useTableTreeState.d.ts.map +1 -0
- package/dist/Table/plugins/tree/useTableTreeState.js +175 -0
- package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
- package/dist/Table/useBaseTablePlugins.js +6 -3
- package/dist/Thumbnail/Thumbnail.d.ts +8 -1
- package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
- package/dist/Thumbnail/Thumbnail.js +46 -8
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +15 -4
- 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/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/Toolbar/Toolbar.js +8 -1
- 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/TopNavMenu.d.ts +2 -1
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +68 -6
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +5 -14
- package/dist/astryx.css +51 -16
- package/dist/astryx.umd.js +56 -56
- package/dist/astryx.umd.js.map +4 -4
- package/dist/authoring/doc.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/useOverflow.d.ts +19 -0
- package/dist/hooks/useOverflow.d.ts.map +1 -1
- package/dist/hooks/useOverflow.js +37 -25
- package/dist/theme/Theme.js +1 -1
- package/dist/theme/defineTheme.d.ts +1 -1
- package/dist/theme/defineTheme.js +1 -1
- 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 +0 -3
- package/dist/utils/types.d.ts +13 -0
- package/dist/utils/types.d.ts.map +1 -1
- package/docs.mjs +32 -461
- package/locales/en.json +212 -172
- package/locales/fr-FR.json +10 -0
- package/locales/pseudo.json +30 -0
- package/package.json +3 -1
- package/scripts/postinstall.mjs +93 -0
- 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 +6 -6
- package/src/Avatar/Avatar.tsx +16 -11
- package/src/Avatar/AvatarSizeContext.ts +1 -1
- package/src/Avatar/AvatarStatusDot.tsx +2 -2
- package/src/AvatarGroup/AvatarGroup.doc.mjs +1 -1
- package/src/AvatarGroup/AvatarGroup.test.tsx +17 -3
- package/src/AvatarGroup/AvatarGroup.tsx +3 -3
- package/src/AvatarGroup/AvatarGroupOverflow.doc.mjs +4 -4
- package/src/AvatarGroup/AvatarGroupOverflow.tsx +21 -5
- 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/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/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 +149 -1
- package/src/Carousel/Carousel.tsx +62 -5
- package/src/Chat/Chat.doc.mjs +1 -1
- package/src/Chat/ChatComposer.doc.mjs +9 -0
- package/src/Chat/ChatComposer.test.tsx +35 -0
- package/src/Chat/ChatComposer.tsx +58 -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 +87 -0
- package/src/Chat/ChatComposerInput.tsx +35 -3
- package/src/Chat/ChatLayout.tsx +3 -1
- package/src/Chat/ChatLayoutScrollButton.tsx +3 -1
- package/src/Chat/ChatMessage.tsx +4 -2
- package/src/Chat/ChatMessageMetadata.tsx +1 -2
- package/src/Chat/ChatSendButton.tsx +5 -1
- package/src/Chat/ChatToolCalls.test.tsx +64 -0
- package/src/Chat/ChatToolCalls.tsx +13 -1
- package/src/Chat/useTriggerMenu.tsx +3 -1
- package/src/CheckboxInput/CheckboxInput.doc.mjs +1 -1
- package/src/CheckboxInput/CheckboxInput.test.tsx +32 -1
- package/src/CheckboxList/CheckboxList.test.tsx +9 -1
- package/src/Citation/Citation.test.tsx +51 -0
- package/src/Citation/Citation.tsx +8 -2
- package/src/ClickableCard/ClickableCard.doc.mjs +2 -0
- package/src/ClickableCard/ClickableCard.test.tsx +34 -0
- package/src/ClickableCard/ClickableCard.tsx +11 -1
- package/src/CodeBlock/CodeBlock.test.tsx +56 -0
- package/src/CodeBlock/CodeBlock.tsx +9 -1
- package/src/Collapsible/Collapsible.doc.mjs +7 -0
- package/src/Collapsible/Collapsible.test.tsx +80 -0
- package/src/Collapsible/Collapsible.tsx +58 -5
- 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 +75 -1
- package/src/ContextMenu/ContextMenu.tsx +10 -5
- package/src/ContextMenu/index.ts +15 -0
- package/src/DateTimeInput/DateTimeInput.test.tsx +35 -0
- package/src/DateTimeInput/DateTimeInput.tsx +14 -2
- package/src/Dialog/Dialog.tsx +12 -2
- package/src/DropdownMenu/DropdownMenu.doc.mjs +23 -0
- package/src/DropdownMenu/DropdownMenu.tsx +11 -6
- package/src/DropdownMenu/DropdownMenuCheckboxItem.doc.mjs +95 -0
- package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +236 -0
- package/src/DropdownMenu/DropdownMenuContext.tsx +29 -1
- package/src/DropdownMenu/DropdownMenuItem.doc.mjs +7 -1
- package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +80 -0
- package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +118 -0
- package/src/DropdownMenu/DropdownMenuRadioItem.doc.mjs +80 -0
- package/src/DropdownMenu/DropdownMenuRadioItem.tsx +232 -0
- package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +163 -0
- package/src/DropdownMenu/index.ts +23 -3
- package/src/DropdownMenu/menuItemRoles.ts +22 -0
- package/src/Field/Field.doc.mjs +1 -1
- package/src/Field/Field.test.tsx +57 -12
- package/src/Field/FieldLabel.doc.mjs +1 -1
- 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 +12 -0
- package/src/Icon/Icon.test.tsx +142 -0
- package/src/Icon/Icon.tsx +98 -30
- package/src/IconButton/IconButton.doc.mjs +8 -0
- package/src/IconButton/IconButton.test.tsx +15 -18
- package/src/Item/Item.doc.mjs +1 -1
- package/src/Layer/useLayer.tsx +8 -0
- 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 +162 -1
- package/src/MultiSelector/MultiSelector.tsx +55 -12
- package/src/MultiSelector/hooks.ts +17 -0
- package/src/NumberInput/NumberInput.doc.mjs +2 -2
- package/src/NumberInput/NumberInput.test.tsx +11 -7
- package/src/NumberInput/NumberInput.tsx +1 -2
- package/src/Outline/Outline.doc.mjs +123 -3
- package/src/Outline/Outline.test.tsx +735 -0
- package/src/Outline/Outline.tsx +183 -33
- package/src/Outline/useScrollSpy.ts +248 -72
- 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/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 +27 -14
- package/src/Selector/Selector.test.tsx +69 -0
- package/src/Selector/Selector.tsx +5 -1
- package/src/Selector/SelectorOption.doc.mjs +1 -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.doc.mjs +2 -2
- package/src/SideNav/SideNavItem.tsx +5 -1
- package/src/Slider/Slider.test.tsx +62 -2
- package/src/Slider/Slider.tsx +15 -1
- package/src/Switch/Switch.doc.mjs +1 -1
- package/src/Switch/Switch.test.tsx +32 -1
- package/src/TabList/Tab.tsx +5 -1
- package/src/TabList/TabList.doc.mjs +3 -3
- package/src/TabList/TabList.test.tsx +66 -0
- package/src/TabList/TabList.tsx +8 -0
- package/src/TabList/TabMenu.tsx +4 -1
- package/src/Table/Table.doc.mjs +2 -0
- package/src/Table/Table.tsx +1 -1
- package/src/Table/index.ts +12 -0
- package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +6 -2
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +90 -1
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +37 -5
- 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 +18 -1
- package/src/Table/plugins/sortable/useTableSortable.test.tsx +95 -9
- package/src/Table/plugins/sortable/useTableSortable.tsx +23 -7
- package/src/Table/plugins/tree/index.ts +12 -0
- package/src/Table/plugins/tree/useTableTreeData-perf.test.tsx +163 -0
- package/src/Table/plugins/tree/useTableTreeData.test.tsx +594 -0
- package/src/Table/plugins/tree/useTableTreeData.tsx +495 -0
- package/src/Table/plugins/tree/useTableTreeState.test.tsx +636 -0
- package/src/Table/plugins/tree/useTableTreeState.tsx +253 -0
- package/src/Table/useBaseTablePlugins.ts +6 -2
- package/src/Table/useTableRowStatus.doc.mjs +30 -0
- package/src/Table/useTableSelection.doc.mjs +65 -14
- package/src/Table/useTableTreeData.doc.mjs +75 -0
- package/src/Table/useTableTreeState.doc.mjs +98 -0
- package/src/TextArea/TextArea.doc.mjs +1 -1
- package/src/TextArea/TextArea.test.tsx +9 -1
- package/src/TextInput/TextInput.doc.mjs +1 -1
- package/src/Thumbnail/Thumbnail.doc.mjs +9 -0
- package/src/Thumbnail/Thumbnail.test.tsx +38 -8
- package/src/Thumbnail/Thumbnail.tsx +28 -1
- package/src/TimeInput/TimeInput.test.tsx +76 -1
- package/src/TimeInput/TimeInput.tsx +19 -6
- package/src/Tokenizer/Tokenizer.doc.mjs +1 -1
- package/src/Tokenizer/Tokenizer.test.tsx +137 -2
- package/src/Tokenizer/Tokenizer.tsx +16 -4
- package/src/Toolbar/Toolbar.tsx +10 -3
- package/src/TopNav/TopNav.test.tsx +80 -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/TopNavMenu.test.tsx +118 -4
- package/src/TopNav/TopNavMenu.tsx +83 -5
- package/src/TreeList/TreeList.test.tsx +32 -1
- package/src/TreeList/TreeListItem.tsx +19 -10
- package/src/authoring/doc.ts +1 -3
- package/src/docs-script.test.mjs +18 -13
- package/src/hooks/computeOverflow.test.ts +422 -0
- package/src/hooks/computeOverflow.ts +290 -0
- package/src/hooks/useOverflow.test.ts +96 -4
- package/src/hooks/useOverflow.ts +77 -33
- package/src/postinstall.test.mjs +71 -0
- package/src/theme/Theme.doc.mjs +4 -4
- package/src/theme/Theme.tsx +1 -1
- package/src/theme/defineTheme.ts +1 -1
- package/src/theme/generateThemeRules.test.ts +65 -0
- package/src/theme/generateThemeRules.ts +56 -0
- package/src/theme/hct.ts +0 -4
- package/src/utils/types.ts +14 -0
- package/dist/TreeList/treeListItem.markers.stylex.d.ts +0 -16
- package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +0 -1
- package/dist/TreeList/treeListItem.markers.stylex.js +0 -22
- package/src/TreeList/treeListItem.markers.stylex.ts +0 -20
|
@@ -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
|
+
}
|
|
@@ -14,12 +14,15 @@ function mockContainer(width: number): HTMLElement {
|
|
|
14
14
|
function mockMeasure(
|
|
15
15
|
itemWidths: number[],
|
|
16
16
|
indicatorWidth?: number,
|
|
17
|
+
itemHeight = 0,
|
|
17
18
|
): HTMLElement {
|
|
18
|
-
const children: {offsetWidth: number}[] =
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
const children: {offsetWidth: number; offsetHeight: number}[] =
|
|
20
|
+
itemWidths.map(w => ({
|
|
21
|
+
offsetWidth: w,
|
|
22
|
+
offsetHeight: itemHeight,
|
|
23
|
+
}));
|
|
21
24
|
if (indicatorWidth != null) {
|
|
22
|
-
children.push({offsetWidth: indicatorWidth});
|
|
25
|
+
children.push({offsetWidth: indicatorWidth, offsetHeight: itemHeight});
|
|
23
26
|
}
|
|
24
27
|
return {children} as unknown as HTMLElement;
|
|
25
28
|
}
|
|
@@ -332,6 +335,95 @@ describe('useOverflow', () => {
|
|
|
332
335
|
});
|
|
333
336
|
});
|
|
334
337
|
|
|
338
|
+
describe('useOverflow with maxVisibleItems (cap)', () => {
|
|
339
|
+
it('caps visible items even when they all fit', () => {
|
|
340
|
+
const {result} = renderHook(() =>
|
|
341
|
+
useOverflow(5, {gap: 10, maxVisibleItems: 3}),
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
act(() => {
|
|
345
|
+
result.current.measureRef(mockMeasure([50, 50, 50, 50, 50], 30));
|
|
346
|
+
result.current.containerRef(mockContainer(10000));
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
expect(result.current.visibleCount).toBe(3);
|
|
350
|
+
expect(result.current.hasOverflow).toBe(true);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it('cap does not raise a fit-limited count', () => {
|
|
354
|
+
const {result} = renderHook(() =>
|
|
355
|
+
useOverflow(4, {gap: 10, maxVisibleItems: 4}),
|
|
356
|
+
);
|
|
357
|
+
|
|
358
|
+
act(() => {
|
|
359
|
+
result.current.measureRef(mockMeasure([50, 50, 50, 50], 30));
|
|
360
|
+
result.current.containerRef(mockContainer(150));
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
expect(result.current.visibleCount).toBe(2);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it('min wins when maxVisibleItems < minVisibleItems and warns in dev', () => {
|
|
367
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
368
|
+
const {result} = renderHook(() =>
|
|
369
|
+
useOverflow(5, {gap: 10, minVisibleItems: 3, maxVisibleItems: 1}),
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
act(() => {
|
|
373
|
+
result.current.measureRef(mockMeasure([50, 50, 50, 50, 50], 30));
|
|
374
|
+
result.current.containerRef(mockContainer(10000));
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
expect(result.current.visibleCount).toBe(3);
|
|
378
|
+
expect(warn).toHaveBeenCalled();
|
|
379
|
+
warn.mockRestore();
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
describe('useOverflow with maxRows (multi-row)', () => {
|
|
384
|
+
it('exposes rows and rowHeight', () => {
|
|
385
|
+
const {result} = renderHook(() => useOverflow(6, {gap: 10, maxRows: 2}));
|
|
386
|
+
|
|
387
|
+
act(() => {
|
|
388
|
+
result.current.measureRef(mockMeasure([50, 50, 50, 50, 50, 50], 40, 24));
|
|
389
|
+
result.current.containerRef(mockContainer(170));
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
// 3 items per row (170), 2 rows → all 6 fit
|
|
393
|
+
expect(result.current.visibleCount).toBe(6);
|
|
394
|
+
expect(result.current.rows).toBe(2);
|
|
395
|
+
expect(result.current.rowHeight).toBe(24);
|
|
396
|
+
expect(result.current.hasOverflow).toBe(false);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it('collapses items beyond maxRows into the indicator', () => {
|
|
400
|
+
const {result} = renderHook(() => useOverflow(8, {gap: 10, maxRows: 2}));
|
|
401
|
+
|
|
402
|
+
act(() => {
|
|
403
|
+
result.current.measureRef(
|
|
404
|
+
mockMeasure([50, 50, 50, 50, 50, 50, 50, 50], 40, 24),
|
|
405
|
+
);
|
|
406
|
+
result.current.containerRef(mockContainer(170));
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
expect(result.current.visibleCount).toBe(5);
|
|
410
|
+
expect(result.current.rows).toBe(2);
|
|
411
|
+
expect(result.current.hasOverflow).toBe(true);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it('maxRows=1 matches single-line behavior', () => {
|
|
415
|
+
const {result} = renderHook(() => useOverflow(4, {gap: 10, maxRows: 1}));
|
|
416
|
+
|
|
417
|
+
act(() => {
|
|
418
|
+
result.current.measureRef(mockMeasure([50, 50, 50, 50], 30, 24));
|
|
419
|
+
result.current.containerRef(mockContainer(150));
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
expect(result.current.visibleCount).toBe(2);
|
|
423
|
+
expect(result.current.rows).toBe(1);
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
|
|
335
427
|
describe('useOverflow with behavior=observeParent', () => {
|
|
336
428
|
function mockContainerWithParent(
|
|
337
429
|
parentWidth: number,
|
package/src/hooks/useOverflow.ts
CHANGED
|
@@ -10,15 +10,19 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Measures children rendered in a hidden container to determine how many fit
|
|
12
12
|
* in the available width, without flickering. Uses ResizeObserver to react
|
|
13
|
-
* to container size changes.
|
|
13
|
+
* to container size changes. Supports an optional item cap (`maxVisibleItems`)
|
|
14
|
+
* and bounded multi-row wrapping (`maxRows`); the fit/clamp/row-packing math
|
|
15
|
+
* lives in the pure `computeOverflow` helper.
|
|
14
16
|
*
|
|
15
17
|
* SYNC: When modified, update:
|
|
16
18
|
* - /packages/core/src/hooks/index.ts
|
|
19
|
+
* - /packages/core/src/hooks/computeOverflow.ts (the pure computation)
|
|
17
20
|
*/
|
|
18
21
|
|
|
19
22
|
import {useState, useCallback, useRef} from 'react';
|
|
20
23
|
import {useIsomorphicLayoutEffect} from './useIsomorphicLayoutEffect';
|
|
21
24
|
import {observeResize, unobserveResize} from '../utils/sharedResizeObserver';
|
|
25
|
+
import {computeOverflow} from './computeOverflow';
|
|
22
26
|
|
|
23
27
|
export interface UseOverflowOptions {
|
|
24
28
|
/**
|
|
@@ -33,6 +37,23 @@ export interface UseOverflowOptions {
|
|
|
33
37
|
*/
|
|
34
38
|
minVisibleItems?: number;
|
|
35
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Maximum number of items to ever show, even if they all fit. The ceiling
|
|
42
|
+
* partner to `minVisibleItems`. `undefined` means no cap. When it is less
|
|
43
|
+
* than `minVisibleItems`, the floor wins and a dev-only warning is emitted.
|
|
44
|
+
* @default undefined
|
|
45
|
+
*/
|
|
46
|
+
maxVisibleItems?: number;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Wrap items across up to this many rows before collapsing the rest into the
|
|
50
|
+
* overflow indicator. `undefined` (or `1`) keeps the single-line behavior.
|
|
51
|
+
* A number, not a boolean: unbounded wrapping is a plain flex-wrap layout,
|
|
52
|
+
* not overflow collapse. Assumes uniform row height.
|
|
53
|
+
* @default undefined
|
|
54
|
+
*/
|
|
55
|
+
maxRows?: number;
|
|
56
|
+
|
|
36
57
|
/**
|
|
37
58
|
* Which end to collapse items from.
|
|
38
59
|
* @default 'end'
|
|
@@ -61,6 +82,10 @@ export interface UseOverflowReturn {
|
|
|
61
82
|
visibleCount: number;
|
|
62
83
|
/** Whether any items are overflowing */
|
|
63
84
|
hasOverflow: boolean;
|
|
85
|
+
/** Number of rows the visible items occupy (1 for the single-line path) */
|
|
86
|
+
rows: number;
|
|
87
|
+
/** Measured max item height in pixels; used to size the multi-row container */
|
|
88
|
+
rowHeight: number;
|
|
64
89
|
}
|
|
65
90
|
|
|
66
91
|
/**
|
|
@@ -88,13 +113,29 @@ export function useOverflow(
|
|
|
88
113
|
const {
|
|
89
114
|
gap = 0,
|
|
90
115
|
minVisibleItems = 0,
|
|
116
|
+
maxVisibleItems,
|
|
117
|
+
maxRows,
|
|
91
118
|
collapseFrom = 'end',
|
|
92
119
|
behavior = 'observeSelf',
|
|
93
120
|
} = options;
|
|
94
121
|
|
|
122
|
+
if (
|
|
123
|
+
process.env.NODE_ENV !== 'production' &&
|
|
124
|
+
maxVisibleItems != null &&
|
|
125
|
+
maxVisibleItems < minVisibleItems
|
|
126
|
+
) {
|
|
127
|
+
console.warn(
|
|
128
|
+
`useOverflow: maxVisibleItems (${maxVisibleItems}) is less than ` +
|
|
129
|
+
`minVisibleItems (${minVisibleItems}); the floor wins and ` +
|
|
130
|
+
`minVisibleItems items will be shown.`,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
95
134
|
const observeParent = behavior === 'observeParent';
|
|
96
135
|
|
|
97
136
|
const [visibleCount, setVisibleCount] = useState(itemCount);
|
|
137
|
+
const [rows, setRows] = useState(1);
|
|
138
|
+
const [rowHeight, setRowHeight] = useState(0);
|
|
98
139
|
const containerElRef = useRef<HTMLElement | null>(null);
|
|
99
140
|
const measureElRef = useRef<HTMLElement | null>(null);
|
|
100
141
|
const observedElRef = useRef<HTMLElement | null>(null);
|
|
@@ -134,44 +175,45 @@ export function useOverflow(
|
|
|
134
175
|
if (children.length === 0) {
|
|
135
176
|
// eslint-disable-next-line @eslint-react/set-state-in-effect -- overflow count is derived from measured DOM widths
|
|
136
177
|
setVisibleCount(0);
|
|
178
|
+
// eslint-disable-next-line @eslint-react/set-state-in-effect -- derived from measured DOM
|
|
179
|
+
setRows(0);
|
|
137
180
|
return;
|
|
138
181
|
}
|
|
139
182
|
|
|
140
|
-
// Collect widths of all children
|
|
141
183
|
const widths = children.map(child => child.offsetWidth);
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const reservedWidth = isLastItem
|
|
158
|
-
? 0
|
|
159
|
-
: indicatorWidth + (count > 0 || indicatorWidth > 0 ? gap : 0);
|
|
160
|
-
|
|
161
|
-
if (
|
|
162
|
-
candidateWidth + reservedWidth > availableWidth &&
|
|
163
|
-
count >= minVisibleItems
|
|
164
|
-
) {
|
|
165
|
-
break;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
totalWidth = candidateWidth;
|
|
169
|
-
count++;
|
|
170
|
-
}
|
|
184
|
+
const measuredRowHeight = children.reduce(
|
|
185
|
+
(max, child) => Math.max(max, child.offsetHeight || 0),
|
|
186
|
+
0,
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
const {visibleCount: nextVisible, rows: nextRows} = computeOverflow({
|
|
190
|
+
widths,
|
|
191
|
+
gap,
|
|
192
|
+
availableWidth,
|
|
193
|
+
indicatorWidth,
|
|
194
|
+
minVisibleItems,
|
|
195
|
+
maxVisibleItems,
|
|
196
|
+
maxRows,
|
|
197
|
+
collapseFrom,
|
|
198
|
+
});
|
|
171
199
|
|
|
172
200
|
// eslint-disable-next-line @eslint-react/set-state-in-effect -- overflow count is derived from measured DOM widths
|
|
173
|
-
setVisibleCount(
|
|
174
|
-
|
|
201
|
+
setVisibleCount(nextVisible);
|
|
202
|
+
// eslint-disable-next-line @eslint-react/set-state-in-effect -- derived from measured DOM
|
|
203
|
+
setRows(prev => (prev === nextRows ? prev : nextRows));
|
|
204
|
+
// eslint-disable-next-line @eslint-react/set-state-in-effect -- derived from measured DOM
|
|
205
|
+
setRowHeight(prev =>
|
|
206
|
+
prev === measuredRowHeight ? prev : measuredRowHeight,
|
|
207
|
+
);
|
|
208
|
+
}, [
|
|
209
|
+
itemCount,
|
|
210
|
+
gap,
|
|
211
|
+
minVisibleItems,
|
|
212
|
+
maxVisibleItems,
|
|
213
|
+
maxRows,
|
|
214
|
+
collapseFrom,
|
|
215
|
+
observeParent,
|
|
216
|
+
]);
|
|
175
217
|
|
|
176
218
|
const containerRef = useCallback(
|
|
177
219
|
(el: HTMLElement | null) => {
|
|
@@ -217,5 +259,7 @@ export function useOverflow(
|
|
|
217
259
|
measureRef,
|
|
218
260
|
visibleCount,
|
|
219
261
|
hasOverflow,
|
|
262
|
+
rows,
|
|
263
|
+
rowHeight,
|
|
220
264
|
};
|
|
221
265
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Guardrail tests for the @astryxdesign/core postinstall nudge (layer 1).
|
|
5
|
+
*
|
|
6
|
+
* The marker check mirrors the CLI's single source of truth (core can't import
|
|
7
|
+
* the CLI). Tests that it detects the marker across EVERY agent-doc location
|
|
8
|
+
* (incl. Hermes) + legacy markers, and that the nudge decision matrix is correct.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {describe, it, expect, beforeEach, afterEach} from 'vitest';
|
|
12
|
+
import * as fs from 'node:fs';
|
|
13
|
+
import * as os from 'node:os';
|
|
14
|
+
import * as path from 'node:path';
|
|
15
|
+
import {isAstryxInitialized, shouldNudge} from '../scripts/postinstall.mjs';
|
|
16
|
+
|
|
17
|
+
const MARKER = '<!-- ASTRYX:START -->';
|
|
18
|
+
|
|
19
|
+
let tmp;
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-core-postinstall-'));
|
|
22
|
+
});
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
fs.rmSync(tmp, {recursive: true, force: true});
|
|
25
|
+
});
|
|
26
|
+
function write(rel, body) {
|
|
27
|
+
const p = path.join(tmp, rel);
|
|
28
|
+
fs.mkdirSync(path.dirname(p), {recursive: true});
|
|
29
|
+
fs.writeFileSync(p, body);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe('core postinstall — isAstryxInitialized (mirrors CLI contract)', () => {
|
|
33
|
+
it('is false in an empty project', () => {
|
|
34
|
+
expect(isAstryxInitialized(tmp)).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it.each(['AGENTS.md', 'CLAUDE.md', '.claude/CLAUDE.md', '.cursorrules', '.hermes.md', 'HERMES.md'])(
|
|
38
|
+
'detects the marker in %s',
|
|
39
|
+
file => {
|
|
40
|
+
write(file, `# doc\n${MARKER}\nbody`);
|
|
41
|
+
expect(isAstryxInitialized(tmp)).toBe(true);
|
|
42
|
+
},
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
it('detects the legacy XDS marker', () => {
|
|
46
|
+
write('AGENTS.md', '<!-- XDS:START -->');
|
|
47
|
+
expect(isAstryxInitialized(tmp)).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('core postinstall — shouldNudge', () => {
|
|
52
|
+
const DEP = '/proj/node_modules/@astryxdesign/core/scripts/postinstall.mjs';
|
|
53
|
+
const NPX = '/Users/x/.npm/_npx/a1/node_modules/@astryxdesign/core/scripts/postinstall.mjs';
|
|
54
|
+
const REPO = '/repo/packages/core/scripts/postinstall.mjs';
|
|
55
|
+
|
|
56
|
+
it('nudges for a real dependency install when not set up', () => {
|
|
57
|
+
expect(shouldNudge({scriptPath: DEP, npmCommand: 'install', isSetUp: false})).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
it('quiet in the monorepo/source', () => {
|
|
60
|
+
expect(shouldNudge({scriptPath: REPO, npmCommand: 'install', isSetUp: false})).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
it('quiet during npx transient install (_npx path)', () => {
|
|
63
|
+
expect(shouldNudge({scriptPath: NPX, npmCommand: 'install', isSetUp: false})).toBe(false);
|
|
64
|
+
});
|
|
65
|
+
it('quiet during npx (npm_command=exec)', () => {
|
|
66
|
+
expect(shouldNudge({scriptPath: DEP, npmCommand: 'exec', isSetUp: false})).toBe(false);
|
|
67
|
+
});
|
|
68
|
+
it('quiet once set up', () => {
|
|
69
|
+
expect(shouldNudge({scriptPath: DEP, npmCommand: 'install', isSetUp: true})).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
});
|
package/src/theme/Theme.doc.mjs
CHANGED
|
@@ -39,12 +39,12 @@ export const docs = {
|
|
|
39
39
|
},
|
|
40
40
|
usage: {
|
|
41
41
|
description:
|
|
42
|
-
'Wraps a subtree with a specific Astryx theme. For static production themes, use `
|
|
42
|
+
'Wraps a subtree with a specific Astryx theme. For static production themes, use `astryx theme build` and import the generated CSS plus built theme object for first-paint and SSR performance. Use runtime `defineTheme()` when themes are dynamic or for prototyping.\n\n`defineTheme` accepts a `tokens` object whose keys are CSS custom property names (always prefixed with `--`). Common token names include `--color-accent`, `--color-background-surface`, `--color-background-body`, `--color-text-primary`, `--color-text-secondary`, `--radius-container`, `--spacing-1` through `--spacing-6`. Values can be a string (same for light/dark) or a `[light, dark]` tuple.\n\nExample:\n```ts\nimport {defineTheme} from \'@astryxdesign/core/theme\';\nconst myTheme = defineTheme({\n name: \'ocean\',\n tokens: {\n \'--color-accent\': [\'#0077B6\', \'#48CAE4\'],\n \'--color-background-surface\': [\'#F0F8FF\', \'#0A1628\'],\n \'--color-text-primary\': [\'#0A1317\', \'#FFFFFF\'],\n \'--radius-container\': \'16px\',\n },\n});\n```',
|
|
43
43
|
bestPractices: [
|
|
44
44
|
{
|
|
45
45
|
guidance: true,
|
|
46
46
|
description:
|
|
47
|
-
'Build app themes that are known ahead of time with `
|
|
47
|
+
'Build app themes that are known ahead of time with `astryx theme build`, then import the generated CSS and built theme object.',
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
guidance: true,
|
|
@@ -90,12 +90,12 @@ export const docs = {
|
|
|
90
90
|
export const docsDense = {
|
|
91
91
|
usage: {
|
|
92
92
|
description:
|
|
93
|
-
'Wraps subtree w/ specific Astryx theme. For static production themes, use `
|
|
93
|
+
'Wraps subtree w/ specific Astryx theme. For static production themes, use `astryx theme build` + generated CSS + built theme object for first-paint/SSR performance. Use runtime `defineTheme()` for dynamic themes or prototyping. Token names always start with `--` (e.g. `--color-accent`, `--color-background-surface`).',
|
|
94
94
|
bestPractices: [
|
|
95
95
|
{
|
|
96
96
|
guidance: true,
|
|
97
97
|
description:
|
|
98
|
-
'Build app themes known ahead of time w/ `
|
|
98
|
+
'Build app themes known ahead of time w/ `astryx theme build`; import generated CSS + built theme object.',
|
|
99
99
|
},
|
|
100
100
|
{
|
|
101
101
|
guidance: true,
|
package/src/theme/Theme.tsx
CHANGED
|
@@ -127,7 +127,7 @@ function useThemeStyleInjection(theme: DefinedTheme): void {
|
|
|
127
127
|
`For better performance, use the pre-built theme:\n\n` +
|
|
128
128
|
` import {${theme.name}Theme} from '@astryxdesign/theme-${theme.name}/built';\n` +
|
|
129
129
|
` import '@astryxdesign/theme-${theme.name}/theme.css';\n\n` +
|
|
130
|
-
`For custom themes, run \`npx
|
|
130
|
+
`For custom themes, run \`npx @astryxdesign/cli theme build <file>\` to generate ` +
|
|
131
131
|
`the built artifacts.`,
|
|
132
132
|
);
|
|
133
133
|
}
|