@astryxdesign/core 0.3.0 → 0.4.0
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 +156 -0
- package/dist/AlertDialog/AlertDialog.d.ts +10 -2
- package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
- package/dist/AlertDialog/AlertDialog.js +22 -3
- package/dist/AppShell/AppShell.d.ts +2 -2
- package/dist/AppShell/AppShell.d.ts.map +1 -1
- package/dist/AppShell/AppShell.js +40 -9
- package/dist/AspectRatio/AspectRatio.d.ts +13 -0
- package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
- package/dist/AspectRatio/AspectRatio.js +9 -0
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +33 -20
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.js +2 -3
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +15 -14
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +21 -8
- package/dist/Breadcrumbs/index.d.ts +2 -1
- package/dist/Breadcrumbs/index.d.ts.map +1 -1
- package/dist/Breadcrumbs/index.js +1 -0
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +11 -15
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +2 -1
- package/dist/Calendar/styles.d.ts +0 -2
- package/dist/Calendar/styles.d.ts.map +1 -1
- package/dist/Calendar/styles.js +0 -2
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +8 -12
- package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
- package/dist/Chat/ChatDictationButton.js +2 -1
- package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
- package/dist/Chat/ChatLayoutScrollButton.js +3 -1
- package/dist/Chat/ChatSendButton.d.ts.map +1 -1
- package/dist/Chat/ChatSendButton.js +4 -3
- package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.js +17 -17
- package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
- package/dist/Chat/useTriggerMenu.js +2 -6
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +36 -125
- package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
- package/dist/ClickableCard/ClickableCard.js +3 -11
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +84 -67
- package/dist/Collapsible/Collapsible.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.js +35 -15
- package/dist/CommandPalette/CommandPaletteItem.d.ts +1 -1
- package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteItem.js +6 -3
- package/dist/CommandPalette/CommandPaletteList.js +1 -1
- package/dist/ComplexSelector/ComplexSelector.d.ts +1 -1
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +35 -33
- package/dist/ContextMenu/ContextMenu.d.ts +2 -2
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +1 -1
- package/dist/ContextMenu/index.d.ts +2 -1
- package/dist/ContextMenu/index.d.ts.map +1 -1
- package/dist/ContextMenu/index.js +1 -0
- package/dist/DateInput/DateInput.d.ts +9 -2
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +24 -23
- package/dist/DateRangeInput/DateRangeInput.d.ts +9 -2
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +11 -16
- package/dist/DateTimeInput/DateTimeInput.d.ts +9 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +22 -17
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +4 -4
- package/dist/DropdownMenu/DropdownMenu.d.ts +33 -8
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +62 -20
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +8 -11
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +32 -31
- package/dist/DropdownMenu/DropdownMenuDivider.d.ts +25 -0
- package/dist/DropdownMenu/DropdownMenuDivider.d.ts.map +1 -0
- package/dist/DropdownMenu/DropdownMenuDivider.js +66 -0
- package/dist/DropdownMenu/DropdownMenuItem.d.ts +15 -2
- package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuItem.js +20 -8
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +5 -5
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioItem.js +24 -67
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.js +2 -8
- package/dist/DropdownMenu/index.d.ts +2 -1
- package/dist/DropdownMenu/index.d.ts.map +1 -1
- package/dist/DropdownMenu/index.js +4 -0
- package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
- package/dist/DropdownMenu/renderDropdownItems.js +35 -27
- package/dist/EmptyState/EmptyState.d.ts.map +1 -1
- package/dist/EmptyState/EmptyState.js +9 -5
- package/dist/Field/InputClearButton.d.ts +16 -3
- package/dist/Field/InputClearButton.d.ts.map +1 -1
- package/dist/Field/InputClearButton.js +15 -4
- package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +3 -19
- package/dist/Indicator/CheckIndicator.d.ts +25 -0
- package/dist/Indicator/CheckIndicator.d.ts.map +1 -0
- package/dist/Indicator/CheckIndicator.js +155 -0
- package/dist/Indicator/CheckboxIndicator.d.ts +22 -0
- package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -0
- package/dist/Indicator/CheckboxIndicator.js +173 -0
- package/dist/Indicator/RadioIndicator.d.ts +23 -0
- package/dist/Indicator/RadioIndicator.d.ts.map +1 -0
- package/dist/Indicator/RadioIndicator.js +137 -0
- package/dist/Indicator/index.d.ts +24 -0
- package/dist/Indicator/index.d.ts.map +1 -0
- package/dist/Indicator/index.js +26 -0
- package/dist/Indicator/indicator.markers.stylex.d.ts +16 -0
- package/dist/Indicator/indicator.markers.stylex.d.ts.map +1 -0
- package/dist/Indicator/indicator.markers.stylex.js +21 -0
- package/dist/Indicator/indicatorRegistry.d.ts +71 -0
- package/dist/Indicator/indicatorRegistry.d.ts.map +1 -0
- package/dist/Indicator/indicatorRegistry.js +93 -0
- package/dist/Indicator/types.d.ts +167 -0
- package/dist/Indicator/types.d.ts.map +1 -0
- package/dist/Indicator/types.js +1 -0
- package/dist/Indicator/useIndicator.d.ts +18 -0
- package/dist/Indicator/useIndicator.d.ts.map +1 -0
- package/dist/Indicator/useIndicator.js +30 -0
- package/dist/Item/Item.d.ts.map +1 -1
- package/dist/Item/Item.js +4 -8
- package/dist/Layer/useLayer.d.ts +14 -0
- package/dist/Layer/useLayer.d.ts.map +1 -1
- package/dist/Layer/useLayer.js +25 -2
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +56 -53
- package/dist/Link/Link.d.ts.map +1 -1
- package/dist/Link/Link.js +4 -5
- package/dist/MobileNav/MobileNav.js +8 -8
- package/dist/MoreMenu/MoreMenu.d.ts +14 -1
- package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
- package/dist/MoreMenu/MoreMenu.js +4 -0
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +78 -75
- package/dist/NumberInput/NumberInput.d.ts +27 -1
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +230 -42
- package/dist/Outline/Outline.d.ts.map +1 -1
- package/dist/Outline/Outline.js +2 -3
- package/dist/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Pagination/Pagination.js +61 -63
- package/dist/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +6 -8
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +1 -1
- package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchEditPopover.js +7 -0
- package/dist/ProgressBar/ProgressBar.d.ts +11 -4
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +96 -6
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +29 -84
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +2 -3
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +2 -3
- package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
- package/dist/SelectableCard/SelectableCard.js +3 -11
- package/dist/Selector/Selector.d.ts +2 -1
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +164 -94
- package/dist/Selector/hooks.d.ts +18 -6
- package/dist/Selector/hooks.d.ts.map +1 -1
- package/dist/Selector/hooks.js +57 -40
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +1 -1
- package/dist/SideNav/SideNavCollapseButton.d.ts +1 -1
- package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
- package/dist/SideNav/SideNavCollapseButton.js +29 -14
- package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
- package/dist/SideNav/SideNavHeading.js +56 -20
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +28 -22
- package/dist/Stack/stack.stylex.d.ts +3 -3
- package/dist/Switch/Switch.js +11 -10
- package/dist/TabList/Tab.d.ts.map +1 -1
- package/dist/TabList/Tab.js +2 -3
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +41 -19
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +8 -0
- package/dist/Table/index.d.ts +1 -1
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +1 -1
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts +1 -1
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +22 -18
- package/dist/Table/plugins/rowExpansion/index.d.ts +1 -1
- package/dist/Table/plugins/rowExpansion/index.d.ts.map +1 -1
- package/dist/Table/plugins/rowExpansion/index.js +1 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts +41 -76
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +94 -345
- package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortable.js +23 -4
- package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
- package/dist/Table/plugins/tree/useTableTreeData.js +29 -33
- package/dist/Table/tableContextMenu.d.ts.map +1 -1
- package/dist/Table/tableContextMenu.js +2 -1
- package/dist/Table/types.d.ts +11 -0
- package/dist/Table/types.d.ts.map +1 -1
- package/dist/TextArea/TextArea.d.ts +10 -1
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +16 -6
- package/dist/TextInput/TextInput.d.ts +10 -1
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +13 -19
- package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
- package/dist/Thumbnail/Thumbnail.js +32 -26
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +7 -15
- package/dist/Timestamp/TimestampHoverCard.d.ts.map +1 -1
- package/dist/Timestamp/TimestampHoverCard.js +16 -34
- package/dist/Token/Token.d.ts.map +1 -1
- package/dist/Token/Token.js +20 -11
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +4 -2
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +3 -19
- package/dist/TopNav/TopNav.d.ts.map +1 -1
- package/dist/TopNav/TopNav.js +6 -5
- package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
- package/dist/TopNav/TopNavHeading.js +54 -16
- package/dist/TopNav/TopNavItem.d.ts.map +1 -1
- package/dist/TopNav/TopNavItem.js +3 -9
- package/dist/TopNav/TopNavMegaMenu.d.ts +7 -0
- package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenu.js +146 -60
- package/dist/TopNav/TopNavMegaMenuItem.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenuItem.js +22 -3
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +83 -38
- package/dist/TreeList/TreeList.d.ts.map +1 -1
- package/dist/TreeList/TreeList.js +13 -0
- package/dist/TreeList/TreeListBranches.d.ts +1 -1
- package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
- package/dist/TreeList/TreeListBranches.js +14 -4
- package/dist/TreeList/TreeListItem.d.ts +11 -1
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +57 -30
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +16 -8
- package/dist/astryx.css +74 -85
- package/dist/astryx.umd.js +56 -56
- package/dist/astryx.umd.js.map +4 -4
- package/dist/hooks/containerReveal.stylex.d.ts +61 -0
- package/dist/hooks/containerReveal.stylex.d.ts.map +1 -0
- package/dist/hooks/containerReveal.stylex.js +63 -0
- package/dist/hooks/index.d.ts +3 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/useClipboard.d.ts +59 -0
- package/dist/hooks/useClipboard.d.ts.map +1 -0
- package/dist/hooks/useClipboard.js +106 -0
- package/dist/hooks/useContainerReveal.d.ts +14 -8
- package/dist/hooks/useContainerReveal.d.ts.map +1 -1
- package/dist/hooks/useContainerReveal.js +28 -69
- package/dist/hooks/useIndicatorFocusRing.d.ts +28 -0
- package/dist/hooks/useIndicatorFocusRing.d.ts.map +1 -0
- package/dist/hooks/useIndicatorFocusRing.js +96 -0
- package/dist/hooks/useLongPress.d.ts.map +1 -1
- package/dist/hooks/useLongPress.js +12 -2
- package/dist/hooks/useScrollLock.d.ts.map +1 -1
- package/dist/hooks/useScrollLock.js +33 -18
- package/dist/hooks/useStreamingText.d.ts +18 -3
- package/dist/hooks/useStreamingText.d.ts.map +1 -1
- package/dist/hooks/useStreamingText.js +47 -7
- package/dist/hooks/useTreeFocus.d.ts.map +1 -1
- package/dist/hooks/useTreeFocus.js +9 -3
- package/dist/hooks/useTypeahead.d.ts +3 -2
- package/dist/hooks/useTypeahead.d.ts.map +1 -1
- package/dist/hooks/useTypeahead.js +19 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/theme/defineTheme.d.ts +16 -0
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/defineTheme.js +8 -0
- package/dist/theme/derivedVarRegistry.d.ts +8 -0
- package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
- package/dist/theme/derivedVarRegistry.js +14 -13
- package/dist/theme/expandColorScale.d.ts +3 -1
- package/dist/theme/expandColorScale.d.ts.map +1 -1
- package/dist/theme/expandColorScale.js +17 -7
- package/dist/theme/expandTypeScale.d.ts.map +1 -1
- package/dist/theme/expandTypeScale.js +17 -0
- package/dist/theme/generateThemeRules.d.ts.map +1 -1
- package/dist/theme/generateThemeRules.js +11 -0
- package/dist/theme/index.d.ts +1 -0
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/utils/focusOutline.stylex.d.ts +107 -0
- package/dist/utils/focusOutline.stylex.d.ts.map +1 -0
- package/dist/utils/focusOutline.stylex.js +130 -0
- package/dist/utils/focusReturn.d.ts +24 -0
- package/dist/utils/focusReturn.d.ts.map +1 -0
- package/dist/utils/focusReturn.js +29 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/mergeRefs.d.ts.map +1 -1
- package/dist/utils/mergeRefs.js +7 -5
- package/dist/utils/rtlStyles.d.ts +2 -2
- package/dist/utils/rtlStyles.js +2 -2
- package/dist/utils/themeProps.d.ts +20 -1
- package/dist/utils/themeProps.d.ts.map +1 -1
- package/dist/utils/themeProps.js +8 -2
- package/locales/en.json +12 -0
- package/locales/pseudo.json +9 -0
- package/package.json +7 -2
- package/src/AlertDialog/AlertDialog.doc.mjs +16 -3
- package/src/AlertDialog/AlertDialog.test.tsx +208 -1
- package/src/AlertDialog/AlertDialog.tsx +21 -3
- package/src/AppShell/AppShell.doc.mjs +11 -0
- package/src/AppShell/AppShell.test.tsx +139 -0
- package/src/AppShell/AppShell.tsx +32 -22
- package/src/AspectRatio/AspectRatio.doc.mjs +27 -3
- package/src/AspectRatio/AspectRatio.test.tsx +30 -0
- package/src/AspectRatio/AspectRatio.tsx +13 -0
- package/src/Avatar/Avatar.doc.mjs +2 -13
- package/src/Avatar/Avatar.test.tsx +33 -8
- package/src/Avatar/Avatar.tsx +52 -50
- package/src/AvatarGroup/AvatarGroupOverflow.tsx +2 -9
- package/src/Banner/Banner.tsx +9 -6
- package/src/Breadcrumbs/BreadcrumbItem.tsx +16 -6
- package/src/Breadcrumbs/index.ts +5 -1
- package/src/Button/Button.tsx +21 -41
- package/src/Calendar/Calendar.tsx +2 -1
- package/src/Calendar/styles.ts +0 -8
- package/src/Carousel/Carousel.doc.mjs +3 -0
- package/src/Carousel/Carousel.tsx +10 -6
- package/src/Chat/Chat.doc.mjs +2 -0
- package/src/Chat/ChatDictationButton.tsx +7 -1
- package/src/Chat/ChatLayoutScrollButton.test.tsx +22 -0
- package/src/Chat/ChatLayoutScrollButton.tsx +8 -1
- package/src/Chat/ChatSendButton.tsx +2 -3
- package/src/Chat/ChatToolCalls.test.tsx +35 -0
- package/src/Chat/ChatToolCalls.tsx +36 -16
- package/src/Chat/useTriggerMenu.tsx +2 -5
- package/src/CheckboxInput/CheckboxInput.doc.mjs +4 -2
- package/src/CheckboxInput/CheckboxInput.test.tsx +115 -0
- package/src/CheckboxInput/CheckboxInput.tsx +45 -202
- package/src/ClickableCard/ClickableCard.tsx +6 -11
- package/src/CodeBlock/CodeBlock.doc.mjs +3 -0
- package/src/CodeBlock/CodeBlock.test.tsx +91 -0
- package/src/CodeBlock/CodeBlock.tsx +57 -80
- package/src/Collapsible/Collapsible.tsx +22 -18
- package/src/CommandPalette/CommandPalette.test.tsx +29 -0
- package/src/CommandPalette/CommandPaletteItem.tsx +7 -4
- package/src/CommandPalette/CommandPaletteList.tsx +2 -2
- package/src/ComplexSelector/ComplexSelector.doc.mjs +42 -0
- package/src/ComplexSelector/ComplexSelector.test.tsx +64 -0
- package/src/ComplexSelector/ComplexSelector.tsx +46 -29
- package/src/ContextMenu/ContextMenu.doc.mjs +1 -1
- package/src/ContextMenu/ContextMenu.test.tsx +41 -0
- package/src/ContextMenu/ContextMenu.tsx +3 -3
- package/src/ContextMenu/index.ts +5 -1
- package/src/DateInput/DateInput.doc.mjs +18 -3
- package/src/DateInput/DateInput.test.tsx +93 -11
- package/src/DateInput/DateInput.tsx +41 -19
- package/src/DateRangeInput/DateRangeInput.doc.mjs +10 -2
- package/src/DateRangeInput/DateRangeInput.test.tsx +110 -14
- package/src/DateRangeInput/DateRangeInput.tsx +24 -12
- package/src/DateTimeInput/DateTimeInput.doc.mjs +16 -2
- package/src/DateTimeInput/DateTimeInput.test.tsx +75 -0
- package/src/DateTimeInput/DateTimeInput.tsx +35 -8
- package/src/Dialog/Dialog.tsx +2 -9
- package/src/DropdownMenu/DropdownMenu.doc.mjs +5 -9
- package/src/DropdownMenu/DropdownMenu.test.tsx +534 -4
- package/src/DropdownMenu/DropdownMenu.tsx +113 -52
- package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +27 -30
- package/src/DropdownMenu/DropdownMenuDivider.doc.mjs +34 -0
- package/src/DropdownMenu/DropdownMenuDivider.tsx +81 -0
- package/src/DropdownMenu/DropdownMenuItem.doc.mjs +39 -4
- package/src/DropdownMenu/DropdownMenuItem.tsx +48 -10
- package/src/DropdownMenu/DropdownMenuRadioItem.tsx +30 -76
- package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +25 -19
- package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +44 -14
- package/src/DropdownMenu/DropdownMenuSubMenu.tsx +1 -5
- package/src/DropdownMenu/index.ts +8 -1
- package/src/DropdownMenu/renderDropdownItems.tsx +33 -34
- package/src/EmptyState/EmptyState.doc.mjs +4 -0
- package/src/EmptyState/EmptyState.test.tsx +62 -0
- package/src/EmptyState/EmptyState.tsx +16 -6
- package/src/Field/Field.doc.mjs +1 -0
- package/src/Field/InputClearButton.test.tsx +117 -0
- package/src/Field/InputClearButton.tsx +30 -3
- package/src/HoverCard/useHoverCard.tsx +2 -23
- package/src/Icon/Icon.doc.mjs +2 -1
- package/src/Indicator/CheckIndicator.tsx +174 -0
- package/src/Indicator/CheckboxIndicator.tsx +257 -0
- package/src/Indicator/Indicator.doc.mjs +344 -0
- package/src/Indicator/Indicator.test.tsx +393 -0
- package/src/Indicator/RadioIndicator.tsx +206 -0
- package/src/Indicator/index.ts +45 -0
- package/src/Indicator/indicator.markers.stylex.ts +19 -0
- package/src/Indicator/indicatorRegistry.test.tsx +106 -0
- package/src/Indicator/indicatorRegistry.ts +120 -0
- package/src/Indicator/types.ts +186 -0
- package/src/Indicator/useIndicator.ts +39 -0
- package/src/Item/Item.doc.mjs +1 -1
- package/src/Item/Item.tsx +7 -14
- package/src/Layer/useLayer.doc.mjs +2 -2
- package/src/Layer/useLayer.test.tsx +85 -0
- package/src/Layer/useLayer.tsx +39 -1
- package/src/Lightbox/Lightbox.doc.mjs +1 -1
- package/src/Lightbox/Lightbox.tsx +43 -38
- package/src/Link/Link.tsx +4 -11
- package/src/MobileNav/MobileNav.tsx +3 -3
- package/src/MoreMenu/MoreMenu.doc.mjs +31 -0
- package/src/MoreMenu/MoreMenu.test.tsx +68 -1
- package/src/MoreMenu/MoreMenu.tsx +19 -0
- package/src/MultiSelector/MultiSelector.doc.mjs +9 -1
- package/src/MultiSelector/MultiSelector.test.tsx +255 -16
- package/src/MultiSelector/MultiSelector.tsx +103 -99
- package/src/NumberInput/NumberInput.doc.mjs +265 -50
- package/src/NumberInput/NumberInput.test.tsx +726 -21
- package/src/NumberInput/NumberInput.tsx +353 -59
- package/src/Outline/Outline.tsx +2 -5
- package/src/Pagination/Pagination.doc.mjs +1 -1
- package/src/Pagination/Pagination.test.tsx +22 -7
- package/src/Pagination/Pagination.tsx +38 -35
- package/src/Popover/Popover.tsx +6 -7
- package/src/PowerSearch/PowerSearch.tsx +1 -1
- package/src/PowerSearch/PowerSearchEditPopover.test.tsx +42 -0
- package/src/PowerSearch/PowerSearchEditPopover.tsx +7 -0
- package/src/ProgressBar/ProgressBar.doc.mjs +27 -5
- package/src/ProgressBar/ProgressBar.test.tsx +224 -1
- package/src/ProgressBar/ProgressBar.tsx +124 -22
- package/src/RadioList/RadioList.doc.mjs +4 -2
- package/src/RadioList/RadioListItem.tsx +31 -130
- package/src/Resizable/ResizeHandle.tsx +2 -9
- package/src/SegmentedControl/SegmentedControlItem.tsx +2 -9
- package/src/SelectableCard/SelectableCard.tsx +4 -9
- package/src/Selector/Selector.doc.mjs +7 -4
- package/src/Selector/Selector.test.tsx +935 -25
- package/src/Selector/Selector.tsx +189 -106
- package/src/Selector/hooks.ts +77 -50
- package/src/SideNav/SideNav.test.tsx +46 -0
- package/src/SideNav/SideNav.tsx +1 -0
- package/src/SideNav/SideNavCollapseButton.tsx +15 -10
- package/src/SideNav/SideNavHeading.tsx +48 -10
- package/src/SideNav/SideNavItem.tsx +20 -12
- package/src/Stack/Stack.doc.mjs +110 -0
- package/src/StatusDot/StatusDot.doc.mjs +12 -3
- package/src/Switch/Switch.test.tsx +32 -0
- package/src/Switch/Switch.tsx +8 -8
- package/src/TabList/Tab.tsx +2 -9
- package/src/TabList/TabMenu.tsx +14 -21
- package/src/Table/BaseTable.tsx +11 -0
- package/src/Table/index.ts +1 -4
- package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +30 -14
- package/src/Table/plugins/rowExpansion/index.ts +1 -4
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +125 -175
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +115 -458
- package/src/Table/plugins/sortable/useTableSortable.tsx +2 -6
- package/src/Table/plugins/tree/useTableTreeData.tsx +43 -24
- package/src/Table/tableContextMenu.test.tsx +27 -0
- package/src/Table/tableContextMenu.tsx +1 -0
- package/src/Table/types.ts +11 -0
- package/src/Table/useTableGroupedRows.doc.mjs +1 -1
- package/src/Table/useTableRowExpansion.doc.mjs +43 -66
- package/src/TextArea/TextArea.doc.mjs +42 -2
- package/src/TextArea/TextArea.test.tsx +219 -0
- package/src/TextArea/TextArea.tsx +34 -7
- package/src/TextInput/TextInput.doc.mjs +18 -2
- package/src/TextInput/TextInput.test.tsx +189 -0
- package/src/TextInput/TextInput.tsx +25 -31
- package/src/Thumbnail/Thumbnail.tsx +2 -9
- package/src/TimeInput/TimeInput.doc.mjs +2 -2
- package/src/TimeInput/TimeInput.test.tsx +30 -3
- package/src/TimeInput/TimeInput.tsx +9 -26
- package/src/Timestamp/Timestamp.doc.mjs +4 -3
- package/src/Timestamp/TimestampHoverCard.tsx +13 -35
- package/src/Token/Token.tsx +5 -28
- package/src/Tokenizer/Tokenizer.doc.mjs +2 -2
- package/src/Tokenizer/Tokenizer.test.tsx +45 -3
- package/src/Tokenizer/Tokenizer.tsx +10 -2
- package/src/Tooltip/useTooltip.tsx +2 -23
- package/src/TopNav/TopNav.tsx +1 -3
- package/src/TopNav/TopNavHeading.tsx +42 -13
- package/src/TopNav/TopNavItem.tsx +3 -21
- package/src/TopNav/TopNavMegaMenu.test.tsx +433 -33
- package/src/TopNav/TopNavMegaMenu.tsx +118 -53
- package/src/TopNav/TopNavMegaMenuItem.tsx +2 -9
- package/src/TopNav/TopNavMenu.tsx +34 -37
- package/src/TreeList/TreeList.doc.mjs +5 -0
- package/src/TreeList/TreeList.test.tsx +206 -7
- package/src/TreeList/TreeList.tsx +22 -0
- package/src/TreeList/TreeListBranches.tsx +25 -2
- package/src/TreeList/TreeListItem.tsx +89 -52
- package/src/Typeahead/BaseTypeahead.tsx +19 -6
- package/src/Typeahead/Typeahead.doc.mjs +1 -0
- package/src/Typeahead/Typeahead.test.tsx +76 -3
- package/src/hooks/containerReveal.stylex.ts +102 -0
- package/src/hooks/index.ts +4 -0
- package/src/hooks/useClipboard.doc.mjs +87 -0
- package/src/hooks/useClipboard.test.tsx +151 -0
- package/src/hooks/useClipboard.ts +135 -0
- package/src/hooks/useContainerReveal.doc.mjs +7 -7
- package/src/hooks/useContainerReveal.test.tsx +42 -62
- package/src/hooks/useContainerReveal.ts +36 -91
- package/src/hooks/useImageMode.test.ts +1 -1
- package/src/hooks/useIndicatorFocusRing.tsx +120 -0
- package/src/hooks/useLongPress.test.tsx +18 -0
- package/src/hooks/useLongPress.ts +12 -2
- package/src/hooks/useScrollLock.test.ts +72 -0
- package/src/hooks/useScrollLock.ts +51 -19
- package/src/hooks/useStreamingText.test.ts +46 -1
- package/src/hooks/useStreamingText.ts +49 -7
- package/src/hooks/useTreeFocus.test.tsx +50 -0
- package/src/hooks/useTreeFocus.ts +14 -3
- package/src/hooks/useTypeahead.test.tsx +150 -0
- package/src/hooks/useTypeahead.ts +24 -9
- package/src/i18n/InternationalizationProvider.doc.mjs +7 -2
- package/src/index.ts +1 -0
- package/src/theme/defineTheme.ts +24 -0
- package/src/theme/derivedVarRegistry.test.ts +21 -1
- package/src/theme/derivedVarRegistry.ts +19 -6
- package/src/theme/expandColorScale.test.ts +14 -0
- package/src/theme/expandColorScale.ts +24 -9
- package/src/theme/expandTypeScale.test.ts +43 -0
- package/src/theme/expandTypeScale.ts +17 -0
- package/src/theme/generateThemeRules.test.ts +61 -20
- package/src/theme/generateThemeRules.ts +11 -0
- package/src/theme/index.ts +13 -0
- package/src/utils/focusOutline.stylex.ts +163 -0
- package/src/utils/focusReturn.test.ts +42 -0
- package/src/utils/focusReturn.ts +31 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/mergeRefs.test.ts +48 -0
- package/src/utils/mergeRefs.ts +12 -6
- package/src/utils/rtlStyles.ts +2 -2
- package/src/utils/themeProps.ts +26 -4
- package/dist/CheckboxInput/checkbox.markers.stylex.d.ts +0 -9
- package/dist/CheckboxInput/checkbox.markers.stylex.d.ts.map +0 -1
- package/dist/CheckboxInput/checkbox.markers.stylex.js +0 -14
- package/dist/RadioList/radio.markers.stylex.d.ts +0 -7
- package/dist/RadioList/radio.markers.stylex.d.ts.map +0 -1
- package/dist/RadioList/radio.markers.stylex.js +0 -12
- package/dist/hooks/containerReveal.pool.stylex.d.ts +0 -303
- package/dist/hooks/containerReveal.pool.stylex.d.ts.map +0 -1
- package/dist/hooks/containerReveal.pool.stylex.js +0 -292
- package/src/CheckboxInput/checkbox.markers.stylex.ts +0 -12
- package/src/RadioList/radio.markers.stylex.ts +0 -10
- package/src/hooks/containerReveal.pool.stylex.ts +0 -571
|
@@ -171,6 +171,28 @@ describe('BaseTypeahead', () => {
|
|
|
171
171
|
});
|
|
172
172
|
});
|
|
173
173
|
|
|
174
|
+
it('exposes the empty state as a themeable target', async () => {
|
|
175
|
+
const {container} = render(
|
|
176
|
+
<BaseTypeahead
|
|
177
|
+
searchSource={fruitSource}
|
|
178
|
+
value={null}
|
|
179
|
+
onChange={() => {}}
|
|
180
|
+
debounceMs={0}
|
|
181
|
+
emptySearchResultsText="No results found"
|
|
182
|
+
/>,
|
|
183
|
+
);
|
|
184
|
+
const input = screen.getByRole('combobox');
|
|
185
|
+
fireEvent.change(input, {target: {value: 'zzzzz'}});
|
|
186
|
+
|
|
187
|
+
await waitFor(() => {
|
|
188
|
+
const emptyState = container.querySelector(
|
|
189
|
+
'.astryx-typeahead-empty-state',
|
|
190
|
+
);
|
|
191
|
+
expect(emptyState).not.toBeNull();
|
|
192
|
+
expect(emptyState).toHaveTextContent('No results found');
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
174
196
|
describe('empty results active descendant (#4059)', () => {
|
|
175
197
|
it('does not set aria-activedescendant when search has 0 results', async () => {
|
|
176
198
|
render(
|
|
@@ -198,6 +220,45 @@ describe('BaseTypeahead', () => {
|
|
|
198
220
|
});
|
|
199
221
|
});
|
|
200
222
|
|
|
223
|
+
describe('IME composition guard (#4828)', () => {
|
|
224
|
+
it('does not select the highlighted result on a composing Enter', async () => {
|
|
225
|
+
const onChange = vi.fn();
|
|
226
|
+
render(
|
|
227
|
+
<BaseTypeahead
|
|
228
|
+
searchSource={fruitSource}
|
|
229
|
+
value={null}
|
|
230
|
+
onChange={onChange}
|
|
231
|
+
debounceMs={0}
|
|
232
|
+
/>,
|
|
233
|
+
);
|
|
234
|
+
const input = screen.getByRole('combobox');
|
|
235
|
+
fireEvent.change(input, {target: {value: 'App'}});
|
|
236
|
+
await waitFor(() => {
|
|
237
|
+
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// The browser fires this composing keydown for the Enter that commits
|
|
241
|
+
// an IME candidate (isComposing: true, or legacy keyCode 229) before
|
|
242
|
+
// compositionend writes the pending syllable into the input. Without
|
|
243
|
+
// the guard this both selects the highlighted result AND clears the
|
|
244
|
+
// input via handleSelect, so the syllable that compositionend then
|
|
245
|
+
// writes lands in an emptied field instead of being part of the word.
|
|
246
|
+
fireEvent.keyDown(input, {key: 'Enter', isComposing: true});
|
|
247
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
248
|
+
expect(input).toHaveValue('App');
|
|
249
|
+
|
|
250
|
+
fireEvent.keyDown(input, {key: 'Enter', keyCode: 229});
|
|
251
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
252
|
+
expect(input).toHaveValue('App');
|
|
253
|
+
|
|
254
|
+
// A real, non-composing Enter still selects normally.
|
|
255
|
+
fireEvent.keyDown(input, {key: 'Enter'});
|
|
256
|
+
expect(onChange).toHaveBeenCalledWith(
|
|
257
|
+
expect.objectContaining({label: 'Apple'}),
|
|
258
|
+
);
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
201
262
|
it('disables input when isDisabled', () => {
|
|
202
263
|
render(
|
|
203
264
|
<BaseTypeahead
|
|
@@ -1038,11 +1099,16 @@ describe('Typeahead disabledMessage', () => {
|
|
|
1038
1099
|
});
|
|
1039
1100
|
});
|
|
1040
1101
|
|
|
1041
|
-
|
|
1042
1102
|
describe('Typeahead statusVariant forwarding', () => {
|
|
1043
1103
|
it('defaults to attached (status renders with data-variant="attached")', () => {
|
|
1044
1104
|
const {container} = render(
|
|
1045
|
-
<Typeahead
|
|
1105
|
+
<Typeahead
|
|
1106
|
+
label="Fruit"
|
|
1107
|
+
searchSource={fruitSource}
|
|
1108
|
+
value={null}
|
|
1109
|
+
onChange={() => {}}
|
|
1110
|
+
status={{type: 'error', message: 'Required'}}
|
|
1111
|
+
/>,
|
|
1046
1112
|
);
|
|
1047
1113
|
expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
|
|
1048
1114
|
'data-variant',
|
|
@@ -1052,7 +1118,14 @@ describe('Typeahead statusVariant forwarding', () => {
|
|
|
1052
1118
|
|
|
1053
1119
|
it('forwards statusVariant="detached" to the underlying Field status', () => {
|
|
1054
1120
|
const {container} = render(
|
|
1055
|
-
<Typeahead
|
|
1121
|
+
<Typeahead
|
|
1122
|
+
label="Fruit"
|
|
1123
|
+
searchSource={fruitSource}
|
|
1124
|
+
value={null}
|
|
1125
|
+
onChange={() => {}}
|
|
1126
|
+
status={{type: 'error', message: 'Required'}}
|
|
1127
|
+
statusVariant="detached"
|
|
1128
|
+
/>,
|
|
1056
1129
|
);
|
|
1057
1130
|
expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
|
|
1058
1131
|
'data-variant',
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file containerReveal.stylex.ts
|
|
5
|
+
* @input Uses StyleX, theme tokens
|
|
6
|
+
* @output The container style that publishes the reveal state, and the four
|
|
7
|
+
* content style blocks (reveal / conceal, each with a layout-preserved
|
|
8
|
+
* variant) that read it.
|
|
9
|
+
* @position Internal to useContainerReveal.
|
|
10
|
+
*
|
|
11
|
+
* HOW THE SCOPING WORKS: the container declares its own reveal state as
|
|
12
|
+
* inherited custom properties on itself, and content reads them. Because a
|
|
13
|
+
* nested container re-declares the same properties on itself, its subtree sees
|
|
14
|
+
* the inner value and never the ancestor's — nesting isolation falls out of the
|
|
15
|
+
* cascade, with one static style block for any number of containers.
|
|
16
|
+
*
|
|
17
|
+
* SYNC: When the block shape changes, update useContainerReveal.ts.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import * as stylex from '@stylexjs/stylex';
|
|
21
|
+
import {durationVars, easeVars} from '../theme/tokens.stylex';
|
|
22
|
+
|
|
23
|
+
const REST_DELAY = '0s, ' + durationVars['--duration-fast'];
|
|
24
|
+
|
|
25
|
+
export const styles = stylex.create({
|
|
26
|
+
container: {
|
|
27
|
+
'--_reveal-opacity': {
|
|
28
|
+
default: 0,
|
|
29
|
+
':hover': {'@media (hover: hover)': 1},
|
|
30
|
+
':focus-within': 1,
|
|
31
|
+
'@media (any-pointer: coarse)': 1,
|
|
32
|
+
},
|
|
33
|
+
'--_reveal-position': {
|
|
34
|
+
default: 'absolute',
|
|
35
|
+
':hover': {'@media (hover: hover)': 'static'},
|
|
36
|
+
':focus-within': 'static',
|
|
37
|
+
'@media (any-pointer: coarse)': 'static',
|
|
38
|
+
},
|
|
39
|
+
// The position flip is discrete, so it transitions with allow-discrete and
|
|
40
|
+
// a state-conditional delay: 0 on entry (flips into flow immediately, then
|
|
41
|
+
// fades in) and the fade duration on exit (stays in flow until the fade
|
|
42
|
+
// finishes, then snaps out) — without this the exit would snap out of flow
|
|
43
|
+
// at full opacity and flicker.
|
|
44
|
+
'--_reveal-delay': {
|
|
45
|
+
default: REST_DELAY,
|
|
46
|
+
':hover': {'@media (hover: hover)': '0s, 0s'},
|
|
47
|
+
':focus-within': '0s, 0s',
|
|
48
|
+
'@media (any-pointer: coarse)': '0s, 0s',
|
|
49
|
+
},
|
|
50
|
+
// Conceal is a mouse-only visual swap, so it reads hover alone: no
|
|
51
|
+
// :focus-within (a keyboard user must never watch content vanish) and no
|
|
52
|
+
// coarse-pointer branch (it stays visible on touch).
|
|
53
|
+
'--_conceal-opacity': {
|
|
54
|
+
default: 1,
|
|
55
|
+
':hover': {'@media (hover: hover)': 0},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
// The fallbacks make content spread outside a reveal container fail visible
|
|
59
|
+
// rather than invisible.
|
|
60
|
+
reveal: {
|
|
61
|
+
transitionProperty: 'opacity, position',
|
|
62
|
+
transitionDuration: {
|
|
63
|
+
default: durationVars['--duration-fast'] + ', 0s',
|
|
64
|
+
'@media (prefers-reduced-motion: reduce)': '0s, 0s',
|
|
65
|
+
},
|
|
66
|
+
transitionTimingFunction: easeVars['--ease-standard'],
|
|
67
|
+
transitionBehavior: 'allow-discrete',
|
|
68
|
+
transitionDelay: {
|
|
69
|
+
default: 'var(--_reveal-delay, 0s, 0s)',
|
|
70
|
+
'@media (prefers-reduced-motion: reduce)': '0s, 0s',
|
|
71
|
+
},
|
|
72
|
+
opacity: 'var(--_reveal-opacity, 1)',
|
|
73
|
+
position: 'var(--_reveal-position, static)',
|
|
74
|
+
},
|
|
75
|
+
revealLayoutPreserved: {
|
|
76
|
+
transitionProperty: 'opacity',
|
|
77
|
+
transitionDuration: {
|
|
78
|
+
default: durationVars['--duration-fast'],
|
|
79
|
+
'@media (prefers-reduced-motion: reduce)': '0s',
|
|
80
|
+
},
|
|
81
|
+
transitionTimingFunction: easeVars['--ease-standard'],
|
|
82
|
+
opacity: 'var(--_reveal-opacity, 1)',
|
|
83
|
+
},
|
|
84
|
+
conceal: {
|
|
85
|
+
transitionProperty: 'opacity',
|
|
86
|
+
transitionDuration: {
|
|
87
|
+
default: durationVars['--duration-fast'],
|
|
88
|
+
'@media (prefers-reduced-motion: reduce)': '0s',
|
|
89
|
+
},
|
|
90
|
+
transitionTimingFunction: easeVars['--ease-standard'],
|
|
91
|
+
opacity: 'var(--_conceal-opacity, 1)',
|
|
92
|
+
},
|
|
93
|
+
concealLayoutPreserved: {
|
|
94
|
+
transitionProperty: 'opacity',
|
|
95
|
+
transitionDuration: {
|
|
96
|
+
default: durationVars['--duration-fast'],
|
|
97
|
+
'@media (prefers-reduced-motion: reduce)': '0s',
|
|
98
|
+
},
|
|
99
|
+
transitionTimingFunction: easeVars['--ease-standard'],
|
|
100
|
+
opacity: 'var(--_conceal-opacity, 1)',
|
|
101
|
+
},
|
|
102
|
+
});
|
package/src/hooks/index.ts
CHANGED
|
@@ -17,6 +17,9 @@ export type {UseFocusTrapOptions, UseFocusTrapReturn} from './useFocusTrap';
|
|
|
17
17
|
export {useAnnounce} from './useAnnounce';
|
|
18
18
|
export type {AnnounceFn, AnnouncePoliteness} from './useAnnounce';
|
|
19
19
|
|
|
20
|
+
export {useClipboard} from './useClipboard';
|
|
21
|
+
export type {UseClipboardOptions, UseClipboardReturn} from './useClipboard';
|
|
22
|
+
|
|
20
23
|
export {useGridFocus} from './useGridFocus';
|
|
21
24
|
export type {UseGridFocusOptions, UseGridFocusReturn} from './useGridFocus';
|
|
22
25
|
|
|
@@ -92,6 +95,7 @@ export {useLongPress} from './useLongPress';
|
|
|
92
95
|
export type {UseLongPressOptions, UseLongPressHandlers} from './useLongPress';
|
|
93
96
|
|
|
94
97
|
export {useDevWarning} from './useDevWarning';
|
|
98
|
+
export {useIndicatorFocusRing} from './useIndicatorFocusRing';
|
|
95
99
|
|
|
96
100
|
export {useContainerReveal} from './useContainerReveal';
|
|
97
101
|
export type {
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').HookDoc} */
|
|
4
|
+
export const docs = {
|
|
5
|
+
name: 'useClipboard',
|
|
6
|
+
displayName: 'useClipboard',
|
|
7
|
+
keywords: [
|
|
8
|
+
'clipboard', 'copy', 'copied', 'paste', 'writeText', 'copy-to-clipboard',
|
|
9
|
+
'copy button', 'announce', 'a11y',
|
|
10
|
+
],
|
|
11
|
+
params: [
|
|
12
|
+
{
|
|
13
|
+
name: 'options',
|
|
14
|
+
type: 'UseClipboardOptions',
|
|
15
|
+
description: 'Configuration object.',
|
|
16
|
+
required: false,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: 'options.announce',
|
|
20
|
+
type: 'string',
|
|
21
|
+
description:
|
|
22
|
+
'Message announced to a polite live region on a successful copy. A swapped aria-label alone is not reliably announced, so pass the localized confirmation (e.g. "Copied") to have it spoken. Omit to skip the announcement.',
|
|
23
|
+
required: false,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: 'options.resetAfterMs',
|
|
27
|
+
type: 'number',
|
|
28
|
+
description:
|
|
29
|
+
'Milliseconds isCopied stays true after a successful copy before reverting.',
|
|
30
|
+
default: '2000',
|
|
31
|
+
required: false,
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
returns: [
|
|
35
|
+
{
|
|
36
|
+
name: 'copy',
|
|
37
|
+
type: '(text: string) => Promise<boolean>',
|
|
38
|
+
description:
|
|
39
|
+
'Writes text to the clipboard. On success flips isCopied to true, announces the configured message, restarts the reset timer, and resolves true. A clipboard rejection is a silent no-op that leaves the copied state unchanged and resolves false.',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'isCopied',
|
|
43
|
+
type: 'boolean',
|
|
44
|
+
description:
|
|
45
|
+
'True for resetAfterMs after the most recent successful copy, then reverts. Drive the copied confirmation (e.g. a copy → check icon flip) off this.',
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
usage: {
|
|
49
|
+
description:
|
|
50
|
+
'Copy-to-clipboard behavior: the clipboard write, a transient isCopied flag with its own reset timer, and an optional polite screen-reader announcement. Extracted so every copy affordance is a thin control over one implementation instead of re-deriving the timer and announcement. Rapid re-copies restart the reset timer so the confirmation always lasts the full duration, and the timer is cleaned up on unmount. CodeBlock and Timestamp build their built-in copy buttons on it; reach for it directly when building a copy affordance that is not a plain icon button (a menu item, a labeled text button, a copy-on-click value chip).',
|
|
51
|
+
bestPractices: [
|
|
52
|
+
{ guidance: true, description: 'Drive the copied confirmation (copy → check icon, label swap) off the returned isCopied flag rather than tracking your own state.' },
|
|
53
|
+
{ guidance: true, description: 'Pass a localized announce message so the copy is spoken by screen readers; swapping the button aria-label alone is not reliably announced.' },
|
|
54
|
+
{ guidance: true, description: 'For the common compact icon copy button, render a ghost IconButton with a "Copy" tooltip and wire onClick to copy(); the tooltip stays "Copy" and the icon flip is the confirmation.' },
|
|
55
|
+
{ guidance: false, description: 'Track a separate copied useState alongside the hook; isCopied already reflects the copied window and resets itself.' },
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
relatedComponents: ['CodeBlock', 'Timestamp', 'IconButton'],
|
|
59
|
+
relatedHooks: ['useAnnounce'],
|
|
60
|
+
importPath: '@astryxdesign/core/hooks',
|
|
61
|
+
category: 'interaction',
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/** @type {import('@astryxdesign/cli/authoring').HookTranslationDoc} */
|
|
65
|
+
export const docsDense = {
|
|
66
|
+
description:
|
|
67
|
+
'Copy-to-clipboard behavior: clipboard write + transient isCopied flag with reset timer + optional polite SR announcement. One implementation shared by copy affordances instead of re-deriving timer/announce. Rapid re-copies restart the timer; timer cleaned up on unmount. CodeBlock/Timestamp build their copy buttons on it.',
|
|
68
|
+
paramDescriptions: {
|
|
69
|
+
options: 'config object.',
|
|
70
|
+
'options.announce': 'polite live-region message on successful copy (e.g. "Copied"); aria-label swap alone is not reliably announced. Omit to skip.',
|
|
71
|
+
'options.resetAfterMs': 'ms isCopied stays true after a copy before reverting.',
|
|
72
|
+
},
|
|
73
|
+
returnDescriptions: {
|
|
74
|
+
copy: '(text) => Promise<boolean>: writes text, flips isCopied, announces, restarts reset timer, resolves true; silent no-op resolving false on rejection.',
|
|
75
|
+
isCopied: 'true for resetAfterMs after the last successful copy; drive the copy → check confirmation off it.',
|
|
76
|
+
},
|
|
77
|
+
usage: {
|
|
78
|
+
description:
|
|
79
|
+
'Copy-to-clipboard behavior (write + isCopied flag with reset timer + optional polite SR announce). Thin controls over one implementation. Rapid re-copies restart the timer; cleaned up on unmount. Use directly for copy affordances that are not plain icon buttons (menu item, labeled text button, value chip).',
|
|
80
|
+
bestPractices: [
|
|
81
|
+
{ guidance: true, description: 'Drive the copy → check confirmation off the returned isCopied, not your own state.' },
|
|
82
|
+
{ guidance: true, description: 'Pass a localized announce so the copy is spoken (aria-label swap alone is not reliably announced).' },
|
|
83
|
+
{ guidance: true, description: 'For a compact icon copy button, use a ghost IconButton with a "Copy" tooltip and wire onClick to copy(); the tooltip stays "Copy" and the icon flip is the confirmation.' },
|
|
84
|
+
{ guidance: false, description: 'Track a separate copied useState alongside the hook.' },
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file useClipboard.test.tsx
|
|
5
|
+
* @input Uses vitest, @testing-library/react, useClipboard hook
|
|
6
|
+
* @output Unit tests for the useClipboard copy behavior
|
|
7
|
+
* @position Testing; validates useClipboard.ts
|
|
8
|
+
*
|
|
9
|
+
* SYNC: When useClipboard.ts changes, update tests to match new behavior
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
|
|
13
|
+
import {act, renderHook, waitFor} from '@testing-library/react';
|
|
14
|
+
import {useClipboard} from './useClipboard';
|
|
15
|
+
import {__resetLiveRegionsForTest} from './useAnnounce';
|
|
16
|
+
|
|
17
|
+
function politeRegion(): HTMLElement | null {
|
|
18
|
+
return document.querySelector('[data-astryx-live-region="polite"]');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe('useClipboard', () => {
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
// jsdom does not implement the async Clipboard API.
|
|
24
|
+
Object.assign(navigator, {
|
|
25
|
+
clipboard: {writeText: vi.fn().mockResolvedValue(undefined)},
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
__resetLiveRegionsForTest();
|
|
31
|
+
vi.restoreAllMocks();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('writes the given text to the clipboard and resolves true', async () => {
|
|
35
|
+
const {result} = renderHook(() => useClipboard());
|
|
36
|
+
let outcome: boolean | undefined;
|
|
37
|
+
await act(async () => {
|
|
38
|
+
outcome = await result.current.copy('hello world');
|
|
39
|
+
});
|
|
40
|
+
expect(navigator.clipboard.writeText).toHaveBeenCalledWith('hello world');
|
|
41
|
+
expect(outcome).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('flips isCopied to true after a successful copy', async () => {
|
|
45
|
+
const {result} = renderHook(() => useClipboard());
|
|
46
|
+
expect(result.current.isCopied).toBe(false);
|
|
47
|
+
await act(async () => {
|
|
48
|
+
await result.current.copy('x');
|
|
49
|
+
});
|
|
50
|
+
expect(result.current.isCopied).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('reverts isCopied after resetAfterMs', async () => {
|
|
54
|
+
vi.useFakeTimers();
|
|
55
|
+
try {
|
|
56
|
+
const {result} = renderHook(() => useClipboard({resetAfterMs: 1000}));
|
|
57
|
+
await act(async () => {
|
|
58
|
+
await result.current.copy('x');
|
|
59
|
+
});
|
|
60
|
+
expect(result.current.isCopied).toBe(true);
|
|
61
|
+
act(() => {
|
|
62
|
+
vi.advanceTimersByTime(1000);
|
|
63
|
+
});
|
|
64
|
+
expect(result.current.isCopied).toBe(false);
|
|
65
|
+
} finally {
|
|
66
|
+
vi.useRealTimers();
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('keeps isCopied true for the full window after a rapid re-copy', async () => {
|
|
71
|
+
vi.useFakeTimers();
|
|
72
|
+
try {
|
|
73
|
+
const {result} = renderHook(() => useClipboard({resetAfterMs: 2000}));
|
|
74
|
+
await act(async () => {
|
|
75
|
+
await result.current.copy('x');
|
|
76
|
+
});
|
|
77
|
+
expect(result.current.isCopied).toBe(true);
|
|
78
|
+
|
|
79
|
+
// Re-copy partway through the window.
|
|
80
|
+
act(() => {
|
|
81
|
+
vi.advanceTimersByTime(1500);
|
|
82
|
+
});
|
|
83
|
+
await act(async () => {
|
|
84
|
+
await result.current.copy('x');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// 600ms after the second copy (2.1s after the first): the first copy's
|
|
88
|
+
// timer must not have reverted it early.
|
|
89
|
+
act(() => {
|
|
90
|
+
vi.advanceTimersByTime(600);
|
|
91
|
+
});
|
|
92
|
+
expect(result.current.isCopied).toBe(true);
|
|
93
|
+
|
|
94
|
+
// It resets 2s after the most recent copy.
|
|
95
|
+
act(() => {
|
|
96
|
+
vi.advanceTimersByTime(1400);
|
|
97
|
+
});
|
|
98
|
+
expect(result.current.isCopied).toBe(false);
|
|
99
|
+
} finally {
|
|
100
|
+
vi.useRealTimers();
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('announces the configured message to a polite live region', async () => {
|
|
105
|
+
const {result} = renderHook(() => useClipboard({announce: 'Copied'}));
|
|
106
|
+
await act(async () => {
|
|
107
|
+
await result.current.copy('x');
|
|
108
|
+
});
|
|
109
|
+
await waitFor(() => {
|
|
110
|
+
expect(politeRegion()).toHaveTextContent('Copied');
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('does not announce when no announce message is configured', async () => {
|
|
115
|
+
const {result} = renderHook(() => useClipboard());
|
|
116
|
+
await act(async () => {
|
|
117
|
+
await result.current.copy('x');
|
|
118
|
+
});
|
|
119
|
+
// No announce option → no live region is ever created.
|
|
120
|
+
expect(politeRegion()).toBeNull();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('is a silent no-op that resolves false when the clipboard rejects', async () => {
|
|
124
|
+
(
|
|
125
|
+
navigator.clipboard.writeText as ReturnType<typeof vi.fn>
|
|
126
|
+
).mockRejectedValue(new Error('denied'));
|
|
127
|
+
const {result} = renderHook(() => useClipboard({announce: 'Copied'}));
|
|
128
|
+
let outcome: boolean | undefined;
|
|
129
|
+
await act(async () => {
|
|
130
|
+
outcome = await result.current.copy('x');
|
|
131
|
+
});
|
|
132
|
+
expect(outcome).toBe(false);
|
|
133
|
+
expect(result.current.isCopied).toBe(false);
|
|
134
|
+
expect(politeRegion()).toBeNull();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('clears a pending reset timer on unmount', async () => {
|
|
138
|
+
vi.useFakeTimers();
|
|
139
|
+
const clearSpy = vi.spyOn(globalThis, 'clearTimeout');
|
|
140
|
+
try {
|
|
141
|
+
const {result, unmount} = renderHook(() => useClipboard());
|
|
142
|
+
await act(async () => {
|
|
143
|
+
await result.current.copy('x');
|
|
144
|
+
});
|
|
145
|
+
unmount();
|
|
146
|
+
expect(clearSpy).toHaveBeenCalled();
|
|
147
|
+
} finally {
|
|
148
|
+
vi.useRealTimers();
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
});
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file useClipboard.ts
|
|
7
|
+
* @input Uses React state/effect/callback; navigator.clipboard; useAnnounce
|
|
8
|
+
* @output Exports useClipboard hook plus its options/return types
|
|
9
|
+
* @position Core behavior hook; owns the copy-to-clipboard behavior shared by
|
|
10
|
+
* CodeBlock and Timestamp — the clipboard write, the transient "copied"
|
|
11
|
+
* flag with its reset timer, and the polite screen-reader announcement — so
|
|
12
|
+
* each call site is a thin control over one implementation instead of
|
|
13
|
+
* re-deriving the timer/announce details independently.
|
|
14
|
+
*
|
|
15
|
+
* SYNC: When modified, update these files to stay in sync:
|
|
16
|
+
* - /packages/core/src/hooks/index.ts
|
|
17
|
+
* - /packages/core/src/hooks/useClipboard.doc.mjs
|
|
18
|
+
* - /packages/core/src/hooks/useClipboard.test.tsx
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import {useCallback, useEffect, useRef, useState} from 'react';
|
|
22
|
+
import {useAnnounce} from './useAnnounce';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* How long `isCopied` stays true after a successful copy before reverting.
|
|
26
|
+
* Long enough for the copied confirmation (e.g. a copy → check icon flip) to
|
|
27
|
+
* register without lingering.
|
|
28
|
+
*/
|
|
29
|
+
const DEFAULT_RESET_AFTER_MS = 2000;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Options for {@link useClipboard}.
|
|
33
|
+
*/
|
|
34
|
+
export interface UseClipboardOptions {
|
|
35
|
+
/**
|
|
36
|
+
* Message announced to a polite live region on a successful copy. Swapping a
|
|
37
|
+
* control's `aria-label` alone is not reliably announced by screen readers,
|
|
38
|
+
* so pass the localized confirmation (e.g. "Copied") to have it spoken.
|
|
39
|
+
* Omit to skip the announcement.
|
|
40
|
+
*/
|
|
41
|
+
announce?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Milliseconds `isCopied` stays true after a successful copy before it
|
|
44
|
+
* reverts to false.
|
|
45
|
+
* @default 2000
|
|
46
|
+
*/
|
|
47
|
+
resetAfterMs?: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Return value of {@link useClipboard}.
|
|
52
|
+
*/
|
|
53
|
+
export interface UseClipboardReturn {
|
|
54
|
+
/**
|
|
55
|
+
* Write `text` to the clipboard. On success, flips `isCopied` to true,
|
|
56
|
+
* announces the configured message, and (re)starts the reset timer;
|
|
57
|
+
* resolves `true`. A clipboard rejection is a silent no-op that leaves the
|
|
58
|
+
* copied state unchanged and resolves `false`.
|
|
59
|
+
*/
|
|
60
|
+
copy: (text: string) => Promise<boolean>;
|
|
61
|
+
/** True for `resetAfterMs` after the most recent successful copy. */
|
|
62
|
+
isCopied: boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Copy-to-clipboard behavior: the clipboard write, a transient `isCopied`
|
|
67
|
+
* flag with its own reset timer, and an optional polite screen-reader
|
|
68
|
+
* announcement — the block otherwise re-derived at every copy affordance.
|
|
69
|
+
*
|
|
70
|
+
* Per [API Conventions → Behaviors: Hooks Over Wrappers], the behavior is a
|
|
71
|
+
* hook and the control (an icon button, a menu item, a value chip) is a thin
|
|
72
|
+
* shell over it. Rapid re-copies restart the reset timer so the confirmation
|
|
73
|
+
* always lasts the full duration, and the timer is cleaned up on unmount.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```
|
|
77
|
+
* function CopyButton({text}: {text: string}) {
|
|
78
|
+
* const {copy, isCopied} = useClipboard({announce: 'Copied'});
|
|
79
|
+
* return (
|
|
80
|
+
* <IconButton
|
|
81
|
+
* label={isCopied ? 'Copied' : 'Copy'}
|
|
82
|
+
* tooltip="Copy"
|
|
83
|
+
* icon={<Icon icon={isCopied ? 'check' : 'copy'} />}
|
|
84
|
+
* onClick={() => void copy(text)}
|
|
85
|
+
* />
|
|
86
|
+
* );
|
|
87
|
+
* }
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export function useClipboard(
|
|
91
|
+
options: UseClipboardOptions = {},
|
|
92
|
+
): UseClipboardReturn {
|
|
93
|
+
const {announce: announceMessage, resetAfterMs = DEFAULT_RESET_AFTER_MS} =
|
|
94
|
+
options;
|
|
95
|
+
const announce = useAnnounce();
|
|
96
|
+
const [isCopied, setIsCopied] = useState(false);
|
|
97
|
+
const resetTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
98
|
+
|
|
99
|
+
// Clear a pending "copied" reset when the consumer unmounts.
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
return () => {
|
|
102
|
+
if (resetTimerRef.current != null) {
|
|
103
|
+
clearTimeout(resetTimerRef.current);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}, []);
|
|
107
|
+
|
|
108
|
+
const copy = useCallback(
|
|
109
|
+
async (text: string): Promise<boolean> => {
|
|
110
|
+
try {
|
|
111
|
+
await navigator.clipboard.writeText(text);
|
|
112
|
+
setIsCopied(true);
|
|
113
|
+
if (announceMessage) {
|
|
114
|
+
announce(announceMessage);
|
|
115
|
+
}
|
|
116
|
+
// Restart the reset timer on every copy — otherwise a rapid re-copy
|
|
117
|
+
// is reverted early by the previous copy's timer.
|
|
118
|
+
if (resetTimerRef.current != null) {
|
|
119
|
+
clearTimeout(resetTimerRef.current);
|
|
120
|
+
}
|
|
121
|
+
resetTimerRef.current = setTimeout(() => {
|
|
122
|
+
resetTimerRef.current = null;
|
|
123
|
+
setIsCopied(false);
|
|
124
|
+
}, resetAfterMs);
|
|
125
|
+
return true;
|
|
126
|
+
} catch {
|
|
127
|
+
// Clipboard failures leave the copied state unchanged.
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
[announce, announceMessage, resetAfterMs],
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
return {copy, isCopied};
|
|
135
|
+
}
|
|
@@ -15,7 +15,7 @@ export const docs = {
|
|
|
15
15
|
{
|
|
16
16
|
name: 'options.isEnabled',
|
|
17
17
|
type: 'boolean',
|
|
18
|
-
description: 'When false the hook is inert:
|
|
18
|
+
description: 'When false the hook is inert: the container gets no styles and content getters return no styles, so content is always shown. Read on every render, so a component can flip it after mount (e.g. revealOn === "hover").',
|
|
19
19
|
default: 'true',
|
|
20
20
|
required: false,
|
|
21
21
|
},
|
|
@@ -34,11 +34,11 @@ export const docs = {
|
|
|
34
34
|
],
|
|
35
35
|
usage: {
|
|
36
36
|
description:
|
|
37
|
-
'A headless hover/focus reveal primitive. Gives a container a scoped trigger that reveals (or conceals) content inside it when the container is hovered or receives keyboard focus: the classic "row actions appear on hover" pattern. The reveal is CSS-only: no hover state lives in React and hovering never triggers a re-render. The caller authors no StyleX for the reveal itself; the hook hands out
|
|
37
|
+
'A headless hover/focus reveal primitive. Gives a container a scoped trigger that reveals (or conceals) content inside it when the container is hovered or receives keyboard focus: the classic "row actions appear on hover" pattern. The reveal is CSS-only: no hover state lives in React and hovering never triggers a re-render. The caller authors no StyleX for the reveal itself; the hook hands out the container and content styles, and a nested container shadows its ancestor, so nested containers never leak hover/focus into one another. Accessible by construction: revealed content is visually hidden at rest with position and opacity (never display:none), so it stays mounted, keeps its place in the tab order, and is announced to assistive technology; it reveals on :focus-within so keyboard users see it when tabbing in, stays visible on touch (never gated behind hover on coarse pointers), and honors prefers-reduced-motion.',
|
|
38
38
|
bestPractices: [
|
|
39
39
|
{ guidance: true, description: 'Destructure getContainerProps and getContentRevealProps; spread getContainerProps() on the container (via mergeProps with your own stylex.props) and getContentRevealProps() on the content to reveal.' },
|
|
40
40
|
{ guidance: true, description: 'Use for secondary affordances: reveal-on-hover row actions (edit/copy/remove on list or table rows) and overlay controls on a card or media tile (e.g. Thumbnail\'s remove button).' },
|
|
41
|
-
{ guidance: true, description: 'Gate the reveal with isEnabled when a consumer prop decides whether content is revealed on hover or always shown.' },
|
|
41
|
+
{ guidance: true, description: 'Gate the reveal with isEnabled when a consumer prop decides whether content is revealed on hover or always shown; it can change at any time.' },
|
|
42
42
|
{ guidance: true, description: 'Pass isLayoutPreserved for absolutely-positioned or overlay content to reserve its box and avoid layout shift when it appears.' },
|
|
43
43
|
{ guidance: false, description: 'Use it to hide content that must always be discoverable; keep essential actions visible instead of gating them behind hover.' },
|
|
44
44
|
],
|
|
@@ -52,10 +52,10 @@ export const docs = {
|
|
|
52
52
|
/** @type {import('@astryxdesign/cli/authoring').HookTranslationDoc} */
|
|
53
53
|
export const docsDense = {
|
|
54
54
|
description:
|
|
55
|
-
'Headless hover/focus reveal primitive. Gives a container a scoped trigger that reveals (or conceals) content inside it on hover / keyboard focus: the "row actions appear on hover" pattern. CSS-only: no hover state in React, no re-render on hover. Caller authors no StyleX; hook hands out a
|
|
55
|
+
'Headless hover/focus reveal primitive. Gives a container a scoped trigger that reveals (or conceals) content inside it on hover / keyboard focus: the "row actions appear on hover" pattern. CSS-only: no hover state in React, no re-render on hover. Caller authors no StyleX; hook hands out container + content styles; a nested container shadows its ancestor, so nested containers never leak hover/focus. Accessible: revealed content visually hidden at rest via position + opacity (never display:none), stays mounted + in tab order + announced; reveals on :focus-within, stays visible on touch, honors prefers-reduced-motion.',
|
|
56
56
|
paramDescriptions: {
|
|
57
57
|
options: 'config for reveal container. optional.',
|
|
58
|
-
'options.isEnabled': 'when false hook is inert: no
|
|
58
|
+
'options.isEnabled': 'when false hook is inert: no container styles, content getters return no styles, content always shown. Read every render, so it can flip after mount.',
|
|
59
59
|
},
|
|
60
60
|
returnDescriptions: {
|
|
61
61
|
getContainerProps: 'spread onto container whose hover/focus-within drives reveal.',
|
|
@@ -63,11 +63,11 @@ export const docsDense = {
|
|
|
63
63
|
},
|
|
64
64
|
usage: {
|
|
65
65
|
description:
|
|
66
|
-
'Headless hover/focus reveal primitive. Gives a container a scoped trigger that reveals (or conceals) content inside it on hover / keyboard focus: the "row actions appear on hover" pattern. CSS-only: no hover state in React, no re-render on hover. Caller authors no StyleX; hook hands out a
|
|
66
|
+
'Headless hover/focus reveal primitive. Gives a container a scoped trigger that reveals (or conceals) content inside it on hover / keyboard focus: the "row actions appear on hover" pattern. CSS-only: no hover state in React, no re-render on hover. Caller authors no StyleX; hook hands out container + content styles; a nested container shadows its ancestor, so nested containers never leak hover/focus. Accessible: revealed content visually hidden at rest via position + opacity (never display:none), stays mounted + in tab order + announced; reveals on :focus-within, stays visible on touch, honors prefers-reduced-motion.',
|
|
67
67
|
bestPractices: [
|
|
68
68
|
{ guidance: true, description: 'Destructure getContainerProps + getContentRevealProps; spread getContainerProps() on container (via mergeProps w/ your stylex.props) + getContentRevealProps() on content to reveal.' },
|
|
69
69
|
{ guidance: true, description: 'Use for secondary affordances: reveal-on-hover row actions (edit/copy/remove on list / table rows) + overlay controls on card / media tile (e.g. Thumbnail remove button).' },
|
|
70
|
-
{ guidance: true, description: 'Gate reveal w/ isEnabled when a consumer prop decides revealed-on-hover vs always shown.' },
|
|
70
|
+
{ guidance: true, description: 'Gate reveal w/ isEnabled when a consumer prop decides revealed-on-hover vs always shown; can change at any time.' },
|
|
71
71
|
{ guidance: true, description: 'Pass isLayoutPreserved for absolutely-positioned / overlay content to reserve its box + avoid layout shift.' },
|
|
72
72
|
{ guidance: false, description: 'Use to hide content that must always be discoverable; keep essential actions visible instead of gating behind hover.' },
|
|
73
73
|
],
|