@astryxdesign/core 0.1.8 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +100 -0
- package/dist/AppShell/AppShell.d.ts.map +1 -1
- package/dist/AppShell/AppShell.js +6 -2
- package/dist/AppShell/AppShellMobileContext.d.ts +8 -0
- package/dist/AppShell/AppShellMobileContext.d.ts.map +1 -1
- package/dist/Avatar/Avatar.d.ts +18 -2
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +63 -7
- package/dist/Avatar/AvatarStatusDot.d.ts +33 -5
- package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
- package/dist/Avatar/AvatarStatusDot.js +144 -19
- package/dist/Banner/Banner.d.ts +9 -1
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +31 -2
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts +31 -2
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +208 -6
- package/dist/Breadcrumbs/index.d.ts +5 -0
- package/dist/Breadcrumbs/index.d.ts.map +1 -1
- package/dist/Breadcrumbs/index.js +9 -1
- package/dist/Button/Button.d.ts +8 -2
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +31 -2
- package/dist/ButtonGroup/ButtonGroup.d.ts +9 -1
- package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
- package/dist/ButtonGroup/ButtonGroup.js +30 -1
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +30 -2
- package/dist/Calendar/dayCellUtils.js +1 -1
- package/dist/Card/Card.d.ts +8 -2
- package/dist/Card/Card.d.ts.map +1 -1
- package/dist/Card/Card.js +28 -2
- package/dist/Carousel/Carousel.d.ts +4 -1
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +24 -4
- package/dist/Chat/ChatComposer.d.ts +8 -0
- package/dist/Chat/ChatComposer.d.ts.map +1 -1
- package/dist/Chat/ChatComposer.js +46 -7
- package/dist/Chat/ChatComposerDrawer.d.ts +2 -0
- package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
- package/dist/Chat/ChatComposerDrawer.js +11 -1
- package/dist/Chat/ChatComposerInput.d.ts +20 -3
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +35 -3
- package/dist/Chat/ChatContext.d.ts +33 -0
- package/dist/Chat/ChatContext.d.ts.map +1 -1
- package/dist/Chat/ChatContext.js +16 -2
- package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
- package/dist/Chat/ChatDictationButton.js +1 -1
- package/dist/Chat/ChatLayout.d.ts.map +1 -1
- package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
- package/dist/Chat/ChatLayoutScrollButton.js +4 -4
- package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
- package/dist/Chat/ChatSendButton.d.ts.map +1 -1
- package/dist/Chat/ChatSendButton.js +7 -1
- package/dist/Chat/ChatToolCalls.d.ts +5 -1
- package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.js +18 -9
- package/dist/Chat/index.d.ts +2 -1
- package/dist/Chat/index.d.ts.map +1 -1
- package/dist/Chat/index.js +1 -1
- package/dist/Chat/useChatStreamScroll.d.ts +2 -1
- package/dist/Chat/useChatStreamScroll.d.ts.map +1 -1
- package/dist/Chat/useChatStreamScroll.js +22 -8
- package/dist/Chat/useSpeechRecognition.d.ts +0 -1
- package/dist/Chat/useSpeechRecognition.d.ts.map +1 -1
- package/dist/Chat/useSpeechRecognition.js +1 -1
- package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +1 -1
- package/dist/CheckboxList/CheckboxListItem.d.ts +20 -1
- package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.js +11 -1
- package/dist/ClickableCard/ClickableCard.d.ts +9 -2
- package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
- package/dist/ClickableCard/ClickableCard.js +5 -2
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +8 -1
- package/dist/CommandPalette/CommandPalette.d.ts +1 -1
- package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPalette.js +3 -1
- package/dist/CommandPalette/CommandPaletteGroup.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteGroup.js +9 -2
- package/dist/CommandPalette/CommandPaletteInput.d.ts +7 -1
- package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteInput.js +6 -1
- package/dist/DateInput/DateInput.d.ts +47 -1
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +26 -2
- package/dist/DateInput/index.d.ts +1 -1
- package/dist/DateInput/index.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +10 -1
- package/dist/Dialog/Dialog.d.ts +3 -0
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +57 -6
- package/dist/Dialog/DialogContext.d.ts +7 -0
- package/dist/Dialog/DialogContext.d.ts.map +1 -1
- package/dist/Dialog/DialogContext.js +2 -2
- package/dist/Dialog/DialogHeader.d.ts +5 -2
- package/dist/Dialog/DialogHeader.d.ts.map +1 -1
- package/dist/Dialog/DialogHeader.js +6 -1
- package/dist/Divider/Divider.js +1 -1
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +1 -2
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +1 -2
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +1 -2
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioGroup.js +1 -2
- package/dist/Field/Field.d.ts.map +1 -1
- package/dist/Field/Field.js +2 -3
- package/dist/Field/FieldLabel.d.ts +1 -1
- package/dist/Field/FieldLabel.d.ts.map +1 -1
- package/dist/Field/FieldLabel.js +42 -15
- package/dist/FieldStatus/FieldStatus.d.ts +8 -0
- package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
- package/dist/FieldStatus/FieldStatus.js +22 -4
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +10 -2
- package/dist/HoverCard/HoverCard.d.ts +9 -1
- package/dist/HoverCard/HoverCard.d.ts.map +1 -1
- package/dist/HoverCard/HoverCard.js +2 -0
- package/dist/HoverCard/useHoverCard.d.ts +8 -0
- package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +7 -2
- package/dist/Icon/Icon.d.ts +7 -2
- package/dist/Icon/Icon.d.ts.map +1 -1
- package/dist/Item/Item.d.ts +6 -1
- package/dist/Item/Item.d.ts.map +1 -1
- package/dist/Item/Item.js +22 -1
- package/dist/Layer/useLayer.d.ts +6 -0
- package/dist/Layer/useLayer.d.ts.map +1 -1
- package/dist/Layer/useLayer.js +2 -0
- package/dist/MobileNav/MobileNav.d.ts.map +1 -1
- package/dist/MobileNav/MobileNav.js +6 -1
- package/dist/MobileNav/MobileNavToggle.d.ts.map +1 -1
- package/dist/MobileNav/MobileNavToggle.js +4 -0
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +39 -12
- package/dist/MultiSelector/hooks.d.ts.map +1 -1
- package/dist/MultiSelector/hooks.js +16 -0
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/Outline/Outline.d.ts +0 -1
- package/dist/Outline/Outline.d.ts.map +1 -1
- package/dist/Outline/Outline.js +0 -1
- package/dist/OverflowList/OverflowList.d.ts +20 -3
- package/dist/OverflowList/OverflowList.d.ts.map +1 -1
- package/dist/OverflowList/OverflowList.js +29 -4
- package/dist/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +3 -2
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +3 -9
- package/dist/PowerSearch/PowerSearchEditPopover.js +1 -1
- package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
- package/dist/SelectableCard/SelectableCard.d.ts +9 -2
- package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
- package/dist/SelectableCard/SelectableCard.js +16 -13
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +4 -2
- package/dist/Selector/hooks.d.ts.map +1 -1
- package/dist/Selector/hooks.js +16 -0
- package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
- package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
- package/dist/SideNav/SideNavHeading.js +21 -8
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +19 -2
- package/dist/Stack/stack.stylex.d.ts +9 -9
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +3 -2
- package/dist/Table/index.d.ts +2 -0
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +1 -0
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
- package/dist/Table/plugins/rowStatus/index.d.ts +3 -0
- package/dist/Table/plugins/rowStatus/index.d.ts.map +1 -0
- package/dist/Table/plugins/rowStatus/index.js +3 -0
- package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts +60 -0
- package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -0
- package/dist/Table/plugins/rowStatus/useTableRowStatus.js +145 -0
- package/dist/Table/plugins/selection/useTableSelection.d.ts +13 -0
- package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
- package/dist/Table/plugins/selection/useTableSelection.js +4 -1
- package/dist/Table/plugins/tree/useTableTreeData.d.ts +16 -0
- package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
- package/dist/Table/plugins/tree/useTableTreeData.js +87 -0
- package/dist/Table/plugins/tree/useTableTreeState.d.ts +6 -0
- package/dist/Table/plugins/tree/useTableTreeState.d.ts.map +1 -1
- package/dist/Table/plugins/tree/useTableTreeState.js +22 -1
- package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
- package/dist/Table/useBaseTablePlugins.js +5 -3
- package/dist/Thumbnail/Thumbnail.d.ts +26 -5
- package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
- package/dist/Thumbnail/Thumbnail.js +97 -54
- package/dist/Thumbnail/thumbnail.markers.stylex.d.ts +9 -0
- package/dist/Thumbnail/thumbnail.markers.stylex.d.ts.map +1 -0
- package/dist/Thumbnail/thumbnail.markers.stylex.js +14 -0
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +15 -4
- package/dist/Timestamp/Timestamp.d.ts +3 -1
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +8 -6
- package/dist/Toast/useToast.d.ts.map +1 -1
- package/dist/Toast/useToast.js +2 -5
- package/dist/ToggleButton/ToggleButtonGroup.d.ts +0 -1
- package/dist/ToggleButton/ToggleButtonGroup.d.ts.map +1 -1
- package/dist/ToggleButton/ToggleButtonGroup.js +1 -1
- package/dist/Tokenizer/Tokenizer.d.ts +1 -1
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +16 -4
- package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
- package/dist/TopNav/TopNavHeading.js +18 -8
- package/dist/TopNav/TopNavMegaMenu.js +10 -4
- package/dist/TopNav/TopNavMegaMenuFeaturedCard.d.ts +9 -1
- package/dist/TopNav/TopNavMegaMenuFeaturedCard.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenuFeaturedCard.js +8 -1
- package/dist/TopNav/TopNavMenu.d.ts +2 -1
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +68 -6
- package/dist/TreeList/TreeList.d.ts +12 -3
- package/dist/TreeList/TreeList.d.ts.map +1 -1
- package/dist/TreeList/TreeList.js +2 -0
- package/dist/TreeList/TreeListBranches.d.ts +5 -0
- package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
- package/dist/TreeList/TreeListBranches.js +10 -4
- package/dist/TreeList/TreeListItem.d.ts +8 -2
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +27 -7
- package/dist/TreeList/TreeListTypes.d.ts +25 -1
- package/dist/TreeList/TreeListTypes.d.ts.map +1 -1
- package/dist/TreeList/index.d.ts +2 -2
- package/dist/TreeList/index.d.ts.map +1 -1
- package/dist/astryx.css +28 -18
- package/dist/astryx.umd.js +51 -51
- package/dist/astryx.umd.js.map +4 -4
- package/dist/authoring/doc.d.ts.map +1 -1
- package/dist/docs-types.d.ts +8 -0
- package/dist/docs-types.d.ts.map +1 -1
- package/dist/hooks/computeOverflow.d.ts +47 -0
- package/dist/hooks/computeOverflow.d.ts.map +1 -0
- package/dist/hooks/computeOverflow.js +213 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +2 -1
- package/dist/hooks/useDevWarning.d.ts +18 -0
- package/dist/hooks/useDevWarning.d.ts.map +1 -0
- package/dist/hooks/useDevWarning.js +49 -0
- package/dist/hooks/useOverflow.d.ts +19 -0
- package/dist/hooks/useOverflow.d.ts.map +1 -1
- package/dist/hooks/useOverflow.js +36 -25
- package/dist/i18n/resolve.d.ts.map +1 -1
- package/dist/i18n/resolve.js +7 -18
- package/dist/theme/Theme.d.ts.map +1 -1
- package/dist/theme/Theme.js +2 -7
- package/dist/theme/generateThemeRules.d.ts.map +1 -1
- package/dist/theme/generateThemeRules.js +51 -0
- package/dist/theme/hct.d.ts +0 -1
- package/dist/theme/hct.d.ts.map +1 -1
- package/dist/theme/hct.js +1 -1
- package/dist/theme/syntax/defineSyntaxTheme.d.ts.map +1 -1
- package/dist/theme/syntax/defineSyntaxTheme.js +2 -1
- package/dist/utils/devWarning.d.ts +42 -0
- package/dist/utils/devWarning.d.ts.map +1 -0
- package/dist/utils/devWarning.js +88 -0
- package/dist/utils/index.d.ts +3 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -2
- package/dist/utils/plainDate.d.ts +30 -0
- package/dist/utils/plainDate.d.ts.map +1 -1
- package/dist/utils/plainDate.js +53 -0
- package/dist/utils/types.d.ts +13 -0
- package/dist/utils/types.d.ts.map +1 -1
- package/locales/en.json +24 -0
- package/locales/fr-FR.json +14 -0
- package/locales/pseudo.json +18 -0
- package/package.json +1 -1
- package/src/AppShell/AppShell.tsx +6 -0
- package/src/AppShell/AppShellMobileContext.tsx +8 -0
- package/src/AppShell/useAppShellMobile.doc.mjs +8 -0
- package/src/Avatar/Avatar.doc.mjs +15 -3
- package/src/Avatar/Avatar.test.tsx +156 -1
- package/src/Avatar/Avatar.tsx +96 -4
- package/src/Avatar/AvatarStatusDot.doc.mjs +23 -12
- package/src/Avatar/AvatarStatusDot.test.tsx +284 -0
- package/src/Avatar/AvatarStatusDot.tsx +178 -21
- package/src/Banner/Banner.doc.mjs +9 -0
- package/src/Banner/Banner.test.tsx +30 -0
- package/src/Banner/Banner.tsx +47 -3
- package/src/Breadcrumbs/BreadcrumbItem.doc.mjs +26 -0
- package/src/Breadcrumbs/BreadcrumbItem.tsx +333 -14
- package/src/Breadcrumbs/Breadcrumbs.doc.mjs +2 -0
- package/src/Breadcrumbs/Breadcrumbs.test.tsx +284 -2
- package/src/Breadcrumbs/index.ts +27 -0
- package/src/Button/Button.doc.mjs +9 -0
- package/src/Button/Button.test.tsx +49 -0
- package/src/Button/Button.tsx +25 -1
- package/src/ButtonGroup/ButtonGroup.doc.mjs +3 -0
- package/src/ButtonGroup/ButtonGroup.test.tsx +37 -0
- package/src/ButtonGroup/ButtonGroup.tsx +32 -0
- package/src/Calendar/Calendar.doc.mjs +12 -1
- package/src/Calendar/Calendar.test.tsx +193 -0
- package/src/Calendar/Calendar.tsx +38 -16
- package/src/Calendar/dayCellUtils.test.ts +5 -0
- package/src/Calendar/dayCellUtils.ts +1 -1
- package/src/Card/Card.doc.mjs +9 -0
- package/src/Card/Card.test.tsx +21 -3
- package/src/Card/Card.tsx +35 -2
- package/src/Carousel/Carousel.doc.mjs +1 -0
- package/src/Carousel/Carousel.test.tsx +53 -0
- package/src/Carousel/Carousel.tsx +34 -4
- package/src/Chat/ChatComposer.doc.mjs +12 -3
- package/src/Chat/ChatComposer.test.tsx +115 -0
- package/src/Chat/ChatComposer.tsx +73 -9
- package/src/Chat/ChatComposerDrawer.test.tsx +61 -0
- package/src/Chat/ChatComposerDrawer.tsx +11 -1
- package/src/Chat/ChatComposerInput.doc.mjs +7 -0
- package/src/Chat/ChatComposerInput.test.tsx +103 -0
- package/src/Chat/ChatComposerInput.tsx +49 -3
- package/src/Chat/ChatContext.tsx +37 -10
- package/src/Chat/ChatDictationButton.tsx +4 -1
- package/src/Chat/ChatLayout.tsx +3 -1
- package/src/Chat/ChatLayoutScrollButton.tsx +7 -2
- package/src/Chat/ChatMessageMetadata.tsx +1 -2
- package/src/Chat/ChatSendButton.tsx +11 -1
- package/src/Chat/ChatToolCalls.test.tsx +64 -0
- package/src/Chat/ChatToolCalls.tsx +25 -4
- package/src/Chat/index.ts +5 -1
- package/src/Chat/useChatStreamScroll.test.tsx +49 -0
- package/src/Chat/useChatStreamScroll.ts +27 -10
- package/src/Chat/useSpeechRecognition.ts +1 -1
- package/src/Chat/useTriggerMenu.tsx +3 -1
- package/src/CheckboxInput/CheckboxInput.test.tsx +32 -1
- package/src/CheckboxInput/CheckboxInput.tsx +1 -1
- package/src/CheckboxList/CheckboxList.test.tsx +81 -3
- package/src/CheckboxList/CheckboxListItem.doc.mjs +33 -5
- package/src/CheckboxList/CheckboxListItem.tsx +41 -5
- package/src/ClickableCard/ClickableCard.doc.mjs +2 -0
- package/src/ClickableCard/ClickableCard.test.tsx +34 -0
- package/src/ClickableCard/ClickableCard.tsx +13 -3
- package/src/CodeBlock/CodeBlock.test.tsx +56 -0
- package/src/CodeBlock/CodeBlock.tsx +9 -1
- package/src/CommandPalette/CommandPalette.doc.mjs +1 -0
- package/src/CommandPalette/CommandPalette.tsx +3 -1
- package/src/CommandPalette/CommandPaletteGroup.test.tsx +71 -0
- package/src/CommandPalette/CommandPaletteGroup.tsx +12 -1
- package/src/CommandPalette/CommandPaletteInput.doc.mjs +26 -9
- package/src/CommandPalette/CommandPaletteInput.test.tsx +34 -0
- package/src/CommandPalette/CommandPaletteInput.tsx +11 -0
- package/src/ContextMenu/ContextMenu.test.tsx +193 -0
- package/src/DateInput/DateInput.doc.mjs +16 -0
- package/src/DateInput/DateInput.test.tsx +108 -0
- package/src/DateInput/DateInput.tsx +68 -3
- package/src/DateInput/index.ts +1 -0
- package/src/DateTimeInput/DateTimeInput.test.tsx +35 -0
- package/src/DateTimeInput/DateTimeInput.tsx +14 -2
- package/src/Dialog/Dialog.doc.mjs +2 -2
- package/src/Dialog/Dialog.test.tsx +77 -4
- package/src/Dialog/Dialog.tsx +89 -5
- package/src/Dialog/DialogContext.ts +9 -2
- package/src/Dialog/DialogHeader.doc.mjs +2 -2
- package/src/Dialog/DialogHeader.tsx +9 -2
- package/src/Divider/Divider.tsx +2 -2
- package/src/DropdownMenu/DropdownMenu.test.tsx +169 -0
- package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +1 -2
- package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +1 -2
- package/src/Field/Field.test.tsx +57 -12
- package/src/Field/Field.tsx +6 -5
- package/src/Field/FieldLabel.tsx +8 -0
- package/src/FieldStatus/FieldStatus.doc.mjs +17 -5
- package/src/FieldStatus/FieldStatus.test.tsx +78 -40
- package/src/FieldStatus/FieldStatus.tsx +22 -4
- package/src/FileInput/FileInput.test.tsx +52 -3
- package/src/FileInput/FileInput.tsx +13 -2
- package/src/HoverCard/HoverCard.doc.mjs +13 -0
- package/src/HoverCard/HoverCard.test.tsx +21 -2
- package/src/HoverCard/HoverCard.tsx +11 -0
- package/src/HoverCard/useHoverCard.doc.mjs +2 -0
- package/src/HoverCard/useHoverCard.tsx +24 -2
- package/src/Icon/Icon.doc.mjs +3 -2
- package/src/Icon/Icon.tsx +7 -2
- package/src/IconButton/IconButton.doc.mjs +8 -0
- package/src/IconButton/IconButton.test.tsx +15 -18
- package/src/Item/Item.test.tsx +40 -18
- package/src/Item/Item.tsx +38 -2
- package/src/Layer/useLayer.tsx +8 -0
- package/src/List/List.test.tsx +9 -8
- package/src/MobileNav/MobileNav.tsx +12 -1
- package/src/MobileNav/MobileNavToggle.test.tsx +116 -0
- package/src/MobileNav/MobileNavToggle.tsx +9 -1
- package/src/MultiSelector/MultiSelector.doc.mjs +4 -2
- package/src/MultiSelector/MultiSelector.test.tsx +172 -3
- package/src/MultiSelector/MultiSelector.tsx +55 -12
- package/src/MultiSelector/hooks.ts +17 -0
- package/src/NumberInput/NumberInput.test.tsx +11 -7
- package/src/NumberInput/NumberInput.tsx +1 -2
- package/src/Outline/Outline.tsx +0 -1
- package/src/OverflowList/OverflowList.doc.mjs +39 -6
- package/src/OverflowList/OverflowList.test.tsx +111 -0
- package/src/OverflowList/OverflowList.tsx +48 -8
- package/src/Popover/Popover.tsx +7 -4
- package/src/Popover/usePopover.tsx +9 -13
- package/src/PowerSearch/PowerSearch.doc.mjs +14 -0
- package/src/PowerSearch/PowerSearch.test.tsx +20 -0
- package/src/PowerSearch/PowerSearchEditPopover.test.tsx +67 -0
- package/src/PowerSearch/PowerSearchEditPopover.tsx +1 -1
- package/src/PowerSearch/usePowerSearchSource.ts +1 -2
- package/src/SelectableCard/SelectableCard.doc.mjs +2 -0
- package/src/SelectableCard/SelectableCard.test.tsx +46 -4
- package/src/SelectableCard/SelectableCard.tsx +29 -16
- package/src/Selector/Selector.test.tsx +69 -0
- package/src/Selector/Selector.tsx +5 -1
- package/src/Selector/hooks.ts +17 -0
- package/src/SideNav/SideNav.test.tsx +119 -3
- package/src/SideNav/SideNavCollapseButton.tsx +6 -1
- package/src/SideNav/SideNavHeading.tsx +24 -8
- package/src/SideNav/SideNavItem.tsx +5 -1
- package/src/Slider/Slider.test.tsx +75 -2
- package/src/Slider/Slider.tsx +21 -5
- package/src/Switch/Switch.test.tsx +32 -1
- package/src/Table/BaseTable.tsx +3 -5
- package/src/Table/index.ts +5 -0
- package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +6 -2
- package/src/Table/plugins/rowStatus/index.ts +7 -0
- package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +138 -0
- package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +189 -0
- package/src/Table/plugins/selection/useTableSelection.test.tsx +16 -0
- package/src/Table/plugins/selection/useTableSelection.tsx +19 -1
- package/src/Table/plugins/tree/useTableTreeData.test.tsx +135 -3
- package/src/Table/plugins/tree/useTableTreeData.tsx +128 -1
- package/src/Table/plugins/tree/useTableTreeState.test.tsx +104 -0
- package/src/Table/plugins/tree/useTableTreeState.tsx +46 -2
- package/src/Table/useBaseTablePlugins.ts +10 -6
- package/src/Table/useTableRowStatus.doc.mjs +30 -0
- package/src/Table/useTableSelection.doc.mjs +65 -14
- package/src/Table/useTableTreeData.doc.mjs +29 -0
- package/src/Table/useTableTreeState.doc.mjs +2 -2
- package/src/TextArea/TextArea.test.tsx +9 -1
- package/src/Thumbnail/Thumbnail.doc.mjs +19 -10
- package/src/Thumbnail/Thumbnail.test.tsx +98 -7
- package/src/Thumbnail/Thumbnail.tsx +141 -41
- package/src/Thumbnail/thumbnail.markers.stylex.ts +12 -0
- package/src/TimeInput/TimeInput.test.tsx +76 -1
- package/src/TimeInput/TimeInput.tsx +19 -6
- package/src/Timestamp/Timestamp.doc.mjs +4 -4
- package/src/Timestamp/Timestamp.test.tsx +32 -0
- package/src/Timestamp/Timestamp.tsx +28 -8
- package/src/Toast/useToast.tsx +7 -8
- package/src/ToggleButton/ToggleButtonGroup.tsx +1 -1
- package/src/Tokenizer/Tokenizer.doc.mjs +15 -1
- package/src/Tokenizer/Tokenizer.test.tsx +183 -2
- package/src/Tokenizer/Tokenizer.tsx +16 -4
- package/src/TopNav/TopNav.test.tsx +125 -1
- package/src/TopNav/TopNavHeading.tsx +24 -8
- package/src/TopNav/TopNavMegaMenu.test.tsx +149 -1
- package/src/TopNav/TopNavMegaMenu.tsx +10 -4
- package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +3 -3
- package/src/TopNav/TopNavMegaMenuFeaturedCard.tsx +19 -2
- package/src/TopNav/TopNavMenu.test.tsx +118 -4
- package/src/TopNav/TopNavMenu.tsx +83 -5
- package/src/TreeList/TreeList.doc.mjs +22 -0
- package/src/TreeList/TreeList.test.tsx +232 -0
- package/src/TreeList/TreeList.tsx +18 -2
- package/src/TreeList/TreeListBranches.tsx +16 -2
- package/src/TreeList/TreeListItem.tsx +51 -11
- package/src/TreeList/TreeListTypes.ts +27 -1
- package/src/TreeList/index.ts +2 -2
- package/src/authoring/doc.ts +1 -3
- package/src/docs-types.ts +5 -0
- package/src/hooks/computeOverflow.test.ts +422 -0
- package/src/hooks/computeOverflow.ts +290 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useDevWarning.test.tsx +45 -0
- package/src/hooks/useDevWarning.ts +54 -0
- package/src/hooks/useOverflow.test.ts +96 -4
- package/src/hooks/useOverflow.ts +74 -33
- package/src/i18n/InternationalizationProvider.doc.mjs +99 -2
- package/src/i18n/resolve.ts +9 -21
- package/src/theme/Theme.tsx +11 -14
- package/src/theme/generateThemeRules.test.ts +65 -0
- package/src/theme/generateThemeRules.ts +56 -0
- package/src/theme/hct.ts +1 -1
- package/src/theme/syntax/defineSyntaxTheme.ts +5 -6
- package/src/utils/devWarning.test.ts +56 -0
- package/src/utils/devWarning.ts +102 -0
- package/src/utils/index.ts +6 -0
- package/src/utils/plainDate.test.ts +22 -0
- package/src/utils/plainDate.ts +61 -0
- package/src/utils/types.ts +14 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
import {describe, it, expect, vi} from 'vitest';
|
|
4
|
+
import {render, fireEvent} from '@testing-library/react';
|
|
5
|
+
import {useEffect, useRef} from 'react';
|
|
6
|
+
import {ChatComposer} from './ChatComposer';
|
|
7
|
+
import {useChatComposerContext} from './ChatContext';
|
|
8
|
+
|
|
9
|
+
// The composer body — the elevated surface — is the div that wraps the input
|
|
10
|
+
// area (styles.body). A custom `input` slot renders inside the inputArea div,
|
|
11
|
+
// so the body is two ancestors up from the slot marker. Walking from a known
|
|
12
|
+
// marker keeps this independent of StyleX's generated class names.
|
|
13
|
+
function renderBodyClass(elevation?: 'none' | 'low'): string {
|
|
14
|
+
const {container} = render(
|
|
15
|
+
<ChatComposer
|
|
16
|
+
onSubmit={() => {}}
|
|
17
|
+
elevation={elevation}
|
|
18
|
+
input={<div data-testid="composer-input-marker" />}
|
|
19
|
+
/>,
|
|
20
|
+
);
|
|
21
|
+
const marker = container.querySelector(
|
|
22
|
+
'[data-testid="composer-input-marker"]',
|
|
23
|
+
)!;
|
|
24
|
+
const inputArea = marker.parentElement!;
|
|
25
|
+
const body = inputArea.parentElement!;
|
|
26
|
+
return body.className;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe('ChatComposer elevation', () => {
|
|
30
|
+
it('applies a distinct body class for each supported level', () => {
|
|
31
|
+
expect(renderBodyClass('none')).not.toBe(renderBodyClass('low'));
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("defaults to 'low' (preserves the raised look)", () => {
|
|
35
|
+
expect(renderBodyClass(undefined)).toBe(renderBodyClass('low'));
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// A custom input that participates in the composer's composition contract:
|
|
40
|
+
// reads value/submit from the exported context, and registers a focus control
|
|
41
|
+
// so the shell can drive body-click-to-focus without knowing its DOM shape.
|
|
42
|
+
function CustomContextInput({focusSpy}: {focusSpy: () => void}) {
|
|
43
|
+
const ctx = useChatComposerContext();
|
|
44
|
+
const ref = useRef<HTMLInputElement>(null);
|
|
45
|
+
const controlRef = ctx?.inputControlRef;
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (!controlRef) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
controlRef.current = {
|
|
51
|
+
focus: () => {
|
|
52
|
+
focusSpy();
|
|
53
|
+
ref.current?.focus();
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
return () => {
|
|
57
|
+
controlRef.current = null;
|
|
58
|
+
};
|
|
59
|
+
}, [controlRef, focusSpy]);
|
|
60
|
+
return (
|
|
61
|
+
<input
|
|
62
|
+
ref={ref}
|
|
63
|
+
data-testid="custom-input"
|
|
64
|
+
value={ctx?.value ?? ''}
|
|
65
|
+
placeholder={ctx?.placeholder}
|
|
66
|
+
disabled={ctx?.isDisabled}
|
|
67
|
+
onChange={e => ctx?.onChange(e.target.value)}
|
|
68
|
+
/>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
describe('ChatComposer input composition contract', () => {
|
|
73
|
+
it('exposes value/onChange/placeholder/isDisabled to a custom input via context', () => {
|
|
74
|
+
const {getByTestId} = render(
|
|
75
|
+
<ChatComposer
|
|
76
|
+
onSubmit={() => {}}
|
|
77
|
+
value="hello"
|
|
78
|
+
placeholder="Say something"
|
|
79
|
+
isDisabled
|
|
80
|
+
input={<CustomContextInput focusSpy={() => {}} />}
|
|
81
|
+
/>,
|
|
82
|
+
);
|
|
83
|
+
const input = getByTestId('custom-input') as HTMLInputElement;
|
|
84
|
+
expect(input.value).toBe('hello');
|
|
85
|
+
expect(input.placeholder).toBe('Say something');
|
|
86
|
+
expect(input.disabled).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('focuses a custom input via its registered control on body click', () => {
|
|
90
|
+
const focusSpy = vi.fn();
|
|
91
|
+
const {getByTestId} = render(
|
|
92
|
+
<ChatComposer
|
|
93
|
+
onSubmit={() => {}}
|
|
94
|
+
input={<CustomContextInput focusSpy={focusSpy} />}
|
|
95
|
+
/>,
|
|
96
|
+
);
|
|
97
|
+
const marker = getByTestId('custom-input');
|
|
98
|
+
const body = marker.parentElement!.parentElement!;
|
|
99
|
+
fireEvent.click(body);
|
|
100
|
+
expect(focusSpy).toHaveBeenCalled();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('falls back to focusing a bare textarea when no control is registered', () => {
|
|
104
|
+
const {getByTestId} = render(
|
|
105
|
+
<ChatComposer
|
|
106
|
+
onSubmit={() => {}}
|
|
107
|
+
input={<textarea data-testid="bare-textarea" />}
|
|
108
|
+
/>,
|
|
109
|
+
);
|
|
110
|
+
const textarea = getByTestId('bare-textarea') as HTMLTextAreaElement;
|
|
111
|
+
const body = textarea.parentElement!.parentElement!;
|
|
112
|
+
fireEvent.click(body);
|
|
113
|
+
expect(document.activeElement).toBe(textarea);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
@@ -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';
|
|
@@ -47,6 +48,7 @@ import {mergeProps} from '../utils';
|
|
|
47
48
|
import {Icon} from '../Icon';
|
|
48
49
|
import {ChatComposerInput} from './ChatComposerInput';
|
|
49
50
|
import {ChatComposerContext} from './ChatContext';
|
|
51
|
+
import type {ChatComposerInputControl} from './ChatContext';
|
|
50
52
|
import {ChatSendButton} from './ChatSendButton';
|
|
51
53
|
import {themeProps} from '../utils/themeProps';
|
|
52
54
|
import {useTranslator} from '../i18n';
|
|
@@ -83,6 +85,14 @@ export interface ChatComposerProps extends Omit<
|
|
|
83
85
|
isDisabled?: boolean;
|
|
84
86
|
/** Density variant */
|
|
85
87
|
density?: ChatComposerDensity;
|
|
88
|
+
/**
|
|
89
|
+
* Resting elevation of the composer body. `low` (the default) keeps today's
|
|
90
|
+
* raised look — low at rest, bumping to med on hover / focus. `none` flattens
|
|
91
|
+
* it and draws a border with the same rest / hover / focus treatment as a
|
|
92
|
+
* text input (emphasized border → accent on focus, with matching inset rings).
|
|
93
|
+
* @default 'low'
|
|
94
|
+
*/
|
|
95
|
+
elevation?: 'none' | 'low';
|
|
86
96
|
|
|
87
97
|
// --- Slot props ---
|
|
88
98
|
|
|
@@ -143,14 +153,7 @@ const styles = stylex.create({
|
|
|
143
153
|
borderRadius: 'var(--_chat-composer-radius)',
|
|
144
154
|
backgroundColor: colorVars['--color-background-popover'],
|
|
145
155
|
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
|
-
},
|
|
156
|
+
transition: `box-shadow ${durationVars['--duration-fast']} ${easeVars['--ease-standard']}, border-color ${durationVars['--duration-fast']} ${easeVars['--ease-standard']}`,
|
|
154
157
|
},
|
|
155
158
|
header: {
|
|
156
159
|
display: 'flex',
|
|
@@ -247,11 +250,56 @@ const styles = stylex.create({
|
|
|
247
250
|
color: colorVars['--color-text-yellow'],
|
|
248
251
|
},
|
|
249
252
|
compact: {
|
|
250
|
-
padding
|
|
253
|
+
// Override the padding var (not the `padding` property) so both the base
|
|
254
|
+
// body padding and the border-inset calc in elevationStyles.none pick up
|
|
255
|
+
// the compact value. Scoped to the body element, so the status bar (which
|
|
256
|
+
// reads the var from root) keeps its own padding.
|
|
257
|
+
'--_chat-composer-padding': spacingVars['--spacing-2'],
|
|
251
258
|
gap: spacingVars['--spacing-1'],
|
|
252
259
|
},
|
|
253
260
|
});
|
|
254
261
|
|
|
262
|
+
// Resting elevation for the composer body, narrowed to two steps.
|
|
263
|
+
// - `low` (default) preserves today's look: low at rest, bumping to med on
|
|
264
|
+
// hover / focus-within (a CSS-only interaction state the component owns).
|
|
265
|
+
// - `none` is flat; depth comes from a border + inset rings instead. The
|
|
266
|
+
// border is drawn *inside* the padding — its width is subtracted from every
|
|
267
|
+
// side (like Card) so total inset (border + padding) equals the elevated
|
|
268
|
+
// case and content geometry is unchanged. Border color, hover, and focus
|
|
269
|
+
// states mirror TextInput (shared `inputWrapperStyles.base` treatment):
|
|
270
|
+
// emphasized border at rest → accent on focus-within, with the same inset
|
|
271
|
+
// shadow rings on hover and focus.
|
|
272
|
+
const elevationStyles = stylex.create({
|
|
273
|
+
low: {
|
|
274
|
+
boxShadow: {
|
|
275
|
+
default: shadowVars['--shadow-low'],
|
|
276
|
+
':hover': {'@media (hover: hover)': shadowVars['--shadow-med']},
|
|
277
|
+
},
|
|
278
|
+
':focus-within': {
|
|
279
|
+
boxShadow: shadowVars['--shadow-med'],
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
none: {
|
|
283
|
+
borderWidth: borderVars['--border-width'],
|
|
284
|
+
borderStyle: 'solid',
|
|
285
|
+
borderColor: {
|
|
286
|
+
default: colorVars['--color-border-emphasized'],
|
|
287
|
+
':focus-within': colorVars['--color-accent'],
|
|
288
|
+
},
|
|
289
|
+
// Draw the border *inside* the padding — subtract its width from the body
|
|
290
|
+
// padding (like Card's withBorder) so total inset (border + padding) equals
|
|
291
|
+
// the elevated case and content geometry is unchanged.
|
|
292
|
+
padding: `calc(var(--_chat-composer-padding) - ${borderVars['--border-width']})`,
|
|
293
|
+
boxShadow: {
|
|
294
|
+
default: 'none',
|
|
295
|
+
':hover:not(:focus-within)': {
|
|
296
|
+
'@media (hover: hover)': `inset 0px 0px 0px 2px color-mix(in srgb, ${colorVars['--color-border-emphasized']} 30%, transparent)`,
|
|
297
|
+
},
|
|
298
|
+
':focus-within': `inset 0px 0px 0px 2px ${colorVars['--color-accent-muted']}`,
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
|
|
255
303
|
// =============================================================================
|
|
256
304
|
// Component
|
|
257
305
|
// =============================================================================
|
|
@@ -280,6 +328,7 @@ export function ChatComposer(props: ChatComposerProps) {
|
|
|
280
328
|
placeholder: placeholderFromProps,
|
|
281
329
|
isDisabled = false,
|
|
282
330
|
density = 'balanced',
|
|
331
|
+
elevation = 'low',
|
|
283
332
|
drawer,
|
|
284
333
|
headerActions,
|
|
285
334
|
headerContext,
|
|
@@ -325,6 +374,11 @@ export function ChatComposer(props: ChatComposerProps) {
|
|
|
325
374
|
|
|
326
375
|
const bodyRef = useRef<HTMLDivElement>(null);
|
|
327
376
|
|
|
377
|
+
// Input slot registers its focus (and future) control here so the shell can
|
|
378
|
+
// drive it without knowing its DOM shape. Stable ref — safe in the memoized
|
|
379
|
+
// context value.
|
|
380
|
+
const inputControlRef = useRef<ChatComposerInputControl | null>(null);
|
|
381
|
+
|
|
328
382
|
const handleBodyClick = useCallback((e: MouseEvent<HTMLDivElement>) => {
|
|
329
383
|
// Focus the input when clicking empty space in the body.
|
|
330
384
|
// Skip if the click target is a button, link, or interactive element.
|
|
@@ -336,6 +390,14 @@ export function ChatComposer(props: ChatComposerProps) {
|
|
|
336
390
|
) {
|
|
337
391
|
return;
|
|
338
392
|
}
|
|
393
|
+
// Prefer the input's registered control (works for any input shape,
|
|
394
|
+
// including editors whose focusable node isn't a bare
|
|
395
|
+
// contenteditable/textarea). Fall back to a DOM query so uninstrumented
|
|
396
|
+
// custom inputs still get click-to-focus.
|
|
397
|
+
if (inputControlRef.current) {
|
|
398
|
+
inputControlRef.current.focus();
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
339
401
|
const editable = bodyRef.current?.querySelector<HTMLElement>(
|
|
340
402
|
'[contenteditable="true"], textarea',
|
|
341
403
|
);
|
|
@@ -370,6 +432,7 @@ export function ChatComposer(props: ChatComposerProps) {
|
|
|
370
432
|
isStopShown,
|
|
371
433
|
canSend,
|
|
372
434
|
onStop,
|
|
435
|
+
inputControlRef,
|
|
373
436
|
}),
|
|
374
437
|
[
|
|
375
438
|
currentValue,
|
|
@@ -402,6 +465,7 @@ export function ChatComposer(props: ChatComposerProps) {
|
|
|
402
465
|
onClick={handleBodyClick}
|
|
403
466
|
{...stylex.props(
|
|
404
467
|
styles.body,
|
|
468
|
+
elevationStyles[elevation],
|
|
405
469
|
density === 'compact' && styles.compact,
|
|
406
470
|
xstyle,
|
|
407
471
|
)}>
|
|
@@ -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
|
};
|
|
@@ -155,6 +155,109 @@ describe('ChatComposerInput', () => {
|
|
|
155
155
|
});
|
|
156
156
|
});
|
|
157
157
|
|
|
158
|
+
describe('composer focus control', () => {
|
|
159
|
+
it('registers a focus control so a body click focuses the input', () => {
|
|
160
|
+
render(
|
|
161
|
+
<ChatComposer onSubmit={() => {}} input={<ChatComposerInput />} />,
|
|
162
|
+
);
|
|
163
|
+
const editable = screen.getByRole('textbox');
|
|
164
|
+
// Walk to the composer body: editable → input root → inputArea → body.
|
|
165
|
+
const inputRoot = editable.parentElement!;
|
|
166
|
+
const inputArea = inputRoot.parentElement!;
|
|
167
|
+
const body = inputArea.parentElement!;
|
|
168
|
+
// Click empty space in the body → shell drives the registered control.
|
|
169
|
+
fireEvent.click(body);
|
|
170
|
+
expect(document.activeElement).toBe(editable);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
describe('Enter submit behavior', () => {
|
|
175
|
+
it('does not submit on Enter while IME composition is in progress', () => {
|
|
176
|
+
const onSubmit = vi.fn();
|
|
177
|
+
render(<ChatComposerInput onSubmit={onSubmit} />);
|
|
178
|
+
const textbox = screen.getByRole('textbox');
|
|
179
|
+
textbox.textContent = 'こんにちは';
|
|
180
|
+
fireEvent.input(textbox);
|
|
181
|
+
// isComposing is surfaced on the native event during IME composition.
|
|
182
|
+
fireEvent.keyDown(textbox, {key: 'Enter', isComposing: true});
|
|
183
|
+
expect(onSubmit).not.toHaveBeenCalled();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('does not submit on Enter for the legacy keyCode 229 composing signal', () => {
|
|
187
|
+
const onSubmit = vi.fn();
|
|
188
|
+
render(<ChatComposerInput onSubmit={onSubmit} />);
|
|
189
|
+
const textbox = screen.getByRole('textbox');
|
|
190
|
+
textbox.textContent = 'ㅎ';
|
|
191
|
+
fireEvent.input(textbox);
|
|
192
|
+
fireEvent.keyDown(textbox, {key: 'Enter', keyCode: 229});
|
|
193
|
+
expect(onSubmit).not.toHaveBeenCalled();
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('lets onKeyDown suppress the default submit via preventDefault (touch-newline recipe)', () => {
|
|
197
|
+
// The documented "insert a newline instead of sending" pattern: a
|
|
198
|
+
// consumer preventDefaults Enter (e.g. on a coarse pointer).
|
|
199
|
+
const onSubmit = vi.fn();
|
|
200
|
+
const onKeyDown = vi.fn(e => {
|
|
201
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
202
|
+
e.preventDefault();
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
render(<ChatComposerInput onSubmit={onSubmit} onKeyDown={onKeyDown} />);
|
|
206
|
+
const textbox = screen.getByRole('textbox');
|
|
207
|
+
textbox.textContent = 'hello';
|
|
208
|
+
fireEvent.input(textbox);
|
|
209
|
+
fireEvent.keyDown(textbox, {key: 'Enter'});
|
|
210
|
+
expect(onKeyDown).toHaveBeenCalled();
|
|
211
|
+
expect(onSubmit).not.toHaveBeenCalled();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('lets onKeyDown add behavior (Cmd/Ctrl+Enter submit) without preventDefault', () => {
|
|
215
|
+
// Adding a submit shortcut is just handling the event yourself; the
|
|
216
|
+
// built-in Enter handling still runs for the plain-Enter case.
|
|
217
|
+
const onSubmit = vi.fn();
|
|
218
|
+
const handle = vi.fn();
|
|
219
|
+
const onKeyDown = vi.fn(e => {
|
|
220
|
+
if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {
|
|
221
|
+
handle();
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
render(<ChatComposerInput onSubmit={onSubmit} onKeyDown={onKeyDown} />);
|
|
225
|
+
const textbox = screen.getByRole('textbox');
|
|
226
|
+
textbox.textContent = 'hello';
|
|
227
|
+
fireEvent.input(textbox);
|
|
228
|
+
fireEvent.keyDown(textbox, {key: 'Enter', metaKey: true});
|
|
229
|
+
expect(handle).toHaveBeenCalled();
|
|
230
|
+
// Consumer did not preventDefault, so the built-in submit also fires.
|
|
231
|
+
expect(onSubmit).toHaveBeenCalledWith('hello');
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('calls onKeyDown before submit and lets preventDefault take over', () => {
|
|
235
|
+
const onSubmit = vi.fn();
|
|
236
|
+
const onKeyDown = vi.fn(e => {
|
|
237
|
+
e.preventDefault();
|
|
238
|
+
});
|
|
239
|
+
render(<ChatComposerInput onSubmit={onSubmit} onKeyDown={onKeyDown} />);
|
|
240
|
+
const textbox = screen.getByRole('textbox');
|
|
241
|
+
textbox.textContent = 'hello';
|
|
242
|
+
fireEvent.input(textbox);
|
|
243
|
+
fireEvent.keyDown(textbox, {key: 'Enter'});
|
|
244
|
+
expect(onKeyDown).toHaveBeenCalled();
|
|
245
|
+
expect(onSubmit).not.toHaveBeenCalled();
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('calls onKeyDown but still submits when the consumer does not preventDefault', () => {
|
|
249
|
+
const onSubmit = vi.fn();
|
|
250
|
+
const onKeyDown = vi.fn();
|
|
251
|
+
render(<ChatComposerInput onSubmit={onSubmit} onKeyDown={onKeyDown} />);
|
|
252
|
+
const textbox = screen.getByRole('textbox');
|
|
253
|
+
textbox.textContent = 'hello';
|
|
254
|
+
fireEvent.input(textbox);
|
|
255
|
+
fireEvent.keyDown(textbox, {key: 'Enter'});
|
|
256
|
+
expect(onKeyDown).toHaveBeenCalled();
|
|
257
|
+
expect(onSubmit).toHaveBeenCalledWith('hello');
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
158
261
|
// Controlled-value sync used to overwrite `textContent` on every
|
|
159
262
|
// distinct render, which (a) collapsed the caret to offset 0
|
|
160
263
|
// (visible after a slash-command pick like
|
|
@@ -11,8 +11,10 @@
|
|
|
11
11
|
*
|
|
12
12
|
* ContentEditable-based rich input for the chat composer.
|
|
13
13
|
* Supports trigger menus (@ mentions, / commands) via SearchSource,
|
|
14
|
-
* inline token rendering, serialization, Enter
|
|
15
|
-
*
|
|
14
|
+
* inline token rendering, serialization, Enter-to-submit with
|
|
15
|
+
* IME-composition guarding and an onKeyDown seam for platform-specific
|
|
16
|
+
* key handling, message history, paste/drop file handling, and
|
|
17
|
+
* mobile-safe touch typography.
|
|
16
18
|
*
|
|
17
19
|
*
|
|
18
20
|
* SYNC: When modified, update:
|
|
@@ -188,6 +190,21 @@ export interface ChatComposerInputProps extends Omit<
|
|
|
188
190
|
onFiles?: (files: File[]) => void;
|
|
189
191
|
/** Submit handler (Enter without Shift) */
|
|
190
192
|
onSubmit?: (value: string) => void;
|
|
193
|
+
/**
|
|
194
|
+
* Key-down handler invoked before the built-in Enter/history behavior
|
|
195
|
+
* (but after an open trigger menu consumes the event).
|
|
196
|
+
*
|
|
197
|
+
* This is the seam for platform- or app-specific key handling:
|
|
198
|
+
* - Call `event.preventDefault()` to suppress the default submit (e.g.
|
|
199
|
+
* let Enter insert a newline on a touch keyboard).
|
|
200
|
+
* - Add behavior by acting on the event yourself (e.g. submit on
|
|
201
|
+
* Cmd/Ctrl+Enter) without calling `preventDefault()`, so the default
|
|
202
|
+
* handling still runs for other keys.
|
|
203
|
+
*
|
|
204
|
+
* IME composition is always respected regardless of this handler: Enter
|
|
205
|
+
* never submits while a composition is in progress.
|
|
206
|
+
*/
|
|
207
|
+
onKeyDown?: (event: KeyboardEvent<HTMLDivElement>) => void;
|
|
191
208
|
}
|
|
192
209
|
|
|
193
210
|
// =============================================================================
|
|
@@ -299,6 +316,7 @@ export function ChatComposerInput(props: ChatComposerInputProps) {
|
|
|
299
316
|
pasteAsToken: pasteAsTokenProp,
|
|
300
317
|
onFiles,
|
|
301
318
|
onSubmit = composerCtx?.onSubmit,
|
|
319
|
+
onKeyDown: onKeyDownProp,
|
|
302
320
|
xstyle,
|
|
303
321
|
className,
|
|
304
322
|
style,
|
|
@@ -366,6 +384,20 @@ export function ChatComposerInput(props: ChatComposerInputProps) {
|
|
|
366
384
|
selfRef.current = handle;
|
|
367
385
|
useImperativeHandle(handleRef, () => handle);
|
|
368
386
|
|
|
387
|
+
// Register a focus control with the composer shell so body-click-to-focus
|
|
388
|
+
// works without the shell sniffing the input's DOM shape. Cleared on
|
|
389
|
+
// unmount so the shell falls back cleanly if the input goes away.
|
|
390
|
+
const inputControlRef = composerCtx?.inputControlRef;
|
|
391
|
+
useEffect(() => {
|
|
392
|
+
if (!inputControlRef) {
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
inputControlRef.current = {focus: () => editableRef.current?.focus()};
|
|
396
|
+
return () => {
|
|
397
|
+
inputControlRef.current = null;
|
|
398
|
+
};
|
|
399
|
+
}, [inputControlRef]);
|
|
400
|
+
|
|
369
401
|
useEffect(() => {
|
|
370
402
|
if (controlledValue === undefined || !editableRef.current) {
|
|
371
403
|
return;
|
|
@@ -477,6 +509,13 @@ export function ChatComposerInput(props: ChatComposerInputProps) {
|
|
|
477
509
|
return;
|
|
478
510
|
}
|
|
479
511
|
|
|
512
|
+
// Consumer passthrough — runs before built-in Enter/history handling.
|
|
513
|
+
// A consumer can preventDefault() to fully own the keystroke.
|
|
514
|
+
onKeyDownProp?.(e);
|
|
515
|
+
if (e.defaultPrevented) {
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
|
|
480
519
|
// Handle Backspace near tokens — prevent browser from creating
|
|
481
520
|
// stray <br> elements or moving the cursor unexpectedly.
|
|
482
521
|
if (e.key === 'Backspace') {
|
|
@@ -519,6 +558,13 @@ export function ChatComposerInput(props: ChatComposerInputProps) {
|
|
|
519
558
|
}
|
|
520
559
|
|
|
521
560
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
561
|
+
// Never submit mid-composition — an IME uses Enter to commit a
|
|
562
|
+
// candidate (e.g. Japanese/Chinese/Korean input), and browsers may
|
|
563
|
+
// also surface the legacy keyCode 229 for composing keystrokes.
|
|
564
|
+
if (e.nativeEvent.isComposing || e.nativeEvent.keyCode === 229) {
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
|
|
522
568
|
e.preventDefault();
|
|
523
569
|
if (!editableRef.current) {
|
|
524
570
|
return;
|
|
@@ -583,7 +629,7 @@ export function ChatComposerInput(props: ChatComposerInputProps) {
|
|
|
583
629
|
}
|
|
584
630
|
}
|
|
585
631
|
},
|
|
586
|
-
[hasHistory, onSubmit, onChange, emitChange, triggerMenu],
|
|
632
|
+
[hasHistory, onSubmit, onChange, emitChange, triggerMenu, onKeyDownProp],
|
|
587
633
|
);
|
|
588
634
|
|
|
589
635
|
const handlePaste = useCallback(
|