@astryxdesign/core 0.5.2-canary.2da7a39 → 0.5.2-canary.2e51213
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/dist/AppShell/AppShell.d.ts +2 -2
- package/dist/AppShell/AppShell.d.ts.map +1 -1
- package/dist/AppShell/AppShell.js +10 -7
- package/dist/Banner/Banner.d.ts +5 -0
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +7 -2
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts +3 -2
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +22 -15
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +2 -2
- package/dist/Chat/ChatComposer.d.ts +2 -2
- package/dist/Chat/ChatComposer.d.ts.map +1 -1
- package/dist/Chat/ChatComposer.js +21 -2
- package/dist/Chat/ChatToolCalls.d.ts +1 -1
- package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.js +12 -34
- package/dist/Chat/useChatNewMessages.js +3 -3
- package/dist/CheckboxList/CheckboxListItem.d.ts +5 -3
- package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts +47 -13
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +269 -162
- package/dist/DateTimeInput/NativeDateSegment.d.ts +24 -0
- package/dist/DateTimeInput/NativeDateSegment.d.ts.map +1 -0
- package/dist/DateTimeInput/NativeDateSegment.js +235 -0
- package/dist/DateTimeInput/NativeTimeSegment.d.ts +30 -0
- package/dist/DateTimeInput/NativeTimeSegment.d.ts.map +1 -0
- package/dist/DateTimeInput/NativeTimeSegment.js +248 -0
- package/dist/DateTimeInput/index.d.ts +1 -1
- package/dist/DateTimeInput/index.d.ts.map +1 -1
- package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts +95 -0
- package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts.map +1 -0
- package/dist/DateTimeInput/nativePickerSegmentStyles.js +103 -0
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +2 -4
- package/dist/Field/Field.d.ts.map +1 -1
- package/dist/Field/Field.js +10 -4
- package/dist/Field/PanelSearchInput.d.ts.map +1 -1
- package/dist/Field/PanelSearchInput.js +3 -5
- package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
- package/dist/FieldStatus/FieldStatus.js +4 -2
- package/dist/Layer/useTouchTrigger.d.ts.map +1 -1
- package/dist/Layer/useTouchTrigger.js +2 -4
- package/dist/Layout/Layout.d.ts +7 -8
- package/dist/Layout/Layout.d.ts.map +1 -1
- package/dist/Layout/Layout.js +7 -8
- package/dist/MultiSelector/MultiSelector.d.ts +11 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +89 -45
- package/dist/RadioList/RadioListItem.d.ts +30 -5
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +17 -4
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +102 -44
- package/dist/Resizable/index.d.ts +2 -0
- package/dist/Resizable/index.d.ts.map +1 -1
- package/dist/Resizable/index.js +1 -0
- package/dist/Resizable/useResizable.d.ts +89 -8
- package/dist/Resizable/useResizable.d.ts.map +1 -1
- package/dist/Resizable/useResizable.js +521 -38
- package/dist/Resizable/utils.d.ts +35 -0
- package/dist/Resizable/utils.d.ts.map +1 -0
- package/dist/Resizable/utils.js +27 -0
- package/dist/Selector/Selector.d.ts +10 -0
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +105 -57
- package/dist/Selector/useSelectorPresentation.d.ts +1 -0
- package/dist/Selector/useSelectorPresentation.d.ts.map +1 -1
- package/dist/Selector/useSelectorPresentation.js +17 -2
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +3 -5
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +9 -10
- package/dist/Stepper/Step.d.ts +2 -1
- package/dist/Stepper/Step.d.ts.map +1 -1
- package/dist/Stepper/Step.js +75 -11
- package/dist/Stepper/Stepper.d.ts.map +1 -1
- package/dist/Stepper/Stepper.js +1 -0
- package/dist/TabList/TabList.d.ts.map +1 -1
- package/dist/TabList/TabList.js +2 -3
- package/dist/Table/index.d.ts +16 -1
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +4 -0
- package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
- package/dist/Table/plugins/columnResize/useTableColumnResize.js +8 -9
- package/dist/Table/plugins/rowStatus/index.d.ts +1 -1
- package/dist/Table/plugins/rowStatus/index.d.ts.map +1 -1
- package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts +11 -30
- package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -1
- package/dist/Table/plugins/rowStatus/useTableRowStatus.js +108 -62
- package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortable.js +9 -1
- package/dist/Text/useTruncation.d.ts +1 -1
- package/dist/Text/useTruncation.d.ts.map +1 -1
- package/dist/Text/useTruncation.js +8 -7
- package/dist/TimeInput/TimeInput.d.ts +16 -1
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +98 -60
- package/dist/TimeInput/index.d.ts +1 -1
- package/dist/TimeInput/index.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.d.ts +2 -2
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +4 -145
- package/dist/Timestamp/formatRelativeTime.d.ts +20 -0
- package/dist/Timestamp/formatRelativeTime.d.ts.map +1 -0
- package/dist/Timestamp/formatRelativeTime.js +81 -0
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +125 -77
- package/dist/Tokenizer/Tokenizer.d.ts +0 -38
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +94 -26
- package/dist/Tokenizer/useEndLaneReserve.d.ts +31 -0
- package/dist/Tokenizer/useEndLaneReserve.d.ts.map +1 -0
- package/dist/Tokenizer/useEndLaneReserve.js +136 -0
- package/dist/Tooltip/useTooltip.d.ts +1 -1
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +53 -42
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +85 -30
- package/dist/Typeahead/Typeahead.d.ts +0 -21
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +108 -67
- package/dist/Typeahead/busyIndicatorLane.d.ts +62 -0
- package/dist/Typeahead/busyIndicatorLane.d.ts.map +1 -0
- package/dist/Typeahead/busyIndicatorLane.js +98 -0
- package/dist/astryx.css +25 -7
- package/dist/hooks/useFocusReturnVisibility.d.ts.map +1 -1
- package/dist/hooks/useFocusReturnVisibility.js +3 -5
- package/dist/hooks/useIsomorphicLayoutEffect.d.ts +2 -2
- package/dist/hooks/useIsomorphicLayoutEffect.d.ts.map +1 -1
- package/dist/hooks/useOverflow.js +11 -11
- package/dist/hooks/useScrollOverflow.d.ts.map +1 -1
- package/dist/hooks/useScrollOverflow.js +7 -4
- package/dist/theme/defineTheme.d.ts +13 -0
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/defineTheme.js +7 -0
- package/dist/theme/generateThemeRules.d.ts.map +1 -1
- package/dist/theme/generateThemeRules.js +1 -1
- package/dist/theme/localTokens.d.ts +18 -0
- package/dist/theme/localTokens.d.ts.map +1 -0
- package/dist/theme/localTokens.js +162 -0
- package/dist/utils/interactionModality.d.ts +6 -28
- package/dist/utils/interactionModality.d.ts.map +1 -1
- package/dist/utils/interactionModality.js +72 -37
- package/dist/utils/sharedResizeObserver.d.ts +17 -9
- package/dist/utils/sharedResizeObserver.d.ts.map +1 -1
- package/dist/utils/sharedResizeObserver.js +54 -12
- package/locales/en.json +8 -0
- package/locales/pseudo.json +6 -0
- package/package.json +7 -2
- package/src/AlertDialog/useImperativeAlertDialog.test.tsx +163 -0
- package/src/AppShell/AppShell.doc.mjs +4 -3
- package/src/AppShell/AppShell.test.tsx +21 -0
- package/src/AppShell/AppShell.tsx +12 -7
- package/src/Banner/Banner.doc.mjs +5 -1
- package/src/Banner/Banner.tsx +12 -1
- package/src/BottomSheet/BottomSheet.doc.mjs +31 -0
- package/src/BottomSheet/BottomSheet.spec.md +178 -0
- package/src/Breadcrumbs/BreadcrumbItem.tsx +18 -9
- package/src/Breadcrumbs/Breadcrumbs.doc.mjs +4 -1
- package/src/Breadcrumbs/Breadcrumbs.test.tsx +12 -0
- package/src/Button/Button.doc.mjs +56 -0
- package/src/ButtonGroup/ButtonGroup.doc.mjs +47 -0
- package/src/Calendar/hooks/useCalendarConstraints.test.ts +153 -0
- package/src/Calendar/hooks/useCalendarDays.test.ts +265 -0
- package/src/Calendar/hooks/useCalendarNavigation.test.ts +252 -0
- package/src/Carousel/Carousel.test.tsx +83 -0
- package/src/Carousel/Carousel.tsx +8 -2
- package/src/Chat/ChatComposer.doc.mjs +7 -4
- package/src/Chat/ChatComposer.test.tsx +84 -2
- package/src/Chat/ChatComposer.tsx +45 -3
- package/src/Chat/ChatSendButton.test.tsx +189 -0
- package/src/Chat/ChatToolCalls.doc.mjs +1 -1
- package/src/Chat/ChatToolCalls.test.tsx +33 -1
- package/src/Chat/ChatToolCalls.tsx +13 -34
- package/src/Chat/useChatNewMessages.ts +3 -3
- package/src/Chat/useChatPasteAsToken.test.ts +191 -0
- package/src/CheckboxList/CheckboxList.doc.mjs +172 -31
- package/src/CheckboxList/CheckboxList.spec.md +215 -0
- package/src/CheckboxList/CheckboxList.test.tsx +45 -0
- package/src/CheckboxList/CheckboxListItem.doc.mjs +11 -9
- package/src/CheckboxList/CheckboxListItem.tsx +5 -3
- package/src/Collapsible/useCollapsible.test.tsx +236 -0
- package/src/CommandPalette/CommandPalette.doc.mjs +72 -0
- package/src/CommandPalette/CommandPalette.spec.md +224 -0
- package/src/CommandPalette/CommandPaletteEmpty.test.tsx +101 -0
- package/src/ComplexSelector/ComplexSelector.doc.mjs +42 -0
- package/src/ComplexSelector/ComplexSelector.spec.md +190 -0
- package/src/ContextMenu/ContextMenu.doc.mjs +48 -0
- package/src/ContextMenu/ContextMenu.spec.md +207 -0
- package/src/DateTimeInput/DateTimeInput.doc.mjs +43 -23
- package/src/DateTimeInput/DateTimeInput.tsx +388 -181
- package/src/DateTimeInput/DateTimeInputTouch.test.tsx +10 -2
- package/src/DateTimeInput/NativeDateSegment.tsx +308 -0
- package/src/DateTimeInput/NativePickerSegments.test.tsx +626 -0
- package/src/DateTimeInput/NativeTimeSegment.tsx +331 -0
- package/src/DateTimeInput/index.ts +1 -0
- package/src/DateTimeInput/nativePickerSegmentStyles.ts +120 -0
- package/src/Divider/Divider.doc.mjs +24 -0
- package/src/Divider/Divider.spec.md +157 -0
- package/src/DropdownMenu/DropdownMenu.doc.mjs +104 -0
- package/src/DropdownMenu/DropdownMenu.spec.md +246 -0
- package/src/DropdownMenu/DropdownMenu.tsx +2 -4
- package/src/Field/Field.doc.mjs +1 -0
- package/src/Field/Field.test.tsx +39 -0
- package/src/Field/Field.tsx +18 -1
- package/src/Field/InputClearButton.test.tsx +68 -6
- package/src/Field/PanelSearchInput.tsx +3 -5
- package/src/FieldStatus/FieldStatus.doc.mjs +21 -0
- package/src/FieldStatus/FieldStatus.spec.md +213 -0
- package/src/FieldStatus/FieldStatus.test.tsx +17 -0
- package/src/FieldStatus/FieldStatus.tsx +7 -2
- package/src/Grid/Grid.doc.mjs +19 -0
- package/src/Grid/Grid.spec.md +162 -0
- package/src/Icon/Icon.doc.mjs +196 -48
- package/src/Icon/Icon.spec.md +192 -0
- package/src/IconButton/IconButton.doc.mjs +38 -0
- package/src/Kbd/Kbd.doc.mjs +18 -0
- package/src/Kbd/Kbd.spec.md +142 -0
- package/src/Layer/useTouchTrigger.ts +2 -4
- package/src/Layout/Layout.doc.mjs +57 -17
- package/src/Layout/Layout.spec.md +235 -0
- package/src/Layout/Layout.test.tsx +1 -1
- package/src/Layout/Layout.tsx +7 -8
- package/src/Layout/LayoutHeader.doc.mjs +9 -0
- package/src/Layout/LayoutPanel.doc.mjs +4 -3
- package/src/Lightbox/Lightbox.doc.mjs +149 -33
- package/src/Lightbox/Lightbox.spec.md +198 -0
- package/src/Lightbox/useLightbox.test.tsx +263 -0
- package/src/Markdown/Markdown.doc.mjs +60 -0
- package/src/Markdown/Markdown.spec.md +207 -0
- package/src/MobileNav/MobileNav.doc.mjs +140 -32
- package/src/MobileNav/MobileNav.spec.md +209 -0
- package/src/MoreMenu/MoreMenu.doc.mjs +42 -0
- package/src/MoreMenu/MoreMenu.spec.md +222 -0
- package/src/MultiSelector/MultiSelector.doc.mjs +124 -1
- package/src/MultiSelector/MultiSelector.spec.md +293 -0
- package/src/MultiSelector/MultiSelector.test.tsx +305 -2
- package/src/MultiSelector/MultiSelector.tsx +124 -55
- package/src/NavIcon/NavIcon.doc.mjs +17 -0
- package/src/NavIcon/NavIcon.spec.md +134 -0
- package/src/NavMenu/NavHeadingMenu.spec.md +210 -0
- package/src/NavMenu/NavMenu.doc.mjs +37 -0
- package/src/Outline/Outline.doc.mjs +35 -0
- package/src/Outline/Outline.spec.md +162 -0
- package/src/OverflowList/OverflowList.doc.mjs +24 -0
- package/src/OverflowList/OverflowList.spec.md +157 -0
- package/src/Pagination/Pagination.doc.mjs +74 -0
- package/src/Pagination/Pagination.spec.md +226 -0
- package/src/Popover/Popover.doc.mjs +264 -65
- package/src/Popover/Popover.spec.md +421 -0
- package/src/Popover/usePopover.doc.mjs +208 -33
- package/src/PowerSearch/PowerSearch.spec.md +291 -0
- package/src/PowerSearch/PowerSearchFilterEditor.test.tsx +253 -0
- package/src/PowerSearch/PowerSearchToken.test.tsx +228 -0
- package/src/ProgressBar/ProgressBar.doc.mjs +198 -38
- package/src/ProgressBar/ProgressBar.spec.md +261 -0
- package/src/RadioList/RadioList.test.tsx +136 -2
- package/src/RadioList/RadioListItem.doc.mjs +30 -11
- package/src/RadioList/RadioListItem.tsx +44 -8
- package/src/Resizable/Resizable.doc.mjs +130 -24
- package/src/Resizable/Resizable.spec.md +246 -0
- package/src/Resizable/ResizeHandle.test.tsx +177 -56
- package/src/Resizable/ResizeHandle.tsx +128 -45
- package/src/Resizable/index.ts +2 -0
- package/src/Resizable/shippedDirections.test.ts +462 -0
- package/src/Resizable/useResizable.doc.mjs +124 -29
- package/src/Resizable/useResizable.test.ts +1277 -3
- package/src/Resizable/useResizable.ts +760 -48
- package/src/Resizable/utils.test.ts +74 -0
- package/src/Resizable/utils.ts +35 -0
- package/src/Section/Section.doc.mjs +17 -0
- package/src/Section/Section.spec.md +139 -0
- package/src/SegmentedControl/SegmentedControl.doc.mjs +83 -0
- package/src/SegmentedControl/SegmentedControl.spec.md +152 -0
- package/src/Selector/Selector.doc.mjs +133 -97
- package/src/Selector/Selector.spec.md +300 -0
- package/src/Selector/Selector.test.tsx +318 -1
- package/src/Selector/Selector.tsx +127 -60
- package/src/Selector/useSelectorPresentation.ts +24 -4
- package/src/SideNav/SideNav.test.tsx +3 -3
- package/src/SideNav/SideNavCollapseButton.doc.mjs +8 -0
- package/src/SideNav/SideNavItem.doc.mjs +1 -1
- package/src/Skeleton/Skeleton.doc.mjs +12 -0
- package/src/Skeleton/Skeleton.spec.md +127 -0
- package/src/Slider/Slider.doc.mjs +184 -30
- package/src/Slider/Slider.spec.md +224 -0
- package/src/Slider/Slider.tsx +2 -5
- package/src/Spinner/Spinner.doc.mjs +5 -5
- package/src/Spinner/Spinner.test.tsx +32 -7
- package/src/Spinner/Spinner.tsx +46 -14
- package/src/Stack/Stack.doc.mjs +22 -0
- package/src/Stack/Stack.spec.md +155 -0
- package/src/StatusDot/StatusDot.doc.mjs +17 -0
- package/src/StatusDot/StatusDot.spec.md +138 -0
- package/src/Stepper/Step.doc.mjs +1 -1
- package/src/Stepper/Step.tsx +118 -5
- package/src/Stepper/Stepper.doc.mjs +51 -1
- package/src/Stepper/Stepper.spec.md +245 -0
- package/src/Stepper/Stepper.test.tsx +315 -0
- package/src/Stepper/Stepper.tsx +8 -0
- package/src/Switch/Switch.doc.mjs +40 -0
- package/src/Switch/Switch.spec.md +175 -0
- package/src/TabList/TabList.tsx +2 -3
- package/src/Table/Table.doc.mjs +102 -24
- package/src/Table/Table.perf.test.tsx +51 -1
- package/src/Table/Table.spec.md +325 -0
- package/src/Table/TableHeaderCell.test.tsx +291 -0
- package/src/Table/index.ts +19 -4
- package/src/Table/plugins/columnResize/useTableColumnResize.tsx +8 -12
- package/src/Table/plugins/filtering/useTableFilterState.test.tsx +143 -0
- package/src/Table/plugins/groupedRows/useTableGroupedRows.test.tsx +2 -1
- package/src/Table/plugins/rowStatus/index.ts +1 -4
- package/src/Table/plugins/rowStatus/useTableRowStatus.public.test.ts +92 -0
- package/src/Table/plugins/rowStatus/useTableRowStatus.spec.md +354 -0
- package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +431 -56
- package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +155 -84
- package/src/Table/plugins/sortable/useTableSortable.test.tsx +72 -0
- package/src/Table/plugins/sortable/useTableSortable.tsx +18 -1
- package/src/Table/useTableGroupedRows.doc.mjs +2 -2
- package/src/Table/useTableRowStatus.doc.mjs +5 -5
- package/src/Text/Text.doc.mjs +190 -43
- package/src/Text/Text.spec.md +206 -0
- package/src/Text/useTruncation.ts +8 -7
- package/src/TextArea/TextArea.doc.mjs +255 -45
- package/src/TextArea/TextArea.spec.md +205 -0
- package/src/TimeInput/NativeTimeInput.test.tsx +269 -0
- package/src/TimeInput/TimeInput.doc.mjs +47 -23
- package/src/TimeInput/TimeInput.tsx +109 -50
- package/src/TimeInput/index.ts +1 -0
- package/src/Timestamp/Timestamp.doc.mjs +2 -2
- package/src/Timestamp/Timestamp.test.tsx +75 -3
- package/src/Timestamp/Timestamp.tsx +7 -153
- package/src/Timestamp/formatRelativeTime.ts +96 -0
- package/src/Toast/ToastViewport.test.tsx +194 -0
- package/src/Toast/ToastViewport.tsx +136 -58
- package/src/ToggleButton/ToggleButton.doc.mjs +56 -0
- package/src/Tokenizer/Tokenizer.doc.mjs +2 -0
- package/src/Tokenizer/Tokenizer.spec.md +293 -0
- package/src/Tokenizer/Tokenizer.test.tsx +335 -1
- package/src/Tokenizer/Tokenizer.tsx +129 -46
- package/src/Tokenizer/useEndLaneReserve.ts +146 -0
- package/src/Toolbar/Toolbar.doc.mjs +19 -0
- package/src/Toolbar/Toolbar.spec.md +174 -0
- package/src/Tooltip/Tooltip.doc.mjs +17 -0
- package/src/Tooltip/Tooltip.spec.md +142 -0
- package/src/Tooltip/Tooltip.test.tsx +21 -1
- package/src/Tooltip/useTooltip.test.tsx +318 -0
- package/src/Tooltip/useTooltip.tsx +60 -39
- package/src/TreeList/TreeList.doc.mjs +58 -0
- package/src/TreeList/TreeList.spec.md +224 -0
- package/src/Typeahead/BaseTypeahead.tsx +96 -25
- package/src/Typeahead/Typeahead.doc.mjs +89 -1
- package/src/Typeahead/Typeahead.spec.md +257 -0
- package/src/Typeahead/Typeahead.test.tsx +502 -1
- package/src/Typeahead/Typeahead.tsx +187 -69
- package/src/Typeahead/TypeaheadItem.test.tsx +93 -2
- package/src/Typeahead/busyIndicatorLane.tsx +109 -0
- package/src/__tests__/stylexDeclarations.ts +51 -0
- package/src/hooks/useFocusReturnVisibility.ts +3 -5
- package/src/hooks/useOverflow.ts +11 -11
- package/src/hooks/useScrollLock.test.ts +103 -0
- package/src/hooks/useScrollOverflow.ts +7 -4
- package/src/i18n/useTranslator.test.tsx +197 -0
- package/src/theme/defineTheme.test.ts +227 -0
- package/src/theme/defineTheme.ts +30 -0
- package/src/theme/derivedVarRegistry.test.ts +6 -0
- package/src/theme/generateThemeRules.ts +4 -1
- package/src/theme/localTokens.ts +272 -0
- package/src/theme/themingTargets.test.ts +126 -28
- package/src/utils/interactionModality.test.ts +83 -0
- package/src/utils/interactionModality.ts +88 -36
- package/src/utils/sharedResizeObserver.test.ts +124 -5
- package/src/utils/sharedResizeObserver.ts +60 -14
|
@@ -27,12 +27,15 @@ import React, {
|
|
|
27
27
|
type RefObject,
|
|
28
28
|
} from 'react';
|
|
29
29
|
import * as stylex from '@stylexjs/stylex';
|
|
30
|
+
import {useBusyIndicatorLane} from './busyIndicatorLane';
|
|
30
31
|
import type {StyleXStyles} from '@stylexjs/stylex';
|
|
31
32
|
import {usePopover} from '../Popover/usePopover';
|
|
32
33
|
import {useAnnounce} from '../hooks/useAnnounce';
|
|
34
|
+
import {useIsomorphicLayoutEffect} from '../hooks/useIsomorphicLayoutEffect';
|
|
33
35
|
import {isImeKeyEvent} from '../utils/ime';
|
|
34
36
|
import {TypeaheadItem} from './TypeaheadItem';
|
|
35
37
|
import {Icon} from '../Icon';
|
|
38
|
+
import {Spinner} from '../Spinner';
|
|
36
39
|
import {
|
|
37
40
|
colorVars,
|
|
38
41
|
spacingVars,
|
|
@@ -310,10 +313,15 @@ const styles = stylex.create({
|
|
|
310
313
|
fontSize: typeScaleVars['--text-supporting-size'],
|
|
311
314
|
color: colorVars['--color-text-secondary'],
|
|
312
315
|
},
|
|
313
|
-
|
|
316
|
+
// The indicator a direct caller gets. In flow, where it has always been, so
|
|
317
|
+
// it reserves its own width and the input's text never runs under it.
|
|
318
|
+
// Typeahead and Tokenizer take the indicator over and paint it in their own
|
|
319
|
+
// inline-end lane instead; this is what renders for everyone else.
|
|
320
|
+
loadingStatus: {
|
|
314
321
|
display: 'inline-flex',
|
|
315
322
|
alignItems: 'center',
|
|
316
323
|
justifyContent: 'center',
|
|
324
|
+
flexShrink: 0,
|
|
317
325
|
padding: spacingVars['--spacing-1'],
|
|
318
326
|
},
|
|
319
327
|
});
|
|
@@ -424,6 +432,40 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
|
|
|
424
432
|
const [isLoading, setIsLoading] = useState(false);
|
|
425
433
|
const [hasSearched, setHasSearched] = useState(false);
|
|
426
434
|
|
|
435
|
+
// Report the busy state to a wrapper that has taken the indicator over.
|
|
436
|
+
//
|
|
437
|
+
// Through a ref, and at the call site rather than from an effect: an effect
|
|
438
|
+
// would run after this component had already committed, so the wrapper's
|
|
439
|
+
// own state change landed in a second commit — two renders of the whole
|
|
440
|
+
// field per transition, four across a search. Called here, the wrapper's
|
|
441
|
+
// setState batches with ours into the one commit that React was already
|
|
442
|
+
// doing. The ref keeps the identity of a caller's inline arrow from
|
|
443
|
+
// mattering, and the guard makes the report edge-triggered: the redundant
|
|
444
|
+
// `false` on every keystroke below the query threshold reports nothing.
|
|
445
|
+
//
|
|
446
|
+
// The ref is synced in a layout effect rather than during render, following
|
|
447
|
+
// `onMotionStartRef` in BottomSheetPanel — a render that React discards
|
|
448
|
+
// must not leave the ref pointing at the callback from the abandoned pass.
|
|
449
|
+
// This effect only writes a ref, so it commits nothing and no wrapper
|
|
450
|
+
// re-renders for it; every caller of `setLoading` runs from an event or an
|
|
451
|
+
// awaited continuation, long after the first commit.
|
|
452
|
+
// A wrapper that owns the inline-end lane subscribes through context; see
|
|
453
|
+
// busyIndicatorLane.tsx for why this is not a prop.
|
|
454
|
+
const busyLane = useBusyIndicatorLane();
|
|
455
|
+
const onLoadingChangeRef = useRef(busyLane?.onBusyChange);
|
|
456
|
+
useIsomorphicLayoutEffect(() => {
|
|
457
|
+
onLoadingChangeRef.current = busyLane?.onBusyChange;
|
|
458
|
+
}, [busyLane]);
|
|
459
|
+
const loadingRef = useRef(false);
|
|
460
|
+
const setLoading = useCallback((next: boolean) => {
|
|
461
|
+
if (loadingRef.current === next) {
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
loadingRef.current = next;
|
|
465
|
+
setIsLoading(next);
|
|
466
|
+
onLoadingChangeRef.current?.(next);
|
|
467
|
+
}, []);
|
|
468
|
+
|
|
427
469
|
// Track active pointer to defer popover.show() past click events.
|
|
428
470
|
// With popover="auto", showing the popover between pointerdown and
|
|
429
471
|
// pointerup/click causes the browser's light-dismiss to immediately
|
|
@@ -499,7 +541,7 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
|
|
|
499
541
|
// in-flight response for an older query fails the gen check below
|
|
500
542
|
// instead of overwriting the newer results.
|
|
501
543
|
const gen = ++searchGenRef.current;
|
|
502
|
-
|
|
544
|
+
setLoading(true);
|
|
503
545
|
setHasSearched(true);
|
|
504
546
|
try {
|
|
505
547
|
const searchResults = await searchSource.search(searchQuery);
|
|
@@ -534,7 +576,7 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
|
|
|
534
576
|
setHighlightedIndex(-1);
|
|
535
577
|
} finally {
|
|
536
578
|
if (searchGenRef.current === gen) {
|
|
537
|
-
|
|
579
|
+
setLoading(false);
|
|
538
580
|
}
|
|
539
581
|
}
|
|
540
582
|
},
|
|
@@ -545,37 +587,68 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
|
|
|
545
587
|
announce,
|
|
546
588
|
emptySearchResultsText,
|
|
547
589
|
__queryEntries,
|
|
590
|
+
setLoading,
|
|
548
591
|
t,
|
|
549
592
|
],
|
|
550
593
|
);
|
|
551
594
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
const gen = ++searchGenRef.current;
|
|
555
|
-
setIsLoading(true);
|
|
556
|
-
try {
|
|
557
|
-
const bootstrapResults = await searchSource.bootstrap();
|
|
595
|
+
const applyBootstrapResults = useCallback(
|
|
596
|
+
(bootstrapResults: T[], gen: number) => {
|
|
558
597
|
if (searchGenRef.current !== gen) {
|
|
559
598
|
return;
|
|
560
599
|
}
|
|
561
600
|
resultsGenRef.current = gen;
|
|
562
601
|
const shown = bootstrapResults.slice(0, maxMenuItems);
|
|
602
|
+
const nextHighlightedIndex = shown.length > 0 ? 0 : -1;
|
|
603
|
+
if (
|
|
604
|
+
results.length === 0 &&
|
|
605
|
+
shown.length === 0 &&
|
|
606
|
+
highlightedIndex === -1
|
|
607
|
+
) {
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
563
610
|
setResults(shown);
|
|
564
|
-
setHighlightedIndex(
|
|
611
|
+
setHighlightedIndex(nextHighlightedIndex);
|
|
565
612
|
if (bootstrapResults.length > 0) {
|
|
566
613
|
showLayer();
|
|
567
614
|
}
|
|
615
|
+
},
|
|
616
|
+
[highlightedIndex, maxMenuItems, results.length, showLayer],
|
|
617
|
+
);
|
|
618
|
+
|
|
619
|
+
// Perform bootstrap
|
|
620
|
+
const performBootstrap = useCallback(async () => {
|
|
621
|
+
const gen = ++searchGenRef.current;
|
|
622
|
+
let bootstrapResult: T[] | Promise<T[]>;
|
|
623
|
+
try {
|
|
624
|
+
bootstrapResult = searchSource.bootstrap();
|
|
568
625
|
} catch {
|
|
569
|
-
if (searchGenRef.current
|
|
570
|
-
|
|
626
|
+
if (searchGenRef.current === gen) {
|
|
627
|
+
setResults([]);
|
|
628
|
+
setLoading(false);
|
|
629
|
+
}
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
if (Array.isArray(bootstrapResult)) {
|
|
634
|
+
setLoading(false);
|
|
635
|
+
applyBootstrapResults(bootstrapResult, gen);
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
setLoading(true);
|
|
640
|
+
try {
|
|
641
|
+
applyBootstrapResults(await bootstrapResult, gen);
|
|
642
|
+
} catch {
|
|
643
|
+
if (searchGenRef.current === gen) {
|
|
644
|
+
setResults([]);
|
|
571
645
|
}
|
|
572
|
-
setResults([]);
|
|
573
646
|
} finally {
|
|
574
647
|
if (searchGenRef.current === gen) {
|
|
575
|
-
|
|
648
|
+
setLoading(false);
|
|
576
649
|
}
|
|
577
650
|
}
|
|
578
|
-
}, [searchSource,
|
|
651
|
+
}, [searchSource, applyBootstrapResults, setLoading]);
|
|
579
652
|
|
|
580
653
|
// Handle query change
|
|
581
654
|
const handleQueryChange = useCallback(
|
|
@@ -608,7 +681,7 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
|
|
|
608
681
|
// its own `finally` will decline to clear this — so clear it here or
|
|
609
682
|
// the field spins forever. Backspacing below the threshold on a remote
|
|
610
683
|
// source is the everyday way to hit that.
|
|
611
|
-
|
|
684
|
+
setLoading(false);
|
|
612
685
|
// Clear any lingering result-count / no-results announcement.
|
|
613
686
|
announce('');
|
|
614
687
|
if (derived.length > 0) {
|
|
@@ -645,6 +718,7 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
|
|
|
645
718
|
debounceMs,
|
|
646
719
|
searchSource,
|
|
647
720
|
announce,
|
|
721
|
+
setLoading,
|
|
648
722
|
],
|
|
649
723
|
);
|
|
650
724
|
|
|
@@ -673,11 +747,11 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
|
|
|
673
747
|
// Same reason as in handleQueryChange: the invalidated search will not
|
|
674
748
|
// clear this itself. Selecting a stale result while the next search is
|
|
675
749
|
// still in flight would otherwise leave the field spinning.
|
|
676
|
-
|
|
750
|
+
setLoading(false);
|
|
677
751
|
popover.hide();
|
|
678
752
|
inputRef.current?.focus();
|
|
679
753
|
},
|
|
680
|
-
[onChange, popover, searchSource],
|
|
754
|
+
[onChange, popover, searchSource, setLoading],
|
|
681
755
|
);
|
|
682
756
|
|
|
683
757
|
// Handle focus
|
|
@@ -889,6 +963,7 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
|
|
|
889
963
|
: undefined
|
|
890
964
|
}
|
|
891
965
|
aria-autocomplete="list"
|
|
966
|
+
aria-busy={isLoading || undefined}
|
|
892
967
|
aria-describedby={ariaDescribedBy}
|
|
893
968
|
aria-labelledby={ariaLabelledBy}
|
|
894
969
|
aria-disabled={isFocusableDisabled ? 'true' : undefined}
|
|
@@ -923,15 +998,11 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
|
|
|
923
998
|
inputXStyle,
|
|
924
999
|
)}
|
|
925
1000
|
/>
|
|
926
|
-
{isLoading && (
|
|
927
|
-
<span
|
|
928
|
-
|
|
929
|
-
aria-label={t('@astryx.typeahead.loading')}
|
|
930
|
-
{...stylex.props(styles.loadingSpinner)}>
|
|
931
|
-
<Icon icon="clock" size="sm" color="secondary" />
|
|
1001
|
+
{isLoading && busyLane == null && (
|
|
1002
|
+
<span {...stylex.props(styles.loadingStatus)}>
|
|
1003
|
+
<Spinner size="sm" aria-label={t('@astryx.typeahead.loading')} />
|
|
932
1004
|
</span>
|
|
933
1005
|
)}
|
|
934
|
-
|
|
935
1006
|
{popover.render(
|
|
936
1007
|
<div
|
|
937
1008
|
id={listboxId}
|
|
@@ -1,5 +1,91 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').ComponentAnatomyElement[]} */
|
|
4
|
+
const anatomy = [
|
|
5
|
+
{
|
|
6
|
+
name: 'Field',
|
|
7
|
+
required: false,
|
|
8
|
+
description:
|
|
9
|
+
'Standalone Field shell that provides the label and optional supporting content; omitted inside InputGroup.',
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
name: 'Input surface',
|
|
13
|
+
required: true,
|
|
14
|
+
description:
|
|
15
|
+
'Painted control surface containing the editable input or the selected token.',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: 'Icon-rendered start icon',
|
|
19
|
+
required: false,
|
|
20
|
+
description:
|
|
21
|
+
'Optional leading semantic icon or icon component rendered through Icon.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: 'Caller-rendered start content',
|
|
25
|
+
required: false,
|
|
26
|
+
description:
|
|
27
|
+
'Optional arbitrary React content rendered directly at the start of the input surface.',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'Selected token',
|
|
31
|
+
required: false,
|
|
32
|
+
description:
|
|
33
|
+
'Token that presents the selected item while the control is not in edit mode.',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'Spinner',
|
|
37
|
+
required: false,
|
|
38
|
+
description:
|
|
39
|
+
'Loading indicator shown at the end of the input surface while a search is in flight.',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'Clear button',
|
|
43
|
+
required: false,
|
|
44
|
+
description:
|
|
45
|
+
'Shared clear action that removes the selected item when hasClear is enabled.',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'Dropdown',
|
|
49
|
+
required: false,
|
|
50
|
+
description:
|
|
51
|
+
'Anchored listbox surface containing the current search results.',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'Empty state',
|
|
55
|
+
required: false,
|
|
56
|
+
description: 'Message shown after a completed search returns no results.',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: 'Result row',
|
|
60
|
+
required: false,
|
|
61
|
+
description:
|
|
62
|
+
'Stable option wrapper that owns highlight, selection, pointer, and keyboard behavior.',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'Default item content',
|
|
66
|
+
required: false,
|
|
67
|
+
description:
|
|
68
|
+
'Standard TypeaheadItem label and supporting content rendered inside a result row when renderItem and item.element are absent.',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: 'Caller-rendered item content',
|
|
72
|
+
required: false,
|
|
73
|
+
description:
|
|
74
|
+
'Caller-owned result content supplied through renderItem or item.element inside the stable result row.',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'Result group heading',
|
|
78
|
+
required: false,
|
|
79
|
+
description: 'Visible heading for a labeled group of result rows.',
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'Selected result state',
|
|
83
|
+
required: false,
|
|
84
|
+
description:
|
|
85
|
+
'Selected styling and trailing check presented on the current result row.',
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
|
|
3
89
|
/** @type {import('@astryxdesign/cli/authoring').ComponentDoc} */
|
|
4
90
|
|
|
5
91
|
export const docs = {
|
|
@@ -86,7 +172,8 @@ export const docs = {
|
|
|
86
172
|
{
|
|
87
173
|
name: 'minQueryLength',
|
|
88
174
|
type: 'number',
|
|
89
|
-
description:
|
|
175
|
+
description:
|
|
176
|
+
'Minimum query length before the search source is queried. Below it no search runs and the menu stays closed.',
|
|
90
177
|
default: '1',
|
|
91
178
|
},
|
|
92
179
|
{
|
|
@@ -199,6 +286,7 @@ export const docs = {
|
|
|
199
286
|
],
|
|
200
287
|
},
|
|
201
288
|
usage: {
|
|
289
|
+
anatomy,
|
|
202
290
|
description:
|
|
203
291
|
'A searchable input for selecting a single item from a large or dynamic dataset. Results appear as the user types, with support for async data sources, debounced search, and custom item rendering. Use it when the option list is too large for a Selector dropdown.',
|
|
204
292
|
bestPractices: [
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema_version: 3
|
|
3
|
+
template_version: 3
|
|
4
|
+
kind: component
|
|
5
|
+
id: component:Typeahead
|
|
6
|
+
authority: draft
|
|
7
|
+
archive_reason: null
|
|
8
|
+
superseded_by: null
|
|
9
|
+
approved_by: null
|
|
10
|
+
approved_at: null
|
|
11
|
+
owners: [cixzhang]
|
|
12
|
+
review_triggers: [theming]
|
|
13
|
+
verified_by:
|
|
14
|
+
[
|
|
15
|
+
packages/core/src/Typeahead/Typeahead.test.tsx,
|
|
16
|
+
packages/core/src/Typeahead/TypeaheadItem.test.tsx,
|
|
17
|
+
packages/core/src/Field/Field.test.tsx,
|
|
18
|
+
packages/core/src/Token/Token.test.tsx,
|
|
19
|
+
packages/core/src/Icon/Icon.test.tsx,
|
|
20
|
+
scripts/check-knowledge.mjs,
|
|
21
|
+
]
|
|
22
|
+
modules: []
|
|
23
|
+
families: [family:input-fields, family:overlay-dismissal]
|
|
24
|
+
design_specs: []
|
|
25
|
+
architecture:
|
|
26
|
+
[architecture:component-theming-surface, architecture:layer-runtime]
|
|
27
|
+
contributing: []
|
|
28
|
+
system_specs: []
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
# Typeahead component contract
|
|
32
|
+
|
|
33
|
+
## Intent
|
|
34
|
+
|
|
35
|
+
Typeahead renders a single-selection search control with an optional standalone
|
|
36
|
+
Field shell and delegates its combobox engine and popup results to BaseTypeahead. This draft records current
|
|
37
|
+
consumer anatomy, target reachability, and the shipped difference between the
|
|
38
|
+
stable result row and its default TypeaheadItem content without changing runtime
|
|
39
|
+
behavior, styling, targets, or public API.
|
|
40
|
+
|
|
41
|
+
## Compatibility and migration
|
|
42
|
+
|
|
43
|
+
- Released default preserved: `yes`
|
|
44
|
+
- Compatibility class: additive documentation only; runtime, DOM, styling,
|
|
45
|
+
targets, and public API remain unchanged
|
|
46
|
+
- Controlled/uncontrolled behavior: unchanged
|
|
47
|
+
- Migration decision: none
|
|
48
|
+
|
|
49
|
+
Consumer migration instructions belong in consumer docs and release notes.
|
|
50
|
+
|
|
51
|
+
## Ownership boundary
|
|
52
|
+
|
|
53
|
+
**Owns**
|
|
54
|
+
|
|
55
|
+
- The painted Input surface and its current `typeahead` target.
|
|
56
|
+
- The Dropdown and Empty state rendered by BaseTypeahead and their current
|
|
57
|
+
`typeahead-dropdown` and `typeahead-empty-state` targets.
|
|
58
|
+
- Default item content rendered by TypeaheadItem and its current
|
|
59
|
+
`typeahead-item` target.
|
|
60
|
+
- The stable outer Result row, Result group heading, and Selected result state,
|
|
61
|
+
which currently have no Typeahead target.
|
|
62
|
+
|
|
63
|
+
**Does not own / non-goals**
|
|
64
|
+
|
|
65
|
+
- Standalone field-shell presentation — rendered by Field and themed through
|
|
66
|
+
Field's current `field` target; omitted when Typeahead is inside InputGroup.
|
|
67
|
+
- The Selected token — rendered by Token and themed through Token's current
|
|
68
|
+
`token` target.
|
|
69
|
+
- The Clear button — rendered by Field's InputClearButton and themed through
|
|
70
|
+
Field's current `input-clear-button` target.
|
|
71
|
+
- Loading-indicator presentation — owned by `component:Spinner`. Typeahead
|
|
72
|
+
decides only where the indicator sits.
|
|
73
|
+
- General Icon presentation for an Icon-rendered start icon.
|
|
74
|
+
- Arbitrary ReactNode start or item content supplied by the caller.
|
|
75
|
+
- A target/state for the outer Result row, Result group heading, or selected-row
|
|
76
|
+
concept; none exists on current `main`.
|
|
77
|
+
- Shared layer lifecycle, positioning, and dismissal behavior.
|
|
78
|
+
|
|
79
|
+
## Public concepts
|
|
80
|
+
|
|
81
|
+
No new public concept is introduced. Consumer props and usage remain documented
|
|
82
|
+
in `Typeahead.doc.mjs`; BaseTypeahead and TypeaheadItem retain their existing
|
|
83
|
+
public component documentation.
|
|
84
|
+
|
|
85
|
+
## Behavioral and layout contract
|
|
86
|
+
|
|
87
|
+
| ID | Candidate invariant | Basis | Draft review state |
|
|
88
|
+
| --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | -------------------------------------------------- |
|
|
89
|
+
| FR1 | The current standalone presentation contains a Field and painted Input surface; the supported InputGroup path renders the same Input surface without a Field. The surface contains an editable input when unselected or editing, and a Selected token plus collapsed input when selected and not editing. | Current source, docs, and tests | Verified current behavior; no normalization |
|
|
90
|
+
| FR2 | Input surface, Dropdown, Empty state, and Default item content carry the four current Typeahead targets documented in `Typeahead.doc.mjs`. | Current source, docs, and tests | Verified current behavior; no target change |
|
|
91
|
+
| FR3 | Every result, whether default or caller-rendered, sits inside the same stable outer Result row that owns option semantics, highlight, selected styling, pointer interaction, and keyboard indexing. | Current source and tests | Verified current behavior; no new behavior decided |
|
|
92
|
+
| FR4 | `typeahead-item` reaches only the standard TypeaheadItem label/supporting-content branch inside a Result row. A custom `renderItem` branch or caller-provided `item.element` does not receive that target, and no branch gives the outer Result row a Typeahead target. | Current source and owner tests | Verified current asymmetry; no normalization |
|
|
93
|
+
| FR5 | Result group heading and Selected result state are stable visible concepts but no current Typeahead target reaches them. The selected check uses a generic Icon, which does not expose a Typeahead-specific selected-row selector. | Current source | Verified factual reachability gaps |
|
|
94
|
+
| FR6 | When present, Field, Selected token, Clear button, Spinner, and Icon-rendered start icon retain their Field, Token, Spinner, and Icon theming owners; arbitrary ReactNode start and item content stays caller-owned. | Current composition and owners | Verified current behavior; no target change |
|
|
95
|
+
|
|
96
|
+
### Allowed variation
|
|
97
|
+
|
|
98
|
+
- Standalone Typeahead renders Field; the supported InputGroup path omits it.
|
|
99
|
+
- The Input surface shows an editable input while empty or editing and a Token
|
|
100
|
+
while a selected value is not being edited. This shipped asymmetry is recorded,
|
|
101
|
+
not normalized into a new shared part or target.
|
|
102
|
+
- A Result row may contain standard TypeaheadItem content, custom `renderItem`
|
|
103
|
+
content, or a caller-provided `item.element`. Only the standard TypeaheadItem
|
|
104
|
+
label/supporting-content branch carries `typeahead-item`.
|
|
105
|
+
- Results may be ungrouped or grouped. Group wrappers and headings do not gain a
|
|
106
|
+
target from that variation.
|
|
107
|
+
- Semantic names and icon component types render through Icon. Arbitrary ReactNode
|
|
108
|
+
start content renders directly and stays caller-owned.
|
|
109
|
+
|
|
110
|
+
### Representative states
|
|
111
|
+
|
|
112
|
+
| State | Required invariant | Allowed variation |
|
|
113
|
+
| ----------------------------- | -------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
|
114
|
+
| Standalone or InputGroup | Field is present only standalone; Input surface anatomy and targets remain the same in both paths. | InputGroup owns grouped layout and labeling support. |
|
|
115
|
+
| Empty or editing | Editable input remains inside the `typeahead` Input surface. | Query, loading state, and popup visibility may vary. |
|
|
116
|
+
| Selected and not editing | Token owns the selected-value presentation while the input is collapsed. | Clear button may be present according to `hasClear` and disabled state. |
|
|
117
|
+
| Results with default renderer | Outer Result row remains untargeted; inner TypeaheadItem carries `typeahead-item`. | Item label, icon/avatar, and description may vary. |
|
|
118
|
+
| Results with custom renderer | Outer Result row remains untargeted; custom content receives no `typeahead-item`. | Caller owns the inner result content. |
|
|
119
|
+
| Grouped and selected results | Group heading and selected-row concept remain untargeted by Typeahead. | A generic Icon paints the selected check. |
|
|
120
|
+
| Empty completed search | Empty state carries `typeahead-empty-state` inside the Dropdown. | Consumer-supplied empty text may vary. |
|
|
121
|
+
|
|
122
|
+
### Transformation and precedence order
|
|
123
|
+
|
|
124
|
+
- No new query, search-result, selection, edit-mode, or styling precedence rule is
|
|
125
|
+
introduced.
|
|
126
|
+
|
|
127
|
+
### Performance and resources
|
|
128
|
+
|
|
129
|
+
- No new performance or resource rule is introduced.
|
|
130
|
+
|
|
131
|
+
## Accessibility contract
|
|
132
|
+
|
|
133
|
+
This draft does not change or extend Typeahead's existing field, combobox,
|
|
134
|
+
listbox, option, live-region, focus, or dismissal behavior.
|
|
135
|
+
|
|
136
|
+
## Design relationships
|
|
137
|
+
|
|
138
|
+
| Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
|
|
139
|
+
| ----------------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------ | ----------------- | ------------------ |
|
|
140
|
+
| Field | Provides the label and optional supporting content outside InputGroup; omitted inside InputGroup. | Field component | Supporting | FR1, FR6 |
|
|
141
|
+
| Input surface | Paints the control around the editable input or selected token. | Current source and public docs | Prominent | FR1, FR2 |
|
|
142
|
+
| Icon-rendered start icon | Presents a semantic or component icon through Icon. | Icon component | Supporting | FR6 |
|
|
143
|
+
| Caller-rendered start content | Presents arbitrary caller content directly in the input surface's start slot. | Caller-supplied content | Context-dependent | FR6 |
|
|
144
|
+
| Selected token | Presents the selected value outside edit mode through Token. | Token component | Prominent | FR1, FR6 |
|
|
145
|
+
| Spinner | Indicates a search in flight, at the end of the input surface. | Spinner component | Supporting | FR6 |
|
|
146
|
+
| Clear button | Removes the selected value through the shared field clear action. | Field component | Supporting | FR6 |
|
|
147
|
+
| Dropdown | Paints the anchored listbox surface containing results or Empty state. | Current source and public docs | Prominent | FR2 |
|
|
148
|
+
| Empty state | Presents the no-results message after a completed empty search. | Current source and public docs | Prominent | FR2 |
|
|
149
|
+
| Result row | Owns option semantics, interaction, highlight, selection styling, and keyboard index for every result. | Current source and tests | Prominent | FR3, FR4 |
|
|
150
|
+
| Default item content | Presents the standard TypeaheadItem label and optional supporting content inside a Result row. | TypeaheadItem component | Prominent | FR2, FR4 |
|
|
151
|
+
| Caller-rendered item content | Presents custom `renderItem` or `item.element` content inside the stable Result row. | Caller-supplied content | Context-dependent | FR3, FR4, FR6 |
|
|
152
|
+
| Result group heading | Labels a visible group of Result rows. | Current source | Supporting | FR5 |
|
|
153
|
+
| Selected result state | Presents selected row weight and a trailing check without a Typeahead-specific target or target state. | Current source | Supporting | FR5 |
|
|
154
|
+
|
|
155
|
+
The current target placement is asymmetric: `typeahead-item` is on default inner
|
|
156
|
+
TypeaheadItem content, while the stable outer Result row owns interaction and state
|
|
157
|
+
for both default and custom content. Group headings and the selected-result concept
|
|
158
|
+
are also untargeted. This record preserves those facts and does not move, add, or
|
|
159
|
+
normalize targets.
|
|
160
|
+
|
|
161
|
+
### Theming anatomy
|
|
162
|
+
|
|
163
|
+
<!-- anatomy-theming:v1 -->
|
|
164
|
+
|
|
165
|
+
```json
|
|
166
|
+
{
|
|
167
|
+
"Field": {"delegatesTo": {"owner": "component:Field", "target": "field"}},
|
|
168
|
+
"Input surface": {"target": "typeahead"},
|
|
169
|
+
"Icon-rendered start icon": {
|
|
170
|
+
"delegatesTo": {"owner": "component:Icon", "target": "icon"}
|
|
171
|
+
},
|
|
172
|
+
"Caller-rendered start content": {
|
|
173
|
+
"none": {
|
|
174
|
+
"reason": "intentional: Arbitrary ReactNode content is caller-owned and receives no Typeahead target."
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
"Selected token": {
|
|
178
|
+
"delegatesTo": {"owner": "component:Token", "target": "token"}
|
|
179
|
+
},
|
|
180
|
+
"Spinner": {
|
|
181
|
+
"delegatesTo": {"owner": "component:Spinner", "target": "spinner"}
|
|
182
|
+
},
|
|
183
|
+
"Clear button": {
|
|
184
|
+
"delegatesTo": {
|
|
185
|
+
"owner": "component:Field",
|
|
186
|
+
"target": "input-clear-button"
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
"Dropdown": {"target": "typeahead-dropdown"},
|
|
190
|
+
"Empty state": {"target": "typeahead-empty-state"},
|
|
191
|
+
"Result row": {
|
|
192
|
+
"none": {
|
|
193
|
+
"reason": "reachability-gap: The stable outer option row owns interaction and state but has no current Typeahead target."
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
"Default item content": {"target": "typeahead-item"},
|
|
197
|
+
"Caller-rendered item content": {
|
|
198
|
+
"none": {
|
|
199
|
+
"reason": "intentional: Custom renderItem and item.element content is caller-owned and does not receive the standard TypeaheadItem target."
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
"Result group heading": {
|
|
203
|
+
"none": {
|
|
204
|
+
"reason": "reachability-gap: No current public target reaches the visible result-group heading."
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
"Selected result state": {
|
|
208
|
+
"none": {
|
|
209
|
+
"reason": "reachability-gap: No current Typeahead target or target state reaches the outer selected row and its selection concept."
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Family and system relationships
|
|
216
|
+
|
|
217
|
+
- `architecture:component-theming-surface` owns anatomy qualification, local
|
|
218
|
+
target mapping, delegation, and factual `none` dispositions.
|
|
219
|
+
- `architecture:layer-runtime` owns the current Popover host, positioning, native
|
|
220
|
+
light-dismiss, and visibility reconciliation used by the Dropdown.
|
|
221
|
+
- `family:overlay-dismissal` owns shared Escape and platform-close ordering;
|
|
222
|
+
Typeahead participates through its composed Popover owner.
|
|
223
|
+
- Field, Token, and Icon retain ownership of their delegated targets and
|
|
224
|
+
presentation.
|
|
225
|
+
|
|
226
|
+
## Verification map
|
|
227
|
+
|
|
228
|
+
| Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
|
|
229
|
+
| ------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- | ------------------------- |
|
|
230
|
+
| FR1, FR6 | `Typeahead.test.tsx`, owner tests, and Typeahead composition source | Standalone/InputGroup, empty, editing, selected, clear, start icon | A documented composed part disappears, gains the wrong owner, or misreports Field presence. | `audit:Typeahead/anatomy` |
|
|
231
|
+
| FR2 | Source inspection, target inventories, empty-state test, and `TypeaheadItem.test.tsx` | Input, open Dropdown, empty search, default item | A current target is missed, invented, or documented on an element that does not carry it. | `audit:Typeahead/theming` |
|
|
232
|
+
| FR3, FR4, FR5 | BaseTypeahead source and result/group/selection tests | Default/custom result, grouped result, selected row | The record hides the outer/inner asymmetry or claims reachability that current code lacks. | `audit:Typeahead/theming` |
|
|
233
|
+
| Theming anatomy map | `scripts/check-knowledge.mjs` | Canonical anatomy and current target inventory | Missing, extra, prefixed, stale, or multiply assigned mappings fail repository validation. | `audit:Typeahead/theming` |
|
|
234
|
+
|
|
235
|
+
Existing tests directly assert Empty state and TypeaheadItem target presence,
|
|
236
|
+
selected-value Token behavior, result semantics, selection state, and standalone
|
|
237
|
+
Field content. Source inspection confirms InputGroup's Field omission and, with
|
|
238
|
+
public target inventories, provides the current Input surface and Dropdown target
|
|
239
|
+
evidence. No test is represented here as proof of a target it does not directly
|
|
240
|
+
assert.
|
|
241
|
+
|
|
242
|
+
## Decision log
|
|
243
|
+
|
|
244
|
+
None. This draft records current facts and introduces no component-local design,
|
|
245
|
+
API, theming, or layer-system decision.
|
|
246
|
+
|
|
247
|
+
## Open questions
|
|
248
|
+
|
|
249
|
+
- **OQ1 — Should the stable Result row, Result group heading, or Selected result
|
|
250
|
+
state gain a public Typeahead target?** (`human-api`) Their current lack of
|
|
251
|
+
reachability is an audit gap, not settled intent. This draft does not normalize
|
|
252
|
+
the existing outer-row/default-content asymmetry.
|
|
253
|
+
|
|
254
|
+
## Content boundary
|
|
255
|
+
|
|
256
|
+
This file does not duplicate consumer prop tables, examples, search algorithms,
|
|
257
|
+
implementation steps, or shared layer and theming rules. It links to their owners.
|