@astryxdesign/core 0.1.9 → 0.2.0-canary.068bda9
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 +107 -0
- package/dist/Avatar/Avatar.d.ts +42 -3
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +190 -59
- package/dist/Avatar/AvatarStatusDot.d.ts +6 -4
- package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroup.d.ts +7 -1
- package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroup.js +58 -6
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.js +7 -2
- package/dist/Banner/Banner.js +2 -2
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +11 -4
- package/dist/Breadcrumbs/index.d.ts +1 -0
- package/dist/Breadcrumbs/index.d.ts.map +1 -1
- package/dist/Breadcrumbs/index.js +2 -1
- package/dist/Button/Button.d.ts +1 -1
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +6 -4
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +50 -9
- package/dist/Calendar/dayCellUtils.d.ts +4 -4
- package/dist/Calendar/dayCellUtils.js +2 -2
- package/dist/Calendar/styles.d.ts +30 -31
- package/dist/Calendar/styles.d.ts.map +1 -1
- package/dist/Calendar/styles.js +30 -31
- package/dist/Card/Card.js +1 -1
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +22 -12
- package/dist/Chat/ChatComposerInput.js +1 -1
- package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
- package/dist/Chat/ChatDictationButton.js +3 -1
- package/dist/Chat/ChatLayout.js +4 -4
- package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
- package/dist/Chat/ChatMessageBubble.js +8 -8
- package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.js +10 -3
- package/dist/Chat/useTriggerMenu.js +2 -2
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +6 -6
- package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.js +19 -4
- package/dist/Citation/Citation.d.ts +22 -1
- package/dist/Citation/Citation.d.ts.map +1 -1
- package/dist/Citation/Citation.js +20 -6
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +16 -10
- package/dist/Collapsible/Collapsible.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.js +3 -1
- package/dist/CommandPalette/CommandPalette.d.ts +1 -1
- package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPalette.js +42 -3
- package/dist/CommandPalette/CommandPaletteItem.js +1 -1
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +23 -5
- package/dist/ContextMenu/index.d.ts +1 -0
- package/dist/ContextMenu/index.d.ts.map +1 -1
- package/dist/ContextMenu/index.js +3 -1
- package/dist/DateInput/DateInput.d.ts +10 -2
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +33 -19
- package/dist/DateRangeInput/DateRangeInput.d.ts +10 -2
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +23 -20
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +23 -18
- package/dist/Divider/Divider.d.ts +2 -2
- package/dist/Divider/Divider.d.ts.map +1 -1
- package/dist/Divider/Divider.js +13 -0
- package/dist/DropdownMenu/DropdownMenu.d.ts +6 -0
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +22 -5
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +4 -1
- package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuItem.js +5 -2
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +8 -11
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioGroup.js +3 -1
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioItem.js +5 -2
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +104 -0
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -0
- package/dist/DropdownMenu/DropdownMenuSubMenu.js +435 -0
- package/dist/DropdownMenu/index.d.ts +10 -0
- package/dist/DropdownMenu/index.d.ts.map +1 -1
- package/dist/DropdownMenu/index.js +13 -0
- package/dist/DropdownMenu/menuItemHover.d.ts +24 -0
- package/dist/DropdownMenu/menuItemHover.d.ts.map +1 -0
- package/dist/DropdownMenu/menuItemHover.js +34 -0
- package/dist/DropdownMenu/menuItemRoles.d.ts +8 -0
- package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -1
- package/dist/DropdownMenu/menuItemRoles.js +10 -1
- package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
- package/dist/DropdownMenu/renderDropdownItems.js +21 -6
- package/dist/Field/Field.d.ts +3 -1
- package/dist/Field/Field.d.ts.map +1 -1
- package/dist/Field/Field.js +4 -1
- package/dist/Field/FieldLabel.d.ts +2 -1
- package/dist/Field/FieldLabel.d.ts.map +1 -1
- package/dist/Field/FieldLabel.js +9 -5
- package/dist/Field/inputStyles.stylex.d.ts +1 -0
- package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
- package/dist/Field/inputStyles.stylex.js +1 -0
- package/dist/FieldStatus/FieldStatus.d.ts +10 -0
- package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
- package/dist/FieldStatus/FieldStatus.js +51 -5
- package/dist/FileInput/FileInput.d.ts +10 -2
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +93 -65
- package/dist/Heading/Heading.d.ts.map +1 -1
- package/dist/Heading/Heading.js +3 -2
- package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +10 -4
- package/dist/Icon/Icon.d.ts +15 -1
- package/dist/Icon/Icon.d.ts.map +1 -1
- package/dist/Icon/Icon.js +19 -3
- package/dist/Item/Item.d.ts +16 -3
- package/dist/Item/Item.d.ts.map +1 -1
- package/dist/Item/Item.js +31 -6
- package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
- package/dist/Layer/layerAnimations.stylex.js +10 -2
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +34 -15
- package/dist/List/List.d.ts.map +1 -1
- package/dist/List/List.js +6 -3
- package/dist/List/ListItem.d.ts +11 -1
- package/dist/List/ListItem.d.ts.map +1 -1
- package/dist/List/ListItem.js +2 -0
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.js +7 -0
- package/dist/MetadataList/MetadataList.d.ts +1 -1
- package/dist/MetadataList/MetadataList.d.ts.map +1 -1
- package/dist/MetadataList/MetadataList.js +4 -2
- package/dist/MultiSelector/MultiSelector.d.ts +9 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +81 -43
- package/dist/NavMenu/NavHeadingMenuItem.js +1 -1
- package/dist/NumberInput/NumberInput.d.ts +10 -2
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +22 -17
- package/dist/Outline/Outline.js +2 -2
- package/dist/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Pagination/Pagination.js +11 -3
- package/dist/PowerSearch/PowerSearch.d.ts +9 -2
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +2 -0
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +6 -1
- package/dist/RadioList/RadioList.d.ts +2 -2
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +5 -5
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +49 -19
- package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +5 -3
- package/dist/Selector/Selector.d.ts +8 -0
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +66 -17
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +5 -5
- package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
- package/dist/SideNav/SideNavCollapseButton.js +13 -10
- package/dist/SideNav/SideNavItem.d.ts +1 -1
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +12 -10
- package/dist/Skeleton/Skeleton.d.ts.map +1 -1
- package/dist/Skeleton/Skeleton.js +2 -2
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +87 -34
- package/dist/Spinner/Spinner.d.ts +3 -2
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +11 -2
- package/dist/Switch/Switch.d.ts +8 -1
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +171 -91
- package/dist/TabList/Tab.js +2 -2
- package/dist/TabList/TabList.d.ts +2 -11
- package/dist/TabList/TabList.d.ts.map +1 -1
- package/dist/TabList/TabList.js +5 -9
- package/dist/TabList/TabListContext.d.ts +0 -5
- package/dist/TabList/TabListContext.d.ts.map +1 -1
- package/dist/TabList/TabListContext.js +0 -5
- package/dist/TabList/TabMenu.js +1 -1
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +30 -4
- package/dist/Table/TableCell.js +3 -3
- package/dist/Table/TableHeaderCell.js +3 -3
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +15 -11
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +37 -27
- package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
- package/dist/Table/plugins/selection/useTableSelection.js +3 -0
- package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
- package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +15 -7
- package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
- package/dist/Table/plugins/tree/useTableTreeData.js +29 -23
- package/dist/Table/types.d.ts +32 -0
- package/dist/Table/types.d.ts.map +1 -1
- package/dist/Text/Text.d.ts +10 -1
- package/dist/Text/Text.d.ts.map +1 -1
- package/dist/Text/Text.js +25 -2
- package/dist/Text/index.d.ts +2 -2
- package/dist/Text/index.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 +119 -52
- package/dist/TextInput/TextInput.d.ts +10 -2
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +20 -17
- package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
- package/dist/Thumbnail/Thumbnail.js +51 -45
- package/dist/TimeInput/TimeInput.d.ts +10 -2
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +17 -17
- package/dist/Timestamp/Timestamp.d.ts +31 -3
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +48 -77
- package/dist/Timestamp/formatInstant.d.ts +41 -0
- package/dist/Timestamp/formatInstant.d.ts.map +1 -0
- package/dist/Timestamp/formatInstant.js +177 -0
- package/dist/Timestamp/index.d.ts +1 -0
- package/dist/Timestamp/index.d.ts.map +1 -1
- package/dist/Timestamp/tooltipEntries.d.ts +56 -0
- package/dist/Timestamp/tooltipEntries.d.ts.map +1 -0
- package/dist/Timestamp/tooltipEntries.js +163 -0
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +61 -9
- package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
- package/dist/ToggleButton/ToggleButton.js +3 -1
- package/dist/Token/Token.d.ts +34 -4
- package/dist/Token/Token.d.ts.map +1 -1
- package/dist/Token/Token.js +85 -45
- package/dist/Token/TokenLink.d.ts +62 -0
- package/dist/Token/TokenLink.d.ts.map +1 -0
- package/dist/Token/TokenLink.js +76 -0
- package/dist/Token/index.d.ts +1 -1
- package/dist/Token/index.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.d.ts +9 -2
- 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 +17 -1
- package/dist/TopNav/TopNavItem.d.ts +3 -1
- package/dist/TopNav/TopNavItem.d.ts.map +1 -1
- package/dist/TopNav/TopNavItem.js +30 -3
- package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenu.js +9 -3
- package/dist/TreeList/TreeList.d.ts.map +1 -1
- package/dist/TreeList/TreeList.js +1 -0
- package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
- package/dist/TreeList/TreeListBranches.js +8 -6
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +32 -14
- package/dist/Typeahead/BaseTypeahead.d.ts +9 -0
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +22 -11
- package/dist/Typeahead/Typeahead.d.ts +9 -2
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +11 -2
- package/dist/Typeahead/TypeaheadItem.d.ts +1 -1
- package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
- package/dist/Typeahead/TypeaheadItem.js +20 -10
- package/dist/astryx.css +114 -35
- package/dist/astryx.umd.js +50 -50
- package/dist/astryx.umd.js.map +4 -4
- package/dist/hooks/containerReveal.pool.stylex.d.ts +281 -0
- package/dist/hooks/containerReveal.pool.stylex.d.ts.map +1 -0
- package/dist/hooks/containerReveal.pool.stylex.js +281 -0
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +3 -1
- package/dist/hooks/isRtlElement.d.ts +18 -0
- package/dist/hooks/isRtlElement.d.ts.map +1 -0
- package/dist/hooks/isRtlElement.js +27 -0
- package/dist/hooks/useAnnounce.d.ts +3 -0
- package/dist/hooks/useAnnounce.d.ts.map +1 -1
- package/dist/hooks/useAnnounce.js +39 -1
- package/dist/hooks/useContainerReveal.d.ts +83 -0
- package/dist/hooks/useContainerReveal.d.ts.map +1 -0
- package/dist/hooks/useContainerReveal.js +123 -0
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +52 -11
- package/dist/hooks/useGridFocus.d.ts +6 -1
- package/dist/hooks/useGridFocus.d.ts.map +1 -1
- package/dist/hooks/useGridFocus.js +12 -9
- package/dist/hooks/useHotkeys.d.ts +9 -0
- package/dist/hooks/useHotkeys.d.ts.map +1 -1
- package/dist/hooks/useHotkeys.js +9 -0
- package/dist/hooks/useInputStatusIcon.d.ts +38 -0
- package/dist/hooks/useInputStatusIcon.d.ts.map +1 -0
- package/dist/hooks/useInputStatusIcon.js +169 -0
- package/dist/hooks/useKeyboardHint.d.ts +1 -1
- package/dist/hooks/useKeyboardHint.d.ts.map +1 -1
- package/dist/hooks/useKeyboardHint.js +4 -2
- package/dist/hooks/useListFocus.d.ts +39 -1
- package/dist/hooks/useListFocus.d.ts.map +1 -1
- package/dist/hooks/useListFocus.js +54 -9
- package/dist/i18n/InternationalizationContext.d.ts +1 -0
- package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
- package/dist/i18n/InternationalizationContext.js +3 -0
- package/dist/i18n/InternationalizationProvider.d.ts +8 -1
- package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
- package/dist/i18n/InternationalizationProvider.js +5 -1
- package/dist/i18n/getLocaleDirection.d.ts +18 -0
- package/dist/i18n/getLocaleDirection.d.ts.map +1 -0
- package/dist/i18n/getLocaleDirection.js +29 -0
- package/dist/i18n/index.d.ts +6 -0
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js +7 -1
- package/dist/i18n/useDirection.d.ts +2 -0
- package/dist/i18n/useDirection.d.ts.map +1 -0
- package/dist/i18n/useDirection.js +27 -0
- package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
- package/dist/theme/derivedVarRegistry.js +13 -0
- package/dist/theme/index.d.ts +3 -3
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/types.d.ts +39 -2
- package/dist/theme/types.d.ts.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +2 -1
- package/dist/utils/plainDate.d.ts +17 -0
- package/dist/utils/plainDate.d.ts.map +1 -1
- package/dist/utils/plainDate.js +23 -4
- package/dist/utils/rtlStyles.d.ts +24 -0
- package/dist/utils/rtlStyles.d.ts.map +1 -0
- package/dist/utils/rtlStyles.js +27 -0
- package/locales/en.json +120 -0
- package/locales/pseudo.json +90 -0
- package/package.json +7 -2
- package/src/Avatar/Avatar.doc.mjs +50 -3
- package/src/Avatar/Avatar.test.tsx +218 -0
- package/src/Avatar/Avatar.tsx +285 -79
- package/src/Avatar/AvatarStatusDot.doc.mjs +4 -4
- package/src/Avatar/AvatarStatusDot.tsx +6 -4
- package/src/AvatarGroup/AvatarGroup.doc.mjs +2 -0
- package/src/AvatarGroup/AvatarGroup.test.tsx +162 -2
- package/src/AvatarGroup/AvatarGroup.tsx +58 -3
- package/src/AvatarGroup/AvatarGroupOverflow.tsx +5 -2
- package/src/Banner/Banner.tsx +6 -6
- package/src/Breadcrumbs/BreadcrumbItem.tsx +10 -10
- package/src/Breadcrumbs/Breadcrumbs.test.tsx +52 -1
- package/src/Breadcrumbs/index.ts +4 -0
- package/src/Button/Button.doc.mjs +0 -4
- package/src/Button/Button.test.tsx +16 -0
- package/src/Button/Button.tsx +6 -4
- package/src/Calendar/Calendar.test.tsx +170 -20
- package/src/Calendar/Calendar.tsx +73 -16
- package/src/Calendar/dayCellUtils.test.ts +18 -18
- package/src/Calendar/dayCellUtils.ts +2 -2
- package/src/Calendar/styles.ts +33 -39
- package/src/Card/Card.tsx +1 -1
- package/src/Carousel/Carousel.tsx +25 -6
- package/src/Chat/ChatComposer.doc.mjs +2 -2
- package/src/Chat/ChatComposerInput.doc.mjs +1 -1
- package/src/Chat/ChatComposerInput.tsx +2 -2
- package/src/Chat/ChatDictationButton.tsx +7 -1
- package/src/Chat/ChatLayout.tsx +4 -4
- package/src/Chat/ChatMessageBubble.tsx +15 -10
- package/src/Chat/ChatSystemMessage.test.tsx +5 -0
- package/src/Chat/ChatToolCalls.tsx +9 -2
- package/src/Chat/useTriggerMenu.tsx +1 -1
- package/src/CheckboxInput/CheckboxInput.doc.mjs +6 -0
- package/src/CheckboxInput/CheckboxInput.test.tsx +23 -0
- package/src/CheckboxInput/CheckboxInput.tsx +21 -6
- package/src/CheckboxList/CheckboxList.doc.mjs +6 -0
- package/src/CheckboxList/CheckboxList.test.tsx +94 -3
- package/src/CheckboxList/CheckboxListItem.tsx +17 -10
- package/src/Citation/Citation.doc.mjs +4 -3
- package/src/Citation/Citation.test.tsx +97 -0
- package/src/Citation/Citation.tsx +43 -11
- package/src/CodeBlock/CodeBlock.test.tsx +17 -0
- package/src/CodeBlock/CodeBlock.tsx +38 -11
- package/src/Collapsible/Collapsible.doc.mjs +1 -0
- package/src/Collapsible/Collapsible.test.tsx +7 -0
- package/src/Collapsible/Collapsible.tsx +16 -6
- package/src/CommandPalette/CommandPalette.test.tsx +95 -1
- package/src/CommandPalette/CommandPalette.tsx +41 -2
- package/src/CommandPalette/CommandPaletteItem.tsx +1 -1
- package/src/ContextMenu/ContextMenu.test.tsx +59 -2
- package/src/ContextMenu/ContextMenu.tsx +22 -11
- package/src/ContextMenu/index.ts +5 -0
- package/src/DateInput/DateInput.doc.mjs +25 -0
- package/src/DateInput/DateInput.test.tsx +230 -0
- package/src/DateInput/DateInput.tsx +51 -29
- package/src/DateRangeInput/DateRangeInput.doc.mjs +16 -0
- package/src/DateRangeInput/DateRangeInput.test.tsx +140 -0
- package/src/DateRangeInput/DateRangeInput.tsx +42 -30
- package/src/DateTimeInput/DateTimeInput.doc.mjs +22 -1
- package/src/DateTimeInput/DateTimeInput.test.tsx +112 -0
- package/src/DateTimeInput/DateTimeInput.tsx +44 -39
- package/src/Divider/Divider.test.tsx +32 -8
- package/src/Divider/Divider.tsx +14 -1
- package/src/DropdownMenu/DropdownMenu.test.tsx +60 -0
- package/src/DropdownMenu/DropdownMenu.tsx +31 -12
- package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +8 -2
- package/src/DropdownMenu/DropdownMenuItem.tsx +9 -3
- package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +4 -12
- package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +9 -10
- package/src/DropdownMenu/DropdownMenuRadioItem.tsx +9 -3
- package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +20 -2
- package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +88 -0
- package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +663 -0
- package/src/DropdownMenu/DropdownMenuSubMenu.tsx +561 -0
- package/src/DropdownMenu/index.ts +16 -0
- package/src/DropdownMenu/menuItemHover.ts +40 -0
- package/src/DropdownMenu/menuItemRoles.ts +9 -0
- package/src/DropdownMenu/renderDropdownItems.tsx +29 -15
- package/src/Field/Field.doc.mjs +4 -0
- package/src/Field/Field.tsx +14 -9
- package/src/Field/FieldLabel.test.tsx +25 -0
- package/src/Field/FieldLabel.tsx +12 -3
- package/src/Field/inputStyles.stylex.ts +2 -0
- package/src/FieldStatus/FieldStatus.doc.mjs +1 -0
- package/src/FieldStatus/FieldStatus.test.tsx +133 -0
- package/src/FieldStatus/FieldStatus.tsx +61 -4
- package/src/FileInput/FileInput.doc.mjs +23 -2
- package/src/FileInput/FileInput.test.tsx +104 -13
- package/src/FileInput/FileInput.tsx +106 -79
- package/src/Heading/Heading.tsx +2 -1
- package/src/HoverCard/useHoverCard.tsx +15 -3
- package/src/Icon/Icon.doc.mjs +13 -0
- package/src/Icon/Icon.test.tsx +89 -0
- package/src/Icon/Icon.tsx +51 -3
- package/src/Item/Item.doc.mjs +5 -0
- package/src/Item/Item.test.tsx +88 -0
- package/src/Item/Item.tsx +57 -11
- package/src/Layer/layerAnimations.stylex.ts +23 -0
- package/src/Lightbox/Lightbox.test.tsx +33 -0
- package/src/Lightbox/Lightbox.tsx +30 -10
- package/src/List/List.test.tsx +17 -29
- package/src/List/List.tsx +6 -1
- package/src/List/ListItem.doc.mjs +12 -0
- package/src/List/ListItem.tsx +13 -0
- package/src/Markdown/Markdown.test.tsx +21 -0
- package/src/Markdown/Markdown.tsx +4 -0
- package/src/MetadataList/MetadataList.test.tsx +24 -0
- package/src/MetadataList/MetadataList.tsx +6 -2
- package/src/MultiSelector/MultiSelector.doc.mjs +20 -0
- package/src/MultiSelector/MultiSelector.test.tsx +402 -0
- package/src/MultiSelector/MultiSelector.tsx +126 -64
- package/src/NavMenu/NavHeadingMenuItem.tsx +1 -1
- package/src/NumberInput/NumberInput.doc.mjs +21 -0
- package/src/NumberInput/NumberInput.test.tsx +71 -0
- package/src/NumberInput/NumberInput.tsx +35 -26
- package/src/Outline/Outline.test.tsx +12 -12
- package/src/Outline/Outline.tsx +2 -2
- package/src/Pagination/Pagination.tsx +11 -3
- package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
- package/src/PowerSearch/PowerSearch.test.tsx +23 -0
- package/src/PowerSearch/PowerSearch.tsx +10 -1
- package/src/ProgressBar/ProgressBar.test.tsx +34 -0
- package/src/ProgressBar/ProgressBar.tsx +16 -1
- package/src/RadioList/RadioList.doc.mjs +6 -0
- package/src/RadioList/RadioList.test.tsx +26 -4
- package/src/RadioList/RadioList.tsx +2 -2
- package/src/RadioList/RadioListItem.tsx +12 -5
- package/src/Resizable/ResizeHandle.test.tsx +94 -0
- package/src/Resizable/ResizeHandle.tsx +70 -20
- package/src/SegmentedControl/SegmentedControl.test.tsx +28 -0
- package/src/SegmentedControl/SegmentedControlItem.tsx +22 -2
- package/src/Selector/Selector.doc.mjs +15 -0
- package/src/Selector/Selector.test.tsx +370 -0
- package/src/Selector/Selector.tsx +88 -20
- package/src/SideNav/SideNav.tsx +1 -8
- package/src/SideNav/SideNavCollapseButton.tsx +9 -7
- package/src/SideNav/SideNavItem.tsx +5 -3
- package/src/Skeleton/Skeleton.test.tsx +16 -0
- package/src/Skeleton/Skeleton.tsx +13 -1
- package/src/Slider/Slider.doc.mjs +6 -0
- package/src/Slider/Slider.test.tsx +240 -12
- package/src/Slider/Slider.tsx +96 -23
- package/src/Spinner/Spinner.test.tsx +10 -8
- package/src/Spinner/Spinner.tsx +15 -5
- package/src/Switch/Switch.doc.mjs +142 -42
- package/src/Switch/Switch.test.tsx +111 -0
- package/src/Switch/Switch.tsx +149 -32
- package/src/TabList/Tab.tsx +2 -2
- package/src/TabList/TabList.doc.mjs +0 -6
- package/src/TabList/TabList.test.tsx +3 -15
- package/src/TabList/TabList.tsx +6 -17
- package/src/TabList/TabListContext.ts +0 -6
- package/src/TabList/TabMenu.tsx +2 -2
- package/src/Table/BaseTable.tsx +34 -2
- package/src/Table/Table.doc.mjs +11 -0
- package/src/Table/Table.test.tsx +85 -0
- package/src/Table/TableCell.tsx +3 -3
- package/src/Table/TableHeaderCell.tsx +3 -3
- package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +9 -6
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +19 -0
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +27 -18
- package/src/Table/plugins/selection/useTableSelection.test.tsx +61 -3
- package/src/Table/plugins/selection/useTableSelection.tsx +3 -0
- package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +38 -9
- package/src/Table/plugins/tree/useTableTreeData.tsx +17 -13
- package/src/Table/types.ts +33 -0
- package/src/Table/useTableRowStatus.doc.mjs +1 -1
- package/src/Text/Heading.doc.mjs +3 -3
- package/src/Text/Text.doc.mjs +1 -1
- package/src/Text/Text.test.tsx +42 -0
- package/src/Text/Text.tsx +25 -1
- package/src/Text/index.ts +2 -6
- package/src/TextArea/TextArea.doc.mjs +23 -2
- package/src/TextArea/TextArea.test.tsx +132 -4
- package/src/TextArea/TextArea.tsx +161 -67
- package/src/TextInput/TextInput.doc.mjs +21 -0
- package/src/TextInput/TextInput.test.tsx +150 -0
- package/src/TextInput/TextInput.tsx +28 -25
- package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
- package/src/Thumbnail/Thumbnail.test.tsx +24 -1
- package/src/Thumbnail/Thumbnail.tsx +28 -35
- package/src/TimeInput/TimeInput.doc.mjs +21 -0
- package/src/TimeInput/TimeInput.test.tsx +23 -0
- package/src/TimeInput/TimeInput.tsx +24 -25
- package/src/Timestamp/Timestamp.doc.mjs +32 -6
- package/src/Timestamp/Timestamp.test.tsx +371 -0
- package/src/Timestamp/Timestamp.tsx +96 -93
- package/src/Timestamp/formatInstant.ts +218 -0
- package/src/Timestamp/index.ts +4 -0
- package/src/Timestamp/tooltipEntries.test.ts +552 -0
- package/src/Timestamp/tooltipEntries.ts +210 -0
- package/src/Toast/ToastViewport.test.tsx +218 -8
- package/src/Toast/ToastViewport.tsx +71 -12
- package/src/ToggleButton/ToggleButton.test.tsx +24 -0
- package/src/ToggleButton/ToggleButton.tsx +19 -1
- package/src/Token/Token.test.tsx +166 -15
- package/src/Token/Token.tsx +104 -47
- package/src/Token/TokenLink.tsx +107 -0
- package/src/Token/index.ts +1 -1
- package/src/Tokenizer/Tokenizer.doc.mjs +23 -1
- package/src/Tokenizer/Tokenizer.test.tsx +23 -0
- package/src/Tokenizer/Tokenizer.tsx +14 -2
- package/src/Tooltip/Tooltip.test.tsx +46 -0
- package/src/Tooltip/useTooltip.tsx +20 -0
- package/src/TopNav/TopNav.test.tsx +93 -2
- package/src/TopNav/TopNavItem.doc.mjs +3 -3
- package/src/TopNav/TopNavItem.tsx +35 -6
- package/src/TopNav/TopNavMegaMenu.tsx +26 -2
- package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +1 -1
- package/src/TreeList/TreeList.doc.mjs +6 -0
- package/src/TreeList/TreeList.test.tsx +50 -10
- package/src/TreeList/TreeList.tsx +5 -0
- package/src/TreeList/TreeListBranches.tsx +8 -6
- package/src/TreeList/TreeListItem.tsx +33 -11
- package/src/Typeahead/BaseTypeahead.tsx +42 -15
- package/src/Typeahead/Typeahead.doc.mjs +13 -0
- package/src/Typeahead/Typeahead.test.tsx +112 -0
- package/src/Typeahead/Typeahead.tsx +17 -1
- package/src/Typeahead/TypeaheadItem.tsx +2 -1
- package/src/__tests__/babelPluginAddExtensions.test.ts +190 -0
- package/src/__tests__/forcedColors.ts +60 -0
- package/src/__tests__/inputWidthContract.test.tsx +62 -0
- package/src/doc-mjs.d.ts +19 -0
- package/src/hooks/containerReveal.pool.stylex.ts +464 -0
- package/src/hooks/index.ts +13 -0
- package/src/hooks/isRtlElement.ts +27 -0
- package/src/hooks/useAnnounce.test.tsx +75 -2
- package/src/hooks/useAnnounce.ts +47 -1
- package/src/hooks/useContainerReveal.test.tsx +116 -0
- package/src/hooks/useContainerReveal.ts +180 -0
- package/src/hooks/useFocusTrap.test.tsx +104 -0
- package/src/hooks/useFocusTrap.ts +61 -12
- package/src/hooks/useGridFocus.doc.mjs +3 -3
- package/src/hooks/useGridFocus.test.tsx +41 -0
- package/src/hooks/useGridFocus.ts +21 -10
- package/src/hooks/useHotkeys.ts +9 -0
- package/src/hooks/useInputStatusIcon.test.tsx +221 -0
- package/src/hooks/useInputStatusIcon.tsx +235 -0
- package/src/hooks/useKeyboardHint.test.tsx +22 -0
- package/src/hooks/useKeyboardHint.tsx +6 -2
- package/src/hooks/useListFocus.doc.mjs +25 -3
- package/src/hooks/useListFocus.test.tsx +153 -1
- package/src/hooks/useListFocus.ts +102 -9
- package/src/i18n/InternationalizationContext.ts +4 -0
- package/src/i18n/InternationalizationProvider.tsx +12 -2
- package/src/i18n/__tests__/getLocaleDirection.test.ts +40 -0
- package/src/i18n/__tests__/useDirection.test.tsx +66 -0
- package/src/i18n/getLocaleDirection.ts +29 -0
- package/src/i18n/index.ts +6 -0
- package/src/i18n/useDirection.ts +29 -0
- package/src/reset.css +0 -26
- package/src/reset.test.ts +60 -0
- package/src/theme/derivedVarRegistry.test.ts +1 -0
- package/src/theme/derivedVarRegistry.ts +6 -0
- package/src/theme/generateThemeRules.test.ts +42 -0
- package/src/theme/index.ts +4 -8
- package/src/theme/types.ts +43 -13
- package/src/utils/index.ts +2 -0
- package/src/utils/plainDate.test.ts +24 -6
- package/src/utils/plainDate.ts +18 -4
- package/src/utils/rtlStyles.ts +27 -0
- package/dist/Thumbnail/thumbnail.markers.stylex.d.ts +0 -9
- package/dist/Thumbnail/thumbnail.markers.stylex.d.ts.map +0 -1
- package/dist/Thumbnail/thumbnail.markers.stylex.js +0 -14
- package/src/Thumbnail/thumbnail.markers.stylex.ts +0 -12
package/src/Text/Text.tsx
CHANGED
|
@@ -23,6 +23,7 @@ import type {
|
|
|
23
23
|
BuiltinTextType,
|
|
24
24
|
TextSize,
|
|
25
25
|
TextColor,
|
|
26
|
+
BuiltinTextColor,
|
|
26
27
|
TextWeight,
|
|
27
28
|
TextDisplay,
|
|
28
29
|
TextJustify,
|
|
@@ -185,6 +186,24 @@ function resolveStyleType(type: TextType): BuiltinTextType {
|
|
|
185
186
|
return 'body';
|
|
186
187
|
}
|
|
187
188
|
|
|
189
|
+
/**
|
|
190
|
+
* Resolve the StyleX baseline color. Built-in colors map to their own color
|
|
191
|
+
* style; custom (theme-defined) colors fall back to the `primary` baseline —
|
|
192
|
+
* their actual color comes from theme CSS (`.astryx-text.<color>` /
|
|
193
|
+
* `.astryx-heading.<color>`), which the rendered `color` class targets.
|
|
194
|
+
*
|
|
195
|
+
* Exported so Heading applies the same custom-color fallback as Text.
|
|
196
|
+
*/
|
|
197
|
+
export function resolveStyleColor(color: TextColor): BuiltinTextColor {
|
|
198
|
+
// The `in` guard is a runtime check for consumer-augmented custom colors
|
|
199
|
+
// (which widen `TextColor` beyond the built-ins via TextColorMap); within
|
|
200
|
+
// core the two types coincide, so no cast is needed.
|
|
201
|
+
if (color in colorStyles) {
|
|
202
|
+
return color;
|
|
203
|
+
}
|
|
204
|
+
return 'primary';
|
|
205
|
+
}
|
|
206
|
+
|
|
188
207
|
/**
|
|
189
208
|
* Semantic text component. Renders text with type-based styling from the theme.
|
|
190
209
|
*
|
|
@@ -228,6 +247,11 @@ export function Text({
|
|
|
228
247
|
// Resolve style type — custom types fall back to 'body' for StyleX baseline
|
|
229
248
|
const styleType = resolveStyleType(type);
|
|
230
249
|
|
|
250
|
+
// Resolve style color — custom colors fall back to 'primary' for StyleX
|
|
251
|
+
// baseline; the real color comes from theme CSS via the rendered `color`
|
|
252
|
+
// class (see themeProps below).
|
|
253
|
+
const styleColor = resolveStyleColor(resolvedColor);
|
|
254
|
+
|
|
231
255
|
// Resolve wordBreak with smart default
|
|
232
256
|
const resolvedWordBreak =
|
|
233
257
|
wordBreak ?? (maxLines === 1 ? 'break-all' : 'break-word');
|
|
@@ -257,7 +281,7 @@ export function Text({
|
|
|
257
281
|
{...mergeProps(
|
|
258
282
|
themeProps('text', {type, size, color: resolvedColor}),
|
|
259
283
|
stylex.props(
|
|
260
|
-
colorStyles[
|
|
284
|
+
colorStyles[styleColor],
|
|
261
285
|
sizeByTypeStyles[styleType],
|
|
262
286
|
size && sizeStyles[size],
|
|
263
287
|
defaultWeightByTypeStyles[styleType],
|
package/src/Text/index.ts
CHANGED
|
@@ -7,12 +7,7 @@
|
|
|
7
7
|
* @position Entry point for Text components
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
export {
|
|
11
|
-
Text,
|
|
12
|
-
type TextProps,
|
|
13
|
-
type TextType,
|
|
14
|
-
type TextSize,
|
|
15
|
-
} from './Text';
|
|
10
|
+
export {Text, type TextProps, type TextType, type TextSize} from './Text';
|
|
16
11
|
export {
|
|
17
12
|
Heading,
|
|
18
13
|
type HeadingProps,
|
|
@@ -23,6 +18,7 @@ export {
|
|
|
23
18
|
// Re-export shared types from theme for convenience
|
|
24
19
|
export type {
|
|
25
20
|
TextColor,
|
|
21
|
+
TextColorMap,
|
|
26
22
|
TextWeight,
|
|
27
23
|
TextDisplay,
|
|
28
24
|
TextJustify,
|
|
@@ -98,7 +98,7 @@ export const docs = {
|
|
|
98
98
|
name: 'maxLength',
|
|
99
99
|
type: 'number',
|
|
100
100
|
description:
|
|
101
|
-
'Maximum number of characters allowed. When set, a character counter (current/max) is displayed
|
|
101
|
+
'Maximum number of characters allowed. When set, a character counter (current/max) is displayed inside the input container, anchored to the bottom-right beneath the text. Does not enforce the limit natively; when exceeded the counter turns red and shows a warning icon (a non-color cue), and screen-reader users hear the remaining/over-limit count announced.',
|
|
102
102
|
},
|
|
103
103
|
{
|
|
104
104
|
name: 'status',
|
|
@@ -106,6 +106,13 @@ export const docs = {
|
|
|
106
106
|
description:
|
|
107
107
|
'Status indicator that applies a colored border and icon. An optional message is displayed in a floating box below the textarea.',
|
|
108
108
|
},
|
|
109
|
+
{
|
|
110
|
+
name: 'statusVariant',
|
|
111
|
+
type: "'attached' | 'detached' | 'tooltip'",
|
|
112
|
+
description:
|
|
113
|
+
'How the status message is placed relative to the input. attached overlaps directly below the input (bordered treatment); detached floats below as a separate element with spacing; tooltip hides the message box and surfaces it in a tooltip on the status icon.',
|
|
114
|
+
default: "'attached'",
|
|
115
|
+
},
|
|
109
116
|
{
|
|
110
117
|
name: 'labelTooltip',
|
|
111
118
|
type: 'string',
|
|
@@ -147,6 +154,12 @@ export const docs = {
|
|
|
147
154
|
description:
|
|
148
155
|
'HTML name attribute for the textarea element, useful for form submissions.',
|
|
149
156
|
},
|
|
157
|
+
{
|
|
158
|
+
name: 'width',
|
|
159
|
+
type: 'SizeValue',
|
|
160
|
+
description:
|
|
161
|
+
'Width of the field (number = pixels, string used as-is, e.g. "100%"). Sizes the whole field (label, control, and status) so they stay aligned.',
|
|
162
|
+
},
|
|
150
163
|
{
|
|
151
164
|
name: 'onFocus',
|
|
152
165
|
type: '(e: FocusEvent<HTMLTextAreaElement>) => void',
|
|
@@ -279,6 +292,13 @@ export const docsZh = {
|
|
|
279
292
|
type: "{ type: 'warning' | 'error' | 'success'; message?: string }",
|
|
280
293
|
description: '应用彩色边框和图标的状态指示器。可选消息显示在文本域下方的浮动框中。',
|
|
281
294
|
},
|
|
295
|
+
{
|
|
296
|
+
name: 'statusVariant',
|
|
297
|
+
type: "'attached' | 'detached' | 'tooltip'",
|
|
298
|
+
description:
|
|
299
|
+
'状态消息相对于输入框的放置方式。attached 直接叠加在输入框下方(带边框处理);detached 作为独立元素浮于下方并留有间距;tooltip 隐藏消息框,并在状态图标上以提示气泡形式显示。',
|
|
300
|
+
default: "'attached'",
|
|
301
|
+
},
|
|
282
302
|
{
|
|
283
303
|
name: 'labelTooltip',
|
|
284
304
|
type: 'string',
|
|
@@ -387,8 +407,9 @@ export const docsDense = {
|
|
|
387
407
|
isLoading: 'Loading state w/ spinner inside input.',
|
|
388
408
|
placeholder: 'Placeholder when textarea empty.',
|
|
389
409
|
rows: 'Visible text rows.',
|
|
390
|
-
maxLength: 'Max chars allowed. Shows counter (current/max)
|
|
410
|
+
maxLength: 'Max chars allowed. Shows counter (current/max) inside the container, bottom-right beneath the text. No native enforcement; over-limit shows red + a warning icon and is announced to screen readers.',
|
|
391
411
|
status: 'Colored border+icon status. Optional floating message below textarea.',
|
|
412
|
+
statusVariant: 'How status message is placed: attached overlaps below input; detached floats below w/ spacing; tooltip hides the box and shows it on the status icon.',
|
|
392
413
|
labelTooltip: 'Tooltip in info icon at label end.',
|
|
393
414
|
startIcon: 'Icon inside leading edge of textarea wrapper.',
|
|
394
415
|
hasSpellCheck: 'Enables/disables browser spell checking.',
|
|
@@ -335,6 +335,23 @@ describe('TextArea', () => {
|
|
|
335
335
|
expect(messageElement).toHaveAttribute('id');
|
|
336
336
|
expect(describedBy).toContain(messageElement.id);
|
|
337
337
|
});
|
|
338
|
+
|
|
339
|
+
it('shows both the loading spinner and the status icon while busy', () => {
|
|
340
|
+
const {container} = render(
|
|
341
|
+
<TextArea
|
|
342
|
+
label="Description"
|
|
343
|
+
value=""
|
|
344
|
+
onChange={() => {}}
|
|
345
|
+
isLoading
|
|
346
|
+
status={{type: 'error'}}
|
|
347
|
+
/>,
|
|
348
|
+
);
|
|
349
|
+
// Matches the other inputs: spinner (role="status") and the status icon
|
|
350
|
+
// render side by side in the end slot, not mutually exclusively.
|
|
351
|
+
expect(screen.getByRole('status')).toBeInTheDocument();
|
|
352
|
+
// Status icon svg is also present alongside the spinner.
|
|
353
|
+
expect(container.querySelectorAll('svg').length).toBeGreaterThan(0);
|
|
354
|
+
});
|
|
338
355
|
});
|
|
339
356
|
|
|
340
357
|
it('renders tooltip info icon when labelTooltip is provided', () => {
|
|
@@ -506,7 +523,70 @@ describe('TextArea', () => {
|
|
|
506
523
|
expect(screen.getByText('5/50')).toBeInTheDocument();
|
|
507
524
|
});
|
|
508
525
|
|
|
509
|
-
it('
|
|
526
|
+
it('announces remaining characters politely as the value nears the limit', async () => {
|
|
527
|
+
const user = userEvent.setup();
|
|
528
|
+
function Wrapper() {
|
|
529
|
+
const [val, setVal] = useState('x'.repeat(44));
|
|
530
|
+
return (
|
|
531
|
+
<TextArea
|
|
532
|
+
label="Description"
|
|
533
|
+
value={val}
|
|
534
|
+
onChange={setVal}
|
|
535
|
+
maxLength={50}
|
|
536
|
+
/>
|
|
537
|
+
);
|
|
538
|
+
}
|
|
539
|
+
render(<Wrapper />);
|
|
540
|
+
// Type one more char to cross into the "near the limit" zone (45/50).
|
|
541
|
+
await user.type(screen.getByRole('textbox'), 'x');
|
|
542
|
+
const politeRegion = () =>
|
|
543
|
+
document.querySelector('[data-astryx-live-region="polite"]');
|
|
544
|
+
await waitFor(() => {
|
|
545
|
+
expect(politeRegion()).toHaveTextContent('5 characters remaining');
|
|
546
|
+
});
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
it('announces over-limit assertively once the value exceeds the max', async () => {
|
|
550
|
+
const user = userEvent.setup();
|
|
551
|
+
function Wrapper() {
|
|
552
|
+
const [val, setVal] = useState('x'.repeat(50));
|
|
553
|
+
return (
|
|
554
|
+
<TextArea
|
|
555
|
+
label="Description"
|
|
556
|
+
value={val}
|
|
557
|
+
onChange={setVal}
|
|
558
|
+
maxLength={50}
|
|
559
|
+
/>
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
render(<Wrapper />);
|
|
563
|
+
await user.type(screen.getByRole('textbox'), 'x');
|
|
564
|
+
const assertiveRegion = () =>
|
|
565
|
+
document.querySelector('[data-astryx-live-region="assertive"]');
|
|
566
|
+
await waitFor(() => {
|
|
567
|
+
expect(assertiveRegion()).toHaveTextContent(
|
|
568
|
+
'1 character over the limit',
|
|
569
|
+
);
|
|
570
|
+
});
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
it('shows a non-color over-limit indicator icon when exceeded', () => {
|
|
574
|
+
const {container} = render(
|
|
575
|
+
<TextArea
|
|
576
|
+
label="Description"
|
|
577
|
+
value={'x'.repeat(55)}
|
|
578
|
+
onChange={() => {}}
|
|
579
|
+
maxLength={50}
|
|
580
|
+
/>,
|
|
581
|
+
);
|
|
582
|
+
// The counter renders a warning icon (a shape cue) alongside the red
|
|
583
|
+
// count, so the over-limit state is not conveyed by color alone.
|
|
584
|
+
const counter = screen.getByText('55/50').closest('div');
|
|
585
|
+
expect(counter?.querySelector('svg')).toBeInTheDocument();
|
|
586
|
+
expect(container).toBeInTheDocument();
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
it('does not show the over-limit indicator icon within the limit', () => {
|
|
510
590
|
render(
|
|
511
591
|
<TextArea
|
|
512
592
|
label="Description"
|
|
@@ -515,9 +595,8 @@ describe('TextArea', () => {
|
|
|
515
595
|
maxLength={50}
|
|
516
596
|
/>,
|
|
517
597
|
);
|
|
518
|
-
const
|
|
519
|
-
expect(
|
|
520
|
-
expect(liveRegion).toHaveTextContent('5 characters remaining');
|
|
598
|
+
const counter = screen.getByText('45/50').closest('div');
|
|
599
|
+
expect(counter?.querySelector('svg')).not.toBeInTheDocument();
|
|
521
600
|
});
|
|
522
601
|
|
|
523
602
|
it('counter is linked to textarea via aria-describedby', () => {
|
|
@@ -535,6 +614,22 @@ describe('TextArea', () => {
|
|
|
535
614
|
expect(counter).toHaveAttribute('id');
|
|
536
615
|
expect(describedBy).toContain(counter.id);
|
|
537
616
|
});
|
|
617
|
+
|
|
618
|
+
it('renders the counter inside the input container (same wrapper as textarea)', () => {
|
|
619
|
+
render(
|
|
620
|
+
<TextArea
|
|
621
|
+
label="Description"
|
|
622
|
+
value="Hello"
|
|
623
|
+
onChange={() => {}}
|
|
624
|
+
maxLength={50}
|
|
625
|
+
/>,
|
|
626
|
+
);
|
|
627
|
+
const textarea = screen.getByRole('textbox');
|
|
628
|
+
const counter = screen.getByText('5/50');
|
|
629
|
+
// The counter now lives inside the bordered input container as a sibling
|
|
630
|
+
// overlay of the textarea, not below it as an out-of-container element.
|
|
631
|
+
expect(textarea.parentElement).toBe(counter.parentElement);
|
|
632
|
+
});
|
|
538
633
|
});
|
|
539
634
|
|
|
540
635
|
describe('hasAutoFocus prop', () => {
|
|
@@ -736,3 +831,36 @@ describe('TextArea', () => {
|
|
|
736
831
|
});
|
|
737
832
|
});
|
|
738
833
|
});
|
|
834
|
+
|
|
835
|
+
describe('TextArea statusVariant forwarding', () => {
|
|
836
|
+
it('defaults to attached (status renders with data-variant="attached")', () => {
|
|
837
|
+
const {container} = render(
|
|
838
|
+
<TextArea
|
|
839
|
+
label="Bio"
|
|
840
|
+
value=""
|
|
841
|
+
onChange={() => {}}
|
|
842
|
+
status={{type: 'error', message: 'Required'}}
|
|
843
|
+
/>,
|
|
844
|
+
);
|
|
845
|
+
expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
|
|
846
|
+
'data-variant',
|
|
847
|
+
'attached',
|
|
848
|
+
);
|
|
849
|
+
});
|
|
850
|
+
|
|
851
|
+
it('forwards statusVariant="detached" to the underlying Field status', () => {
|
|
852
|
+
const {container} = render(
|
|
853
|
+
<TextArea
|
|
854
|
+
label="Bio"
|
|
855
|
+
value=""
|
|
856
|
+
onChange={() => {}}
|
|
857
|
+
status={{type: 'error', message: 'Required'}}
|
|
858
|
+
statusVariant="detached"
|
|
859
|
+
/>,
|
|
860
|
+
);
|
|
861
|
+
expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
|
|
862
|
+
'data-variant',
|
|
863
|
+
'detached',
|
|
864
|
+
);
|
|
865
|
+
});
|
|
866
|
+
});
|
|
@@ -21,13 +21,13 @@ import {
|
|
|
21
21
|
useOptimistic,
|
|
22
22
|
useTransition,
|
|
23
23
|
useRef,
|
|
24
|
+
useCallback,
|
|
24
25
|
type ChangeEvent,
|
|
25
26
|
type ClipboardEvent,
|
|
26
27
|
type FocusEvent,
|
|
27
28
|
type ReactNode,
|
|
28
29
|
} from 'react';
|
|
29
30
|
import * as stylex from '@stylexjs/stylex';
|
|
30
|
-
import type {IconName} from '../Icon';
|
|
31
31
|
import {
|
|
32
32
|
colorVars,
|
|
33
33
|
spacingVars,
|
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
inputStatusBorderStyles,
|
|
41
41
|
inputStatusHoverShadowStyles,
|
|
42
42
|
inputStatusFocusWithinStyles,
|
|
43
|
+
type FieldStatusVariant,
|
|
43
44
|
} from '../Field';
|
|
44
45
|
import {Icon, renderIconSlot, type IconType} from '../Icon';
|
|
45
46
|
import {Spinner} from '../Spinner';
|
|
@@ -48,25 +49,40 @@ import {mergeProps, mergeRefs} from '../utils';
|
|
|
48
49
|
import type {BaseProps} from '../BaseProps';
|
|
49
50
|
import type {SizeValue} from '../utils/types';
|
|
50
51
|
import {useInputContainer} from '../hooks/useInputContainer';
|
|
52
|
+
import {useInputStatusIcon} from '../hooks/useInputStatusIcon';
|
|
53
|
+
import {useAnnounce} from '../hooks/useAnnounce';
|
|
51
54
|
import {useSize} from '../SizeContext/SizeContext';
|
|
52
55
|
import {themeProps} from '../utils/themeProps';
|
|
53
|
-
import {
|
|
56
|
+
import {useTranslator} from '../i18n';
|
|
54
57
|
|
|
55
58
|
const COUNTER_WARNING_THRESHOLD = 0.8;
|
|
56
59
|
|
|
57
60
|
const styles = stylex.create({
|
|
61
|
+
// The wrapper is a bare positioning context. The <textarea> spans the full
|
|
62
|
+
// container and carries its own internal padding; icons, status/spinner, and
|
|
63
|
+
// the character counter are absolutely-positioned overlays anchored to the
|
|
64
|
+
// container corners. This keeps the native resize grip in the true
|
|
65
|
+
// bottom-right corner and lets the scrollbar represent the full input area.
|
|
58
66
|
wrapper: {
|
|
59
67
|
zIndex: 1,
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
display: 'block',
|
|
69
|
+
padding: 0,
|
|
70
|
+
// Internal inline padding for the textarea's text. Defined on the wrapper
|
|
71
|
+
// so the counter (a sibling overlay) inherits the same value and stays
|
|
72
|
+
// aligned with the text edge. Kept as an internal var so it can later be
|
|
73
|
+
// driven by a theme "padding" translation without touching either child.
|
|
74
|
+
'--_textarea-inline-padding': spacingVars['--spacing-2'],
|
|
62
75
|
},
|
|
63
76
|
textarea: {
|
|
64
77
|
display: 'block',
|
|
65
|
-
|
|
78
|
+
width: '100%',
|
|
79
|
+
boxSizing: 'border-box',
|
|
66
80
|
minWidth: 0,
|
|
67
81
|
borderWidth: 0,
|
|
68
82
|
borderStyle: 'none',
|
|
69
|
-
padding
|
|
83
|
+
// Base internal padding; overlays get their own reserved space below.
|
|
84
|
+
paddingBlock: spacingVars['--spacing-1'],
|
|
85
|
+
paddingInline: 'var(--_textarea-inline-padding)',
|
|
70
86
|
fontFamily: typographyVars['--font-family-body'],
|
|
71
87
|
fontSize: {
|
|
72
88
|
default: typeScaleVars['--text-body-size'],
|
|
@@ -84,29 +100,59 @@ const styles = stylex.create({
|
|
|
84
100
|
textareaDisabled: {
|
|
85
101
|
cursor: 'not-allowed',
|
|
86
102
|
},
|
|
103
|
+
// Reserve start padding so text clears the start icon.
|
|
104
|
+
// inline inset + 16px icon (sm) + 8px gap
|
|
105
|
+
textareaWithStartIcon: {
|
|
106
|
+
paddingInlineStart: `calc(var(--_textarea-inline-padding) + ${spacingVars['--spacing-6']})`,
|
|
107
|
+
},
|
|
108
|
+
// Reserve end padding so text clears the status icon / spinner overlay.
|
|
109
|
+
// inline inset + 20px icon (md) + 4px gap
|
|
110
|
+
textareaWithStatus: {
|
|
111
|
+
paddingInlineEnd: `calc(var(--_textarea-inline-padding) + ${spacingVars['--spacing-6']})`,
|
|
112
|
+
},
|
|
113
|
+
// Reserve wider end padding when the spinner AND status icon both show.
|
|
114
|
+
// inline inset + 16px spinner + 8px gap + 20px icon + 4px gap
|
|
115
|
+
textareaWithBusyStatus: {
|
|
116
|
+
paddingInlineEnd: `calc(var(--_textarea-inline-padding) + ${spacingVars['--spacing-12']})`,
|
|
117
|
+
},
|
|
118
|
+
// Reserve bottom padding so text clears the character counter overlay.
|
|
119
|
+
textareaWithCounter: {
|
|
120
|
+
paddingBottom: spacingVars['--spacing-7'],
|
|
121
|
+
},
|
|
122
|
+
startIcon: {
|
|
123
|
+
position: 'absolute',
|
|
124
|
+
top: spacingVars['--spacing-2'],
|
|
125
|
+
insetInlineStart: 'var(--_textarea-inline-padding)',
|
|
126
|
+
pointerEvents: 'none',
|
|
127
|
+
display: 'flex',
|
|
128
|
+
},
|
|
129
|
+
endSlot: {
|
|
130
|
+
position: 'absolute',
|
|
131
|
+
top: spacingVars['--spacing-2'],
|
|
132
|
+
insetInlineEnd: 'var(--_textarea-inline-padding)',
|
|
133
|
+
pointerEvents: 'none',
|
|
134
|
+
display: 'flex',
|
|
135
|
+
alignItems: 'center',
|
|
136
|
+
gap: spacingVars['--spacing-2'],
|
|
137
|
+
},
|
|
138
|
+
// Character counter lives inside the input container, anchored bottom-right
|
|
139
|
+
// underneath the textarea, aligned to the same inline inset as the text.
|
|
87
140
|
counter: {
|
|
141
|
+
position: 'absolute',
|
|
142
|
+
bottom: spacingVars['--spacing-1'],
|
|
143
|
+
insetInlineEnd: 'var(--_textarea-inline-padding)',
|
|
144
|
+
pointerEvents: 'none',
|
|
88
145
|
display: 'flex',
|
|
89
|
-
|
|
90
|
-
|
|
146
|
+
alignItems: 'center',
|
|
147
|
+
gap: spacingVars['--spacing-1'],
|
|
91
148
|
fontFamily: typographyVars['--font-family-body'],
|
|
92
149
|
fontSize: typeScaleVars['--text-supporting-size'],
|
|
150
|
+
lineHeight: typeScaleVars['--text-supporting-leading'],
|
|
93
151
|
color: colorVars['--color-text-secondary'],
|
|
94
152
|
},
|
|
95
153
|
counterError: {
|
|
96
154
|
color: colorVars['--color-error'],
|
|
97
155
|
},
|
|
98
|
-
statusIcon: {
|
|
99
|
-
position: 'absolute',
|
|
100
|
-
top: spacingVars['--spacing-2'],
|
|
101
|
-
insetInlineEnd: spacingVars['--spacing-2'],
|
|
102
|
-
pointerEvents: 'none',
|
|
103
|
-
display: 'flex',
|
|
104
|
-
},
|
|
105
|
-
textareaWithStatus: {
|
|
106
|
-
// Reserve space so text doesn't flow under the absolutely-positioned icon.
|
|
107
|
-
// 20px (icon md) + 4px gap = 24px (--spacing-6)
|
|
108
|
-
paddingInlineEnd: spacingVars['--spacing-6'],
|
|
109
|
-
},
|
|
110
156
|
});
|
|
111
157
|
|
|
112
158
|
const textareaSizeStyles = stylex.create({
|
|
@@ -217,6 +263,14 @@ export interface TextAreaProps extends Omit<
|
|
|
217
263
|
* If message is provided, displays a floating message box below the textarea.
|
|
218
264
|
*/
|
|
219
265
|
status?: TextAreaStatus;
|
|
266
|
+
/**
|
|
267
|
+
* How the status message is placed relative to the input.
|
|
268
|
+
* - 'attached': message overlaps directly below the input (bordered treatment)
|
|
269
|
+
* - 'detached': message floats below as a separate element with spacing
|
|
270
|
+
* - 'tooltip': no message box; the status icon becomes a focusable info-tip button that reveals the message on hover, keyboard focus, or tap
|
|
271
|
+
* @default 'attached'
|
|
272
|
+
*/
|
|
273
|
+
statusVariant?: FieldStatusVariant;
|
|
220
274
|
/**
|
|
221
275
|
* Width of the field. Numbers are treated as pixels, strings are used as-is
|
|
222
276
|
* (e.g. `'100%'`). Sizes the whole field (label, control, and status) so they
|
|
@@ -298,6 +352,7 @@ export function TextArea({
|
|
|
298
352
|
isDisabled = false,
|
|
299
353
|
disabledMessage,
|
|
300
354
|
status,
|
|
355
|
+
statusVariant = 'attached',
|
|
301
356
|
labelTooltip,
|
|
302
357
|
startIcon,
|
|
303
358
|
hasSpellCheck = true,
|
|
@@ -316,12 +371,17 @@ export function TextArea({
|
|
|
316
371
|
...rest
|
|
317
372
|
}: TextAreaProps) {
|
|
318
373
|
const size = useSize(sizeProp, 'md');
|
|
374
|
+
const t = useTranslator();
|
|
375
|
+
const announce = useAnnounce();
|
|
319
376
|
const id = useId();
|
|
320
377
|
const descriptionID = useId();
|
|
321
378
|
const statusMessageID = useId();
|
|
322
379
|
const counterID = useId();
|
|
323
380
|
const textareaRef = useRef<HTMLTextAreaElement | null>(null);
|
|
324
381
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
382
|
+
// Tracks the counter "zone" last announced (under / near / over) so we only
|
|
383
|
+
// announce on a zone change, not on every keystroke.
|
|
384
|
+
const counterZoneRef = useRef<'under' | 'near' | 'over' | null>(null);
|
|
325
385
|
|
|
326
386
|
const [, startTransition] = useTransition();
|
|
327
387
|
const [optimisticValue, setOptimisticValue] = useOptimistic(value);
|
|
@@ -341,31 +401,62 @@ export function TextArea({
|
|
|
341
401
|
isEnabled: showsDisabledMessage,
|
|
342
402
|
});
|
|
343
403
|
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
const statusIconColorMap: Record<
|
|
351
|
-
TextAreaStatusType,
|
|
352
|
-
'warning' | 'error' | 'success'
|
|
353
|
-
> = {
|
|
354
|
-
warning: 'warning',
|
|
355
|
-
error: 'error',
|
|
356
|
-
success: 'success',
|
|
357
|
-
};
|
|
404
|
+
const {statusIcon, describedBy: statusTooltipDescribedBy} =
|
|
405
|
+
useInputStatusIcon({
|
|
406
|
+
status,
|
|
407
|
+
statusVariant,
|
|
408
|
+
});
|
|
358
409
|
|
|
359
410
|
const ariaDescribedBy =
|
|
360
411
|
[
|
|
361
412
|
description ? descriptionID : null,
|
|
362
|
-
status?.message ? statusMessageID : null,
|
|
413
|
+
statusVariant !== 'tooltip' && status?.message ? statusMessageID : null,
|
|
414
|
+
// The tooltip variant renders no message box; describe the input by the
|
|
415
|
+
// tooltip's content instead so the status is still announced.
|
|
416
|
+
statusTooltipDescribedBy,
|
|
363
417
|
maxLength != null ? counterID : null,
|
|
364
418
|
showsDisabledMessage ? disabledMessageTooltip.describedBy : null,
|
|
365
419
|
]
|
|
366
420
|
.filter(Boolean)
|
|
367
421
|
.join(' ') || undefined;
|
|
368
422
|
|
|
423
|
+
// Announce the character-count status through the shared live region, but
|
|
424
|
+
// only when the value crosses into a new zone (near the limit / over the
|
|
425
|
+
// limit) so we don't speak on every keystroke. Over-limit is assertive (an
|
|
426
|
+
// error the user should hear immediately); nearing the limit is polite.
|
|
427
|
+
const announceCounter = useCallback(
|
|
428
|
+
(length: number) => {
|
|
429
|
+
if (maxLength == null) {
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
const zone: 'under' | 'near' | 'over' =
|
|
433
|
+
length > maxLength
|
|
434
|
+
? 'over'
|
|
435
|
+
: length >= maxLength * COUNTER_WARNING_THRESHOLD
|
|
436
|
+
? 'near'
|
|
437
|
+
: 'under';
|
|
438
|
+
if (zone === counterZoneRef.current) {
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
counterZoneRef.current = zone;
|
|
442
|
+
if (zone === 'over') {
|
|
443
|
+
announce(
|
|
444
|
+
t('@astryx.textArea.charactersOverLimit', {
|
|
445
|
+
count: length - maxLength,
|
|
446
|
+
}),
|
|
447
|
+
'assertive',
|
|
448
|
+
);
|
|
449
|
+
} else if (zone === 'near') {
|
|
450
|
+
announce(
|
|
451
|
+
t('@astryx.textArea.charactersRemaining', {
|
|
452
|
+
count: maxLength - length,
|
|
453
|
+
}),
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
},
|
|
457
|
+
[announce, t, maxLength],
|
|
458
|
+
);
|
|
459
|
+
|
|
369
460
|
const handleChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
|
|
370
461
|
// Value can't change while showing a disabled message (the field is
|
|
371
462
|
// read-only and non-native-disabled), but guard the handler too so the
|
|
@@ -374,6 +465,7 @@ export function TextArea({
|
|
|
374
465
|
return;
|
|
375
466
|
}
|
|
376
467
|
const newValue = e.target.value;
|
|
468
|
+
announceCounter(newValue.length);
|
|
377
469
|
onChange?.(newValue, e);
|
|
378
470
|
if (changeAction && !e.defaultPrevented) {
|
|
379
471
|
startTransition(async () => {
|
|
@@ -412,6 +504,7 @@ export function TextArea({
|
|
|
412
504
|
}
|
|
413
505
|
: undefined
|
|
414
506
|
}
|
|
507
|
+
statusVariant={statusVariant}
|
|
415
508
|
labelTooltip={labelTooltip}
|
|
416
509
|
width={width}>
|
|
417
510
|
<div
|
|
@@ -429,18 +522,20 @@ export function TextArea({
|
|
|
429
522
|
stylex.props(
|
|
430
523
|
inputWrapperStyles.base,
|
|
431
524
|
styles.wrapper,
|
|
432
|
-
|
|
433
|
-
effectivelyDisabled && inputWrapperStyles.disabled,
|
|
525
|
+
isDisabled && inputWrapperStyles.disabled,
|
|
434
526
|
status && inputStatusBorderStyles[status.type],
|
|
435
|
-
status && inputStatusHoverShadowStyles[status.type],
|
|
527
|
+
status && !isDisabled && inputStatusHoverShadowStyles[status.type],
|
|
436
528
|
status && inputStatusFocusWithinStyles[status.type],
|
|
437
529
|
xstyle,
|
|
438
530
|
),
|
|
439
531
|
className,
|
|
440
532
|
style,
|
|
441
533
|
)}>
|
|
442
|
-
{startIcon &&
|
|
443
|
-
|
|
534
|
+
{startIcon && (
|
|
535
|
+
<span {...stylex.props(styles.startIcon)}>
|
|
536
|
+
{renderIconSlot(startIcon, {size: 'sm', color: 'secondary'})}
|
|
537
|
+
</span>
|
|
538
|
+
)}
|
|
444
539
|
<textarea
|
|
445
540
|
{...rest}
|
|
446
541
|
ref={mergeRefs(ref, textareaRef)}
|
|
@@ -473,38 +568,37 @@ export function TextArea({
|
|
|
473
568
|
aria-busy={isBusy || undefined}
|
|
474
569
|
{...stylex.props(
|
|
475
570
|
styles.textarea,
|
|
476
|
-
|
|
477
|
-
|
|
571
|
+
textareaSizeStyles[size],
|
|
572
|
+
isDisabled && styles.textareaDisabled,
|
|
573
|
+
Boolean(startIcon) && styles.textareaWithStartIcon,
|
|
574
|
+
(status || isBusy) && styles.textareaWithStatus,
|
|
575
|
+
isBusy && !!statusIcon && styles.textareaWithBusyStatus,
|
|
576
|
+
maxLength != null && styles.textareaWithCounter,
|
|
478
577
|
)}
|
|
479
578
|
/>
|
|
480
|
-
{isBusy
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
icon={statusIconMap[status.type]}
|
|
485
|
-
size="md"
|
|
486
|
-
color={statusIconColorMap[status.type]}
|
|
487
|
-
/>
|
|
579
|
+
{(isBusy || statusIcon) && (
|
|
580
|
+
<span {...stylex.props(styles.endSlot)}>
|
|
581
|
+
{isBusy && <Spinner size="sm" />}
|
|
582
|
+
{statusIcon}
|
|
488
583
|
</span>
|
|
489
584
|
)}
|
|
585
|
+
{maxLength != null && (
|
|
586
|
+
<div
|
|
587
|
+
id={counterID}
|
|
588
|
+
{...stylex.props(
|
|
589
|
+
styles.counter,
|
|
590
|
+
optimisticValue.length > maxLength && styles.counterError,
|
|
591
|
+
)}>
|
|
592
|
+
{optimisticValue.length > maxLength && (
|
|
593
|
+
// Non-color cue so the over-limit state isn't conveyed by the red
|
|
594
|
+
// color alone (WCAG 1.4.1). Decorative — the count text and the
|
|
595
|
+
// live-region announcement carry the meaning.
|
|
596
|
+
<Icon icon="warning" size="sm" />
|
|
597
|
+
)}
|
|
598
|
+
{optimisticValue.length}/{maxLength}
|
|
599
|
+
</div>
|
|
600
|
+
)}
|
|
490
601
|
</div>
|
|
491
|
-
{maxLength != null && (
|
|
492
|
-
<div
|
|
493
|
-
id={counterID}
|
|
494
|
-
{...stylex.props(
|
|
495
|
-
styles.counter,
|
|
496
|
-
optimisticValue.length > maxLength && styles.counterError,
|
|
497
|
-
)}>
|
|
498
|
-
{optimisticValue.length}/{maxLength}
|
|
499
|
-
<VisuallyHidden aria-live="polite">
|
|
500
|
-
{optimisticValue.length >= maxLength * COUNTER_WARNING_THRESHOLD
|
|
501
|
-
? optimisticValue.length > maxLength
|
|
502
|
-
? `${optimisticValue.length - maxLength} characters over limit`
|
|
503
|
-
: `${maxLength - optimisticValue.length} characters remaining`
|
|
504
|
-
: ''}
|
|
505
|
-
</VisuallyHidden>
|
|
506
|
-
</div>
|
|
507
|
-
)}
|
|
508
602
|
{showsDisabledMessage &&
|
|
509
603
|
disabledMessageTooltip.renderTooltip(disabledMessage)}
|
|
510
604
|
</Field>
|