@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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
|
|
3
3
|
import {describe, it, expect, vi} from 'vitest';
|
|
4
|
-
import {render, screen} from '@testing-library/react';
|
|
4
|
+
import {render, screen, fireEvent} from '@testing-library/react';
|
|
5
5
|
import {Carousel} from './Carousel';
|
|
6
6
|
|
|
7
7
|
// Mock ResizeObserver (not available in jsdom)
|
|
@@ -92,6 +92,59 @@ describe('Carousel', () => {
|
|
|
92
92
|
expect(screen.getByLabelText('Scroll right')).toBeInTheDocument();
|
|
93
93
|
});
|
|
94
94
|
|
|
95
|
+
describe('slide semantics', () => {
|
|
96
|
+
it('exposes each slide as a group with aria-roledescription="slide" and a positional name', () => {
|
|
97
|
+
render(
|
|
98
|
+
<Carousel aria-label="Photos">
|
|
99
|
+
<div>One</div>
|
|
100
|
+
<div>Two</div>
|
|
101
|
+
<div>Three</div>
|
|
102
|
+
</Carousel>,
|
|
103
|
+
);
|
|
104
|
+
// APG carousel pattern: each slide container is role=group with
|
|
105
|
+
// aria-roledescription="slide" and an "N of M" accessible name.
|
|
106
|
+
const slides = screen.getAllByRole('group');
|
|
107
|
+
expect(slides).toHaveLength(3);
|
|
108
|
+
slides.forEach((slide, i) => {
|
|
109
|
+
expect(slide).toHaveAttribute('aria-roledescription', 'slide');
|
|
110
|
+
expect(slide).toHaveAccessibleName(`Slide ${i + 1} of 3`);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('reflects the rendered child count, skipping null and boolean children', () => {
|
|
115
|
+
render(
|
|
116
|
+
<Carousel aria-label="Photos">
|
|
117
|
+
<div>One</div>
|
|
118
|
+
{null}
|
|
119
|
+
{false}
|
|
120
|
+
<div>Two</div>
|
|
121
|
+
</Carousel>,
|
|
122
|
+
);
|
|
123
|
+
const slides = screen.getAllByRole('group');
|
|
124
|
+
expect(slides).toHaveLength(2);
|
|
125
|
+
expect(
|
|
126
|
+
screen.getByRole('group', {name: 'Slide 1 of 2'}),
|
|
127
|
+
).toBeInTheDocument();
|
|
128
|
+
expect(
|
|
129
|
+
screen.getByRole('group', {name: 'Slide 2 of 2'}),
|
|
130
|
+
).toBeInTheDocument();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('keeps the container region semantics unchanged around labelled slides', () => {
|
|
134
|
+
render(
|
|
135
|
+
<Carousel aria-label="Gallery">
|
|
136
|
+
<div>One</div>
|
|
137
|
+
<div>Two</div>
|
|
138
|
+
</Carousel>,
|
|
139
|
+
);
|
|
140
|
+
const region = screen.getByRole('region', {name: 'Gallery'});
|
|
141
|
+
expect(region).toHaveAttribute('aria-roledescription', 'carousel');
|
|
142
|
+
const slides = screen.getAllByRole('group');
|
|
143
|
+
expect(slides).toHaveLength(2);
|
|
144
|
+
slides.forEach(slide => expect(region).toContainElement(slide));
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
95
148
|
it('disables edge scroll buttons instead of removing them from the tab order', () => {
|
|
96
149
|
// In jsdom there is no measurable overflow, so both edges are at rest and
|
|
97
150
|
// the scroll buttons are in their hidden/inert state. They must stay
|
|
@@ -111,4 +164,99 @@ describe('Carousel', () => {
|
|
|
111
164
|
expect(left).toBeDisabled();
|
|
112
165
|
expect(right).toBeDisabled();
|
|
113
166
|
});
|
|
167
|
+
|
|
168
|
+
describe('Shift + wheel horizontal scroll', () => {
|
|
169
|
+
// jsdom doesn't lay out elements, so we fake an overflowing scroll
|
|
170
|
+
// container and capture scrollBy calls.
|
|
171
|
+
function getScroller() {
|
|
172
|
+
const region = screen.getByRole('region');
|
|
173
|
+
return region.firstElementChild as HTMLElement;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function makeOverflowing(el: HTMLElement) {
|
|
177
|
+
Object.defineProperty(el, 'scrollWidth', {
|
|
178
|
+
value: 500,
|
|
179
|
+
configurable: true,
|
|
180
|
+
});
|
|
181
|
+
Object.defineProperty(el, 'clientWidth', {
|
|
182
|
+
value: 200,
|
|
183
|
+
configurable: true,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
it('maps Shift + vertical wheel to horizontal scroll', () => {
|
|
188
|
+
render(
|
|
189
|
+
<Carousel aria-label="Wheel">
|
|
190
|
+
<div>Item 1</div>
|
|
191
|
+
<div>Item 2</div>
|
|
192
|
+
</Carousel>,
|
|
193
|
+
);
|
|
194
|
+
const scroller = getScroller();
|
|
195
|
+
makeOverflowing(scroller);
|
|
196
|
+
const scrollBy = vi.fn();
|
|
197
|
+
scroller.scrollBy = scrollBy;
|
|
198
|
+
|
|
199
|
+
fireEvent.wheel(scroller, {shiftKey: true, deltaY: 120, deltaX: 0});
|
|
200
|
+
|
|
201
|
+
expect(scrollBy).toHaveBeenCalledWith({left: 120, behavior: 'auto'});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('does not translate wheel without Shift held', () => {
|
|
205
|
+
render(
|
|
206
|
+
<Carousel aria-label="Wheel">
|
|
207
|
+
<div>Item 1</div>
|
|
208
|
+
<div>Item 2</div>
|
|
209
|
+
</Carousel>,
|
|
210
|
+
);
|
|
211
|
+
const scroller = getScroller();
|
|
212
|
+
makeOverflowing(scroller);
|
|
213
|
+
const scrollBy = vi.fn();
|
|
214
|
+
scroller.scrollBy = scrollBy;
|
|
215
|
+
|
|
216
|
+
fireEvent.wheel(scroller, {shiftKey: false, deltaY: 120, deltaX: 0});
|
|
217
|
+
|
|
218
|
+
expect(scrollBy).not.toHaveBeenCalled();
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('leaves native horizontal wheel deltas alone', () => {
|
|
222
|
+
// Trackpads emit deltaX directly — don't double-handle those.
|
|
223
|
+
render(
|
|
224
|
+
<Carousel aria-label="Wheel">
|
|
225
|
+
<div>Item 1</div>
|
|
226
|
+
<div>Item 2</div>
|
|
227
|
+
</Carousel>,
|
|
228
|
+
);
|
|
229
|
+
const scroller = getScroller();
|
|
230
|
+
makeOverflowing(scroller);
|
|
231
|
+
const scrollBy = vi.fn();
|
|
232
|
+
scroller.scrollBy = scrollBy;
|
|
233
|
+
|
|
234
|
+
fireEvent.wheel(scroller, {shiftKey: true, deltaY: 120, deltaX: 40});
|
|
235
|
+
|
|
236
|
+
expect(scrollBy).not.toHaveBeenCalled();
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('does not intercept when there is no horizontal overflow', () => {
|
|
240
|
+
render(
|
|
241
|
+
<Carousel aria-label="Wheel">
|
|
242
|
+
<div>Item 1</div>
|
|
243
|
+
</Carousel>,
|
|
244
|
+
);
|
|
245
|
+
const scroller = getScroller();
|
|
246
|
+
Object.defineProperty(scroller, 'scrollWidth', {
|
|
247
|
+
value: 200,
|
|
248
|
+
configurable: true,
|
|
249
|
+
});
|
|
250
|
+
Object.defineProperty(scroller, 'clientWidth', {
|
|
251
|
+
value: 200,
|
|
252
|
+
configurable: true,
|
|
253
|
+
});
|
|
254
|
+
const scrollBy = vi.fn();
|
|
255
|
+
scroller.scrollBy = scrollBy;
|
|
256
|
+
|
|
257
|
+
fireEvent.wheel(scroller, {shiftKey: true, deltaY: 120, deltaX: 0});
|
|
258
|
+
|
|
259
|
+
expect(scrollBy).not.toHaveBeenCalled();
|
|
260
|
+
});
|
|
261
|
+
});
|
|
114
262
|
});
|
|
@@ -7,8 +7,12 @@
|
|
|
7
7
|
* @input Uses React, StyleX, useScrollOverflow, useLayer, Button, Icon, theme tokens
|
|
8
8
|
* @output Exports Carousel component
|
|
9
9
|
* @position Horizontal scroll container with fade-edge overflow indication,
|
|
10
|
-
* optional prev/next buttons on the top layer, scroll-snap,
|
|
11
|
-
* visual bleed allowance for child selection indicators
|
|
10
|
+
* optional prev/next buttons on the top layer, scroll-snap, a 1px
|
|
11
|
+
* visual bleed allowance for child selection indicators, and Shift + wheel
|
|
12
|
+
* mapping so mouse users can scroll horizontally. Exposes APG
|
|
13
|
+
* carousel semantics: the root is a labelled region with
|
|
14
|
+
* aria-roledescription="carousel" and each item wrapper is a group with
|
|
15
|
+
* aria-roledescription="slide" named "Slide N of M".
|
|
12
16
|
*
|
|
13
17
|
* SYNC: When modified, update:
|
|
14
18
|
* - /packages/core/src/Carousel/index.ts (exports)
|
|
@@ -16,7 +20,14 @@
|
|
|
16
20
|
* - /packages/cli/templates/blocks/components/Carousel/ (showcase blocks)
|
|
17
21
|
*/
|
|
18
22
|
|
|
19
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
type ReactNode,
|
|
25
|
+
useRef,
|
|
26
|
+
useCallback,
|
|
27
|
+
useEffect,
|
|
28
|
+
Children,
|
|
29
|
+
isValidElement,
|
|
30
|
+
} from 'react';
|
|
20
31
|
import * as stylex from '@stylexjs/stylex';
|
|
21
32
|
import {
|
|
22
33
|
spacingVars,
|
|
@@ -269,6 +280,11 @@ export function Carousel({
|
|
|
269
280
|
const scrollElRef = useRef<HTMLElement | null>(null);
|
|
270
281
|
const {scrollRef, overflowStart, overflowEnd} = useScrollOverflow();
|
|
271
282
|
|
|
283
|
+
// Children.toArray drops null/undefined/boolean children and assigns
|
|
284
|
+
// stable keys, so slide numbering ("Slide N of M") matches what actually
|
|
285
|
+
// renders even when some children are conditionally omitted.
|
|
286
|
+
const slides = Children.toArray(children);
|
|
287
|
+
|
|
272
288
|
const layer = useLayer({
|
|
273
289
|
mode: 'context',
|
|
274
290
|
lightDismiss: false,
|
|
@@ -290,6 +306,31 @@ export function Carousel({
|
|
|
290
306
|
[scrollRef],
|
|
291
307
|
);
|
|
292
308
|
|
|
309
|
+
// Map Shift + vertical wheel to horizontal scroll. Trackpads emit
|
|
310
|
+
// horizontal deltas natively, but a standard mouse only produces deltaY —
|
|
311
|
+
// so mouse users can't wheel-scroll a horizontal container. Shift + wheel
|
|
312
|
+
// is the long-established convention for horizontal scroll containers; we
|
|
313
|
+
// honor it by translating the vertical delta into a horizontal scroll.
|
|
314
|
+
//
|
|
315
|
+
// Only kicks in when Shift is held and the wheel is purely vertical
|
|
316
|
+
// (deltaX === 0), so native trackpad horizontal scrolling is untouched.
|
|
317
|
+
const handleWheel = useCallback((event: React.WheelEvent<HTMLDivElement>) => {
|
|
318
|
+
if (!event.shiftKey || event.deltaY === 0 || event.deltaX !== 0) {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
const el = scrollElRef.current;
|
|
322
|
+
if (!el) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
// Nothing to scroll horizontally — let the event fall through so the page
|
|
326
|
+
// can scroll as it normally would.
|
|
327
|
+
if (el.scrollWidth <= el.clientWidth) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
event.preventDefault();
|
|
331
|
+
el.scrollBy({left: event.deltaY, behavior: 'auto'});
|
|
332
|
+
}, []);
|
|
333
|
+
|
|
293
334
|
const scrollBy = useCallback((direction: -1 | 1) => {
|
|
294
335
|
const el = scrollElRef.current;
|
|
295
336
|
if (!el) {
|
|
@@ -346,6 +387,7 @@ export function Carousel({
|
|
|
346
387
|
<div
|
|
347
388
|
ref={composedRef}
|
|
348
389
|
tabIndex={0}
|
|
390
|
+
onWheel={handleWheel}
|
|
349
391
|
{...stylex.props(
|
|
350
392
|
styles.scroller,
|
|
351
393
|
gapStyles[gap],
|
|
@@ -353,8 +395,23 @@ export function Carousel({
|
|
|
353
395
|
hasSnap && styles.snap,
|
|
354
396
|
fadeStyle,
|
|
355
397
|
)}>
|
|
356
|
-
{
|
|
357
|
-
|
|
398
|
+
{slides.map((child, index) => (
|
|
399
|
+
// APG carousel pattern: each slide container is a group with
|
|
400
|
+
// aria-roledescription="slide" and an "N of M" accessible name so
|
|
401
|
+
// ATs announce slide boundaries and position instead of anonymous
|
|
402
|
+
// generics.
|
|
403
|
+
<div
|
|
404
|
+
// eslint-disable-next-line @eslint-react/no-array-index-key -- index fallback only applies to text/number children, which are positional by definition; elements keep their Children.toArray keys
|
|
405
|
+
key={isValidElement(child) ? child.key : index}
|
|
406
|
+
role="group"
|
|
407
|
+
aria-roledescription="slide"
|
|
408
|
+
aria-label={t('@astryx.carousel.slideLabel', {
|
|
409
|
+
current: index + 1,
|
|
410
|
+
total: slides.length,
|
|
411
|
+
})}
|
|
412
|
+
{...stylex.props(styles.item)}>
|
|
413
|
+
{child}
|
|
414
|
+
</div>
|
|
358
415
|
))}
|
|
359
416
|
</div>
|
|
360
417
|
|
package/src/Chat/Chat.doc.mjs
CHANGED
|
@@ -80,7 +80,7 @@ export const docs = {
|
|
|
80
80
|
{ name: 'Scroll-to-bottom button', required: false, description: 'Appears when the user scrolls up or new messages arrive. Defaults to ChatLayoutScrollButton; pass null to hide or a custom element to override.' },
|
|
81
81
|
{ name: 'Composer', required: true, description: 'The input area for sending messages, typically ChatComposer. Docked at the bottom inside the frosted glass layer.' },
|
|
82
82
|
{ name: 'Empty state', required: false, description: 'Centered placeholder shown when no messages exist. Use EmptyState for a consistent look.' },
|
|
83
|
-
{ name: 'Avatar', required: false, description: 'A sender avatar rendered beside the message. Typically Avatar with size="
|
|
83
|
+
{ name: 'Avatar', required: false, description: 'A sender avatar rendered beside the message. Typically Avatar with size="md". Hidden for system messages.' },
|
|
84
84
|
{ name: 'Name', required: false, description: 'Sender name above the message body. Place on the bubble when using bubbles, or on the message wrapper for raw content.' },
|
|
85
85
|
{ name: 'Content', required: true, description: 'The message body: one or more ChatMessageBubble elements, or any free-form ReactNode like images or tool calls.' },
|
|
86
86
|
{ name: 'Metadata', required: false, description: 'Timestamp, delivery status, and footer actions below the message. Place on the last bubble or on the message wrapper.' },
|
|
@@ -56,6 +56,13 @@ export const docs = {
|
|
|
56
56
|
description: 'Visual density.',
|
|
57
57
|
default: "'balanced'",
|
|
58
58
|
},
|
|
59
|
+
{
|
|
60
|
+
name: 'elevation',
|
|
61
|
+
type: "'none' | 'low'",
|
|
62
|
+
description:
|
|
63
|
+
"Resting elevation of the composer body. `low` (the default) keeps today's raised look — low at rest, bumping to med on hover / focus. `none` flattens it and draws a border with the same rest / hover / focus treatment as a text input (emphasized border → accent on focus, matching inset rings).",
|
|
64
|
+
default: "'low'",
|
|
65
|
+
},
|
|
59
66
|
{
|
|
60
67
|
name: 'drawer',
|
|
61
68
|
type: 'ReactNode',
|
|
@@ -185,6 +192,7 @@ export const docsZh = {
|
|
|
185
192
|
placeholder: '输入为空时显示的占位文本。',
|
|
186
193
|
isDisabled: '禁用编写器。',
|
|
187
194
|
density: '视觉密度。',
|
|
195
|
+
elevation: "编写器主体的静止高度。low(默认)保持当前的抬起外观——静止时 low,悬停/聚焦时升至 med。none 则扁平化。",
|
|
188
196
|
drawer: '插槽:输入上方的可折叠抽屉——附件、上下文标签等。使用 ChatComposerDrawer。',
|
|
189
197
|
headerActions: '插槽:标题左侧操作按钮(附件、提及按钮)。使用仅图标 size="sm" 按钮。',
|
|
190
198
|
headerContext: '插槽:标题右侧上下文信息(上下文窗口使用情况、ProgressBar、辅助文本)。',
|
|
@@ -210,6 +218,7 @@ export const docsDense = {
|
|
|
210
218
|
placeholder: 'placeholder when empty',
|
|
211
219
|
isDisabled: 'disabled; use during streaming or unmet prereqs',
|
|
212
220
|
density: 'visual density',
|
|
221
|
+
elevation: "resting elevation of composer body: low (default; low→med on hover/focus) | none (flat)",
|
|
213
222
|
drawer: 'slot: collapsible drawer above input: attachments, context chips, etc.; use ChatComposerDrawer',
|
|
214
223
|
headerActions: 'slot: left header actions (attach, mention); icon-only sm buttons',
|
|
215
224
|
headerContext: 'slot: right header context info (window usage, ProgressBar, text)',
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
import {describe, it, expect} from 'vitest';
|
|
4
|
+
import {render} from '@testing-library/react';
|
|
5
|
+
import {ChatComposer} from './ChatComposer';
|
|
6
|
+
|
|
7
|
+
// The composer body — the elevated surface — is the div that wraps the input
|
|
8
|
+
// area (styles.body). A custom `input` slot renders inside the inputArea div,
|
|
9
|
+
// so the body is two ancestors up from the slot marker. Walking from a known
|
|
10
|
+
// marker keeps this independent of StyleX's generated class names.
|
|
11
|
+
function renderBodyClass(elevation?: 'none' | 'low'): string {
|
|
12
|
+
const {container} = render(
|
|
13
|
+
<ChatComposer
|
|
14
|
+
onSubmit={() => {}}
|
|
15
|
+
elevation={elevation}
|
|
16
|
+
input={<div data-testid="composer-input-marker" />}
|
|
17
|
+
/>,
|
|
18
|
+
);
|
|
19
|
+
const marker = container.querySelector(
|
|
20
|
+
'[data-testid="composer-input-marker"]',
|
|
21
|
+
)!;
|
|
22
|
+
const inputArea = marker.parentElement!;
|
|
23
|
+
const body = inputArea.parentElement!;
|
|
24
|
+
return body.className;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe('ChatComposer elevation', () => {
|
|
28
|
+
it('applies a distinct body class for each supported level', () => {
|
|
29
|
+
expect(renderBodyClass('none')).not.toBe(renderBodyClass('low'));
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("defaults to 'low' (preserves the raised look)", () => {
|
|
33
|
+
expect(renderBodyClass(undefined)).toBe(renderBodyClass('low'));
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
shadowVars,
|
|
41
41
|
durationVars,
|
|
42
42
|
easeVars,
|
|
43
|
+
borderVars,
|
|
43
44
|
typeScaleVars,
|
|
44
45
|
typographyVars,
|
|
45
46
|
} from '../theme/tokens.stylex';
|
|
@@ -83,6 +84,14 @@ export interface ChatComposerProps extends Omit<
|
|
|
83
84
|
isDisabled?: boolean;
|
|
84
85
|
/** Density variant */
|
|
85
86
|
density?: ChatComposerDensity;
|
|
87
|
+
/**
|
|
88
|
+
* Resting elevation of the composer body. `low` (the default) keeps today's
|
|
89
|
+
* raised look — low at rest, bumping to med on hover / focus. `none` flattens
|
|
90
|
+
* it and draws a border with the same rest / hover / focus treatment as a
|
|
91
|
+
* text input (emphasized border → accent on focus, with matching inset rings).
|
|
92
|
+
* @default 'low'
|
|
93
|
+
*/
|
|
94
|
+
elevation?: 'none' | 'low';
|
|
86
95
|
|
|
87
96
|
// --- Slot props ---
|
|
88
97
|
|
|
@@ -143,14 +152,7 @@ const styles = stylex.create({
|
|
|
143
152
|
borderRadius: 'var(--_chat-composer-radius)',
|
|
144
153
|
backgroundColor: colorVars['--color-background-popover'],
|
|
145
154
|
cursor: 'text',
|
|
146
|
-
|
|
147
|
-
default: shadowVars['--shadow-low'],
|
|
148
|
-
':hover': {'@media (hover: hover)': shadowVars['--shadow-med']},
|
|
149
|
-
},
|
|
150
|
-
transition: `box-shadow ${durationVars['--duration-fast']} ${easeVars['--ease-standard']}`,
|
|
151
|
-
':focus-within': {
|
|
152
|
-
boxShadow: shadowVars['--shadow-med'],
|
|
153
|
-
},
|
|
155
|
+
transition: `box-shadow ${durationVars['--duration-fast']} ${easeVars['--ease-standard']}, border-color ${durationVars['--duration-fast']} ${easeVars['--ease-standard']}`,
|
|
154
156
|
},
|
|
155
157
|
header: {
|
|
156
158
|
display: 'flex',
|
|
@@ -247,11 +249,56 @@ const styles = stylex.create({
|
|
|
247
249
|
color: colorVars['--color-text-yellow'],
|
|
248
250
|
},
|
|
249
251
|
compact: {
|
|
250
|
-
padding
|
|
252
|
+
// Override the padding var (not the `padding` property) so both the base
|
|
253
|
+
// body padding and the border-inset calc in elevationStyles.none pick up
|
|
254
|
+
// the compact value. Scoped to the body element, so the status bar (which
|
|
255
|
+
// reads the var from root) keeps its own padding.
|
|
256
|
+
'--_chat-composer-padding': spacingVars['--spacing-2'],
|
|
251
257
|
gap: spacingVars['--spacing-1'],
|
|
252
258
|
},
|
|
253
259
|
});
|
|
254
260
|
|
|
261
|
+
// Resting elevation for the composer body, narrowed to two steps.
|
|
262
|
+
// - `low` (default) preserves today's look: low at rest, bumping to med on
|
|
263
|
+
// hover / focus-within (a CSS-only interaction state the component owns).
|
|
264
|
+
// - `none` is flat; depth comes from a border + inset rings instead. The
|
|
265
|
+
// border is drawn *inside* the padding — its width is subtracted from every
|
|
266
|
+
// side (like Card) so total inset (border + padding) equals the elevated
|
|
267
|
+
// case and content geometry is unchanged. Border color, hover, and focus
|
|
268
|
+
// states mirror TextInput (shared `inputWrapperStyles.base` treatment):
|
|
269
|
+
// emphasized border at rest → accent on focus-within, with the same inset
|
|
270
|
+
// shadow rings on hover and focus.
|
|
271
|
+
const elevationStyles = stylex.create({
|
|
272
|
+
low: {
|
|
273
|
+
boxShadow: {
|
|
274
|
+
default: shadowVars['--shadow-low'],
|
|
275
|
+
':hover': {'@media (hover: hover)': shadowVars['--shadow-med']},
|
|
276
|
+
},
|
|
277
|
+
':focus-within': {
|
|
278
|
+
boxShadow: shadowVars['--shadow-med'],
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
none: {
|
|
282
|
+
borderWidth: borderVars['--border-width'],
|
|
283
|
+
borderStyle: 'solid',
|
|
284
|
+
borderColor: {
|
|
285
|
+
default: colorVars['--color-border-emphasized'],
|
|
286
|
+
':focus-within': colorVars['--color-accent'],
|
|
287
|
+
},
|
|
288
|
+
// Draw the border *inside* the padding — subtract its width from the body
|
|
289
|
+
// padding (like Card's withBorder) so total inset (border + padding) equals
|
|
290
|
+
// the elevated case and content geometry is unchanged.
|
|
291
|
+
padding: `calc(var(--_chat-composer-padding) - ${borderVars['--border-width']})`,
|
|
292
|
+
boxShadow: {
|
|
293
|
+
default: 'none',
|
|
294
|
+
':hover:not(:focus-within)': {
|
|
295
|
+
'@media (hover: hover)': `inset 0px 0px 0px 2px color-mix(in srgb, ${colorVars['--color-border-emphasized']} 30%, transparent)`,
|
|
296
|
+
},
|
|
297
|
+
':focus-within': `inset 0px 0px 0px 2px ${colorVars['--color-accent-muted']}`,
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
});
|
|
301
|
+
|
|
255
302
|
// =============================================================================
|
|
256
303
|
// Component
|
|
257
304
|
// =============================================================================
|
|
@@ -280,6 +327,7 @@ export function ChatComposer(props: ChatComposerProps) {
|
|
|
280
327
|
placeholder: placeholderFromProps,
|
|
281
328
|
isDisabled = false,
|
|
282
329
|
density = 'balanced',
|
|
330
|
+
elevation = 'low',
|
|
283
331
|
drawer,
|
|
284
332
|
headerActions,
|
|
285
333
|
headerContext,
|
|
@@ -402,6 +450,7 @@ export function ChatComposer(props: ChatComposerProps) {
|
|
|
402
450
|
onClick={handleBodyClick}
|
|
403
451
|
{...stylex.props(
|
|
404
452
|
styles.body,
|
|
453
|
+
elevationStyles[elevation],
|
|
405
454
|
density === 'compact' && styles.compact,
|
|
406
455
|
xstyle,
|
|
407
456
|
)}>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
import {describe, it, expect} from 'vitest';
|
|
4
|
+
import {render, screen, fireEvent} from '@testing-library/react';
|
|
5
|
+
import {ChatComposerDrawer} from './ChatComposerDrawer';
|
|
6
|
+
|
|
7
|
+
describe('ChatComposerDrawer', () => {
|
|
8
|
+
it('links the toggle to the drawer content via aria-controls', () => {
|
|
9
|
+
render(
|
|
10
|
+
<ChatComposerDrawer count={2} label="Attachments">
|
|
11
|
+
<span>Drawer content</span>
|
|
12
|
+
</ChatComposerDrawer>,
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
const toggle = screen.getByRole('button', {name: /Attachments/});
|
|
16
|
+
const controlsId = toggle.getAttribute('aria-controls');
|
|
17
|
+
// aria-controls must be present and point at the real content region.
|
|
18
|
+
expect(controlsId).toBeTruthy();
|
|
19
|
+
const region = document.getElementById(controlsId as string);
|
|
20
|
+
expect(region).not.toBeNull();
|
|
21
|
+
expect(region).toContainElement(screen.getByText('Drawer content'));
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('keeps aria-controls resolvable while collapsed (content stays mounted)', () => {
|
|
25
|
+
render(
|
|
26
|
+
<ChatComposerDrawer count={2} label="Attachments" defaultIsCollapsed>
|
|
27
|
+
<span>Drawer content</span>
|
|
28
|
+
</ChatComposerDrawer>,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const toggle = screen.getByRole('button', {name: /Attachments/});
|
|
32
|
+
const controlsId = toggle.getAttribute('aria-controls');
|
|
33
|
+
expect(controlsId).toBeTruthy();
|
|
34
|
+
const region = document.getElementById(controlsId as string);
|
|
35
|
+
expect(region).not.toBeNull();
|
|
36
|
+
expect(region).toContainElement(screen.getByText('Drawer content'));
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('toggles aria-expanded when the toggle is activated', () => {
|
|
40
|
+
render(
|
|
41
|
+
<ChatComposerDrawer count={2} label="Attachments">
|
|
42
|
+
<span>Drawer content</span>
|
|
43
|
+
</ChatComposerDrawer>,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const toggle = screen.getByRole('button', {name: /Attachments/});
|
|
47
|
+
expect(toggle).toHaveAttribute('aria-expanded', 'true');
|
|
48
|
+
|
|
49
|
+
fireEvent.click(toggle);
|
|
50
|
+
expect(screen.getByRole('button', {name: /Attachments/})).toHaveAttribute(
|
|
51
|
+
'aria-expanded',
|
|
52
|
+
'false',
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
fireEvent.click(screen.getByRole('button', {name: /Attachments/}));
|
|
56
|
+
expect(screen.getByRole('button', {name: /Attachments/})).toHaveAttribute(
|
|
57
|
+
'aria-expanded',
|
|
58
|
+
'true',
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -9,13 +9,15 @@
|
|
|
9
9
|
* @position Collapsible drawer panel for ChatComposer.
|
|
10
10
|
* Supports expanded (full content) and collapsed (count + label) states
|
|
11
11
|
* with fade animation and grid-template-rows height transition.
|
|
12
|
+
* The collapse toggle exposes aria-expanded + aria-controls linking it to
|
|
13
|
+
* the content region (disclosure pattern).
|
|
12
14
|
*
|
|
13
15
|
* SYNC: When modified, update:
|
|
14
16
|
* - /packages/core/src/Chat/index.ts (exports)
|
|
15
17
|
* - /packages/cli/templates/blocks/components/ChatComposerDrawer/ (block examples)
|
|
16
18
|
*/
|
|
17
19
|
|
|
18
|
-
import {useState, type ReactNode} from 'react';
|
|
20
|
+
import {useId, useState, type ReactNode} from 'react';
|
|
19
21
|
import type {StyleXStyles} from '@stylexjs/stylex';
|
|
20
22
|
import * as stylex from '@stylexjs/stylex';
|
|
21
23
|
import {
|
|
@@ -240,6 +242,12 @@ export function ChatComposerDrawer({
|
|
|
240
242
|
|
|
241
243
|
const canCollapse = count != null;
|
|
242
244
|
|
|
245
|
+
// Links the toggle to the region it shows/hides so assistive tech can move
|
|
246
|
+
// from the button to its controlled content (disclosure pattern). The
|
|
247
|
+
// content stays mounted while collapsed (hidden via the grid-row collapse),
|
|
248
|
+
// so the reference always resolves.
|
|
249
|
+
const contentId = useId();
|
|
250
|
+
|
|
243
251
|
const toggle = () => {
|
|
244
252
|
const next = !isCollapsed;
|
|
245
253
|
if (!isControlled) {
|
|
@@ -271,6 +279,7 @@ export function ChatComposerDrawer({
|
|
|
271
279
|
role="button"
|
|
272
280
|
tabIndex={0}
|
|
273
281
|
aria-expanded={!isCollapsed}
|
|
282
|
+
aria-controls={contentId}
|
|
274
283
|
aria-label={
|
|
275
284
|
isCollapsed
|
|
276
285
|
? t('@astryx.chatComposerDrawer.expand', {label})
|
|
@@ -301,6 +310,7 @@ export function ChatComposerDrawer({
|
|
|
301
310
|
)}
|
|
302
311
|
|
|
303
312
|
<div
|
|
313
|
+
id={contentId}
|
|
304
314
|
{...stylex.props(
|
|
305
315
|
styles.contentGrid,
|
|
306
316
|
canCollapse && isCollapsed && styles.contentGridCollapsed,
|
|
@@ -80,6 +80,11 @@ export const docs = {
|
|
|
80
80
|
type: '(value: string) => void',
|
|
81
81
|
description: 'Called when the user presses Enter without Shift. The serialized value includes token placeholders.',
|
|
82
82
|
},
|
|
83
|
+
{
|
|
84
|
+
name: 'onKeyDown',
|
|
85
|
+
type: '(event) => void',
|
|
86
|
+
description: 'Key-down handler invoked before the built-in Enter/history behavior (after any open trigger menu). The seam for platform-specific keys: call event.preventDefault() to suppress the default submit (e.g. newline on a touch keyboard), or act on the event yourself to add behavior (e.g. submit on Cmd/Ctrl+Enter). IME composition is always respected — Enter never submits mid-composition.',
|
|
87
|
+
},
|
|
83
88
|
],
|
|
84
89
|
};
|
|
85
90
|
|
|
@@ -102,6 +107,7 @@ export const docsZh = {
|
|
|
102
107
|
onPaste: '粘贴文本时调用。用于拦截或转换粘贴内容。',
|
|
103
108
|
onFiles: '粘贴或拖放文件到输入时调用。用于处理附件。',
|
|
104
109
|
onSubmit: '用户不按 Shift 按 Enter 时调用。序列化值包含标记占位符。',
|
|
110
|
+
onKeyDown: '在内置 Enter/历史处理之前(在打开的触发菜单之后)调用的按键处理器。平台特定按键的接缝:调用 event.preventDefault() 可抑制默认提交(例如触控键盘上插入换行),或自行处理事件以添加行为(例如 Cmd/Ctrl+Enter 提交)。始终尊重输入法组字状态——组字过程中 Enter 永不提交。',
|
|
105
111
|
},
|
|
106
112
|
};
|
|
107
113
|
|
|
@@ -124,5 +130,6 @@ export const docsDense = {
|
|
|
124
130
|
onPaste: 'paste handler; intercept/transform pasted content',
|
|
125
131
|
onFiles: 'file paste/drop handler; use for attachments',
|
|
126
132
|
onSubmit: 'Enter w/o Shift handler; serialized value includes token placeholders',
|
|
133
|
+
onKeyDown: 'keydown handler before built-in Enter/history (after trigger menu); preventDefault() to suppress default submit (touch newline) or act on event to add behavior (Cmd/Ctrl+Enter). IME composition always respected',
|
|
127
134
|
},
|
|
128
135
|
};
|