@astryxdesign/core 0.4.4 → 0.4.5-canary.013e728
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 +40 -0
- package/dist/AvatarGroup/AvatarGroupOverflow.js +1 -1
- package/dist/BottomSheet/BottomSheet.d.ts +11 -2
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheet.js +4 -0
- package/dist/BottomSheet/BottomSheetPanel.d.ts +5 -2
- package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetPanel.js +65 -15
- package/dist/BottomSheet/index.d.ts +1 -1
- package/dist/BottomSheet/index.d.ts.map +1 -1
- package/dist/BottomSheet/snapOffsets.d.ts +57 -20
- package/dist/BottomSheet/snapOffsets.d.ts.map +1 -1
- package/dist/BottomSheet/snapOffsets.js +106 -30
- package/dist/BottomSheet/useSheetGestures.d.ts +13 -8
- package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
- package/dist/BottomSheet/useSheetGestures.js +160 -64
- package/dist/Breadcrumbs/BreadcrumbItem.js +6 -6
- package/dist/Button/Button.js +5 -5
- package/dist/Calendar/Calendar.d.ts +17 -0
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +55 -15
- package/dist/Calendar/getStandaloneShortWeekdayNames.d.ts +14 -0
- package/dist/Calendar/getStandaloneShortWeekdayNames.d.ts.map +1 -0
- package/dist/Calendar/getStandaloneShortWeekdayNames.js +26 -0
- package/dist/Calendar/hooks/useCalendarConstraints.d.ts +22 -1
- package/dist/Calendar/hooks/useCalendarConstraints.d.ts.map +1 -1
- package/dist/Calendar/hooks/useCalendarConstraints.js +26 -4
- package/dist/Calendar/hooks/useCalendarDays.d.ts.map +1 -1
- package/dist/Calendar/hooks/useCalendarDays.js +14 -10
- package/dist/Calendar/hooks/useCalendarNavigation.d.ts.map +1 -1
- package/dist/Calendar/hooks/useCalendarNavigation.js +8 -4
- package/dist/Calendar/standaloneShortWeekdayNames.generated.d.ts +47 -0
- package/dist/Calendar/standaloneShortWeekdayNames.generated.d.ts.map +1 -0
- package/dist/Calendar/standaloneShortWeekdayNames.generated.js +40 -0
- package/dist/Calendar/styles.js +3 -3
- package/dist/Center/Center.d.ts +23 -1
- package/dist/Center/Center.d.ts.map +1 -1
- package/dist/Center/Center.js +12 -6
- package/dist/Chat/ChatComposer.d.ts.map +1 -1
- package/dist/Chat/ChatComposer.js +2 -2
- package/dist/Chat/ChatToolCalls.js +1 -1
- package/dist/Chat/useChatDictation.d.ts +1 -1
- package/dist/Chat/useChatDictation.d.ts.map +1 -1
- package/dist/Chat/useSpeechRecognition.d.ts +1 -1
- package/dist/Chat/useSpeechRecognition.d.ts.map +1 -1
- package/dist/Chat/useSpeechRecognition.js +4 -3
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +9 -0
- package/dist/Citation/Citation.js +3 -3
- package/dist/ClickableCard/ClickableCard.js +2 -2
- package/dist/CommandPalette/CommandPaletteItem.js +1 -1
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +8 -3
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +17 -9
- package/dist/DateRangeInput/DateRangeInput.d.ts +23 -1
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +47 -9
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +18 -10
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +3 -3
- package/dist/Field/FieldLabel.d.ts +2 -1
- package/dist/Field/FieldLabel.d.ts.map +1 -1
- package/dist/Field/FieldLabel.js +21 -3
- package/dist/Field/InputClearButton.d.ts +2 -1
- package/dist/Field/InputClearButton.d.ts.map +1 -1
- package/dist/Field/InputClearButton.js +12 -1
- package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
- package/dist/Field/inputStyles.stylex.js +4 -4
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +2 -2
- package/dist/FormLayout/FormLayout.d.ts +24 -2
- package/dist/FormLayout/FormLayout.d.ts.map +1 -1
- package/dist/FormLayout/FormLayout.js +7 -2
- package/dist/FormLayout/FormLayoutContext.d.ts +15 -2
- package/dist/FormLayout/FormLayoutContext.d.ts.map +1 -1
- package/dist/FormLayout/FormLayoutContext.js +19 -4
- package/dist/FormLayout/index.d.ts +1 -1
- package/dist/FormLayout/index.d.ts.map +1 -1
- package/dist/HoverCard/HoverCard.d.ts +13 -3
- package/dist/HoverCard/HoverCard.d.ts.map +1 -1
- package/dist/HoverCard/HoverCard.js +2 -0
- package/dist/HoverCard/index.d.ts +1 -1
- package/dist/HoverCard/index.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.d.ts +17 -2
- package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +78 -9
- package/dist/Item/Item.d.ts +9 -1
- package/dist/Item/Item.d.ts.map +1 -1
- package/dist/Item/Item.js +46 -8
- package/dist/Layer/index.d.ts +4 -2
- package/dist/Layer/index.d.ts.map +1 -1
- package/dist/Layer/index.js +2 -0
- package/dist/Layer/useLayer.d.ts.map +1 -1
- package/dist/Layer/useLayer.js +3 -2
- package/dist/Layer/useTouchTrigger.d.ts +113 -0
- package/dist/Layer/useTouchTrigger.d.ts.map +1 -0
- package/dist/Layer/useTouchTrigger.js +241 -0
- package/dist/Layout/container.stylex.d.ts +4 -2
- package/dist/Layout/container.stylex.d.ts.map +1 -1
- package/dist/Layout/container.stylex.js +19 -11
- package/dist/Layout/index.d.ts +1 -0
- package/dist/Layout/index.d.ts.map +1 -1
- package/dist/Layout/index.js +4 -0
- package/dist/Layout/padding.stylex.d.ts +315 -12
- package/dist/Layout/padding.stylex.d.ts.map +1 -1
- package/dist/Layout/padding.stylex.js +389 -12
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +2 -1
- package/dist/Link/Link.js +4 -4
- package/dist/Markdown/Markdown.d.ts +8 -0
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.js +31 -3
- package/dist/Markdown/parser.d.ts +14 -2
- package/dist/Markdown/parser.d.ts.map +1 -1
- package/dist/Markdown/parser.js +50 -2
- package/dist/MobileNav/MobileNav.d.ts +1 -0
- package/dist/MobileNav/MobileNav.d.ts.map +1 -1
- package/dist/MobileNav/MobileNav.js +24 -6
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +8 -3
- package/dist/NavItem/navItemStyles.stylex.d.ts +2 -2
- package/dist/NavItem/navItemStyles.stylex.js +2 -2
- package/dist/NavMenu/NavHeadingMenuItem.js +1 -1
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +38 -5
- package/dist/Outline/Outline.js +2 -2
- package/dist/Outline/parseOutlineFromMarkdown.d.ts +2 -0
- package/dist/Outline/parseOutlineFromMarkdown.d.ts.map +1 -1
- package/dist/Outline/parseOutlineFromMarkdown.js +5 -31
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +11 -12
- package/dist/PowerSearch/PowerSearchToken.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchToken.js +3 -1
- package/dist/PowerSearch/formatFilterValue.d.ts +11 -2
- package/dist/PowerSearch/formatFilterValue.d.ts.map +1 -1
- package/dist/PowerSearch/formatFilterValue.js +23 -8
- package/dist/RadioList/RadioList.d.ts.map +1 -1
- package/dist/RadioList/RadioList.js +12 -1
- package/dist/RadioList/RadioListItem.d.ts +2 -2
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +68 -38
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +3 -3
- package/dist/Section/Section.d.ts +36 -1
- package/dist/Section/Section.d.ts.map +1 -1
- package/dist/Section/Section.js +12 -2
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +6 -1
- package/dist/SelectableCard/SelectableCard.js +1 -1
- package/dist/Selector/Selector.d.ts +26 -1
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +95 -17
- package/dist/Selector/SelectorOption.d.ts +14 -1
- package/dist/Selector/SelectorOption.d.ts.map +1 -1
- package/dist/Selector/SelectorOption.js +6 -0
- package/dist/Selector/SelectorRowLayoutContext.d.ts +8 -0
- package/dist/Selector/SelectorRowLayoutContext.d.ts.map +1 -0
- package/dist/Selector/SelectorRowLayoutContext.js +27 -0
- package/dist/Selector/types.d.ts +1 -0
- package/dist/Selector/types.d.ts.map +1 -1
- package/dist/SideNav/SideNavHeading.js +1 -1
- package/dist/SideNav/SideNavItem.js +1 -1
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +3 -3
- package/dist/Stack/Stack.d.ts +23 -1
- package/dist/Stack/Stack.d.ts.map +1 -1
- package/dist/Stack/Stack.js +12 -6
- package/dist/StatusDot/StatusDot.d.ts +43 -3
- package/dist/StatusDot/StatusDot.d.ts.map +1 -1
- package/dist/StatusDot/StatusDot.js +42 -5
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +9 -0
- package/dist/TabList/TabMenu.js +1 -1
- package/dist/Table/TableRow.js +4 -4
- package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
- package/dist/Table/plugins/columnResize/useTableColumnResize.js +1 -1
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +1 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +7 -2
- package/dist/Table/plugins/sortable/useTableSortableState.d.ts +5 -3
- package/dist/Table/plugins/sortable/useTableSortableState.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortableState.js +12 -9
- package/dist/Table/plugins/tree/useTableTreeData.js +2 -2
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +6 -1
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +6 -1
- package/dist/Thumbnail/Thumbnail.js +1 -1
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +6 -1
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +6 -4
- package/dist/Timestamp/formatInstant.d.ts +3 -2
- package/dist/Timestamp/formatInstant.d.ts.map +1 -1
- package/dist/Timestamp/formatInstant.js +8 -8
- package/dist/Timestamp/tooltipEntries.d.ts +2 -1
- package/dist/Timestamp/tooltipEntries.d.ts.map +1 -1
- package/dist/Timestamp/tooltipEntries.js +5 -3
- package/dist/Token/Token.js +1 -1
- package/dist/Tooltip/Tooltip.d.ts +16 -3
- package/dist/Tooltip/Tooltip.d.ts.map +1 -1
- package/dist/Tooltip/Tooltip.js +2 -0
- package/dist/Tooltip/index.d.ts +1 -1
- package/dist/Tooltip/index.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.d.ts +21 -2
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +71 -13
- package/dist/TopNav/TopNavHeading.js +2 -2
- package/dist/TopNav/TopNavItem.js +2 -2
- package/dist/TopNav/TopNavMegaMenu.js +1 -1
- package/dist/TopNav/TopNavMegaMenuItem.js +1 -1
- package/dist/TopNav/TopNavMenu.js +2 -2
- package/dist/TreeList/TreeListItem.js +1 -1
- package/dist/astryx.css +85 -60
- package/dist/hooks/containerReveal.stylex.d.ts.map +1 -1
- package/dist/hooks/containerReveal.stylex.js +18 -18
- package/dist/hooks/scrollbarGutter.d.ts +28 -0
- package/dist/hooks/scrollbarGutter.d.ts.map +1 -0
- package/dist/hooks/scrollbarGutter.js +103 -0
- package/dist/hooks/useResolvedRequired.d.ts +19 -0
- package/dist/hooks/useResolvedRequired.d.ts.map +1 -0
- package/dist/hooks/useResolvedRequired.js +40 -0
- package/dist/hooks/useScrollLock.d.ts +4 -0
- package/dist/hooks/useScrollLock.d.ts.map +1 -1
- package/dist/hooks/useScrollLock.js +13 -1
- package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
- package/dist/i18n/InternationalizationContext.js +2 -0
- package/dist/i18n/index.d.ts +6 -0
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js +6 -0
- package/dist/i18n/useCollator.d.ts +12 -0
- package/dist/i18n/useCollator.d.ts.map +1 -0
- package/dist/i18n/useCollator.js +40 -0
- package/dist/i18n/useLocale.d.ts +3 -0
- package/dist/i18n/useLocale.d.ts.map +1 -0
- package/dist/i18n/useLocale.js +31 -0
- package/dist/theme/defineTheme.d.ts +5 -0
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/defineTheme.js +5 -0
- package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
- package/dist/theme/derivedVarRegistry.js +7 -0
- package/dist/theme/generateThemeRules.d.ts.map +1 -1
- package/dist/theme/generateThemeRules.js +66 -5
- package/dist/utils/dateParser.d.ts +4 -3
- package/dist/utils/dateParser.d.ts.map +1 -1
- package/dist/utils/dateParser.js +8 -8
- package/dist/utils/plainDate.d.ts +9 -2
- package/dist/utils/plainDate.d.ts.map +1 -1
- package/dist/utils/plainDate.js +16 -4
- package/locales/af-ZA.json +958 -0
- package/locales/ar-SA.json +998 -0
- package/locales/ca-ES.json +974 -0
- package/locales/cs-CZ.json +990 -0
- package/locales/da-DK.json +966 -0
- package/locales/de-DE.json +966 -0
- package/locales/el-GR.json +990 -0
- package/locales/en.json +8 -0
- package/locales/es-ES.json +978 -0
- package/locales/fi-FI.json +990 -0
- package/locales/fr-FR.json +928 -0
- package/locales/he-IL.json +990 -0
- package/locales/hu-HU.json +990 -0
- package/locales/it-IT.json +978 -0
- package/locales/ja-JP.json +998 -0
- package/locales/ko-KR.json +990 -0
- package/locales/nl-NL.json +954 -0
- package/locales/no-NO.json +978 -0
- package/locales/pl-PL.json +982 -0
- package/locales/pseudo.json +6 -0
- package/locales/pt-BR.json +982 -0
- package/locales/pt-PT.json +982 -0
- package/locales/ro-RO.json +982 -0
- package/locales/ru-RU.json +990 -0
- package/locales/sr-SP.json +990 -0
- package/locales/sv-SE.json +986 -0
- package/locales/tr-TR.json +990 -0
- package/locales/uk-UA.json +990 -0
- package/locales/vi-VN.json +986 -0
- package/locales/zh-CN.json +998 -0
- package/locales/zh-TW.json +998 -0
- package/package.json +3 -3
- package/src/AvatarGroup/AvatarGroupOverflow.tsx +1 -1
- package/src/Banner/Banner.doc.mjs +2 -0
- package/src/BottomSheet/BottomSheet.doc.mjs +36 -3
- package/src/BottomSheet/BottomSheet.test.tsx +177 -2
- package/src/BottomSheet/BottomSheet.tsx +16 -2
- package/src/BottomSheet/BottomSheetPanel.test.tsx +41 -0
- package/src/BottomSheet/BottomSheetPanel.tsx +95 -15
- package/src/BottomSheet/index.ts +5 -1
- package/src/BottomSheet/snapOffsets.test.ts +114 -47
- package/src/BottomSheet/snapOffsets.ts +119 -33
- package/src/BottomSheet/useSheetGestures.test.ts +113 -15
- package/src/BottomSheet/useSheetGestures.ts +196 -100
- package/src/Breadcrumbs/BreadcrumbItem.tsx +1 -1
- package/src/Button/Button.tsx +7 -7
- package/src/Calendar/Calendar.doc.mjs +16 -0
- package/src/Calendar/Calendar.test.tsx +137 -0
- package/src/Calendar/Calendar.tsx +80 -11
- package/src/Calendar/getStandaloneShortWeekdayNames.test.ts +54 -0
- package/src/Calendar/getStandaloneShortWeekdayNames.ts +38 -0
- package/src/Calendar/hooks/useCalendarConstraints.ts +54 -3
- package/src/Calendar/hooks/useCalendarDays.ts +13 -10
- package/src/Calendar/hooks/useCalendarNavigation.ts +6 -4
- package/src/Calendar/standaloneShortWeekdayNames.generated.ts +50 -0
- package/src/Calendar/styles.ts +3 -3
- package/src/Center/Center.doc.mjs +48 -0
- package/src/Center/Center.test.tsx +158 -0
- package/src/Center/Center.tsx +50 -9
- package/src/Chat/ChatComposer.tsx +7 -4
- package/src/Chat/ChatToolCalls.tsx +1 -1
- package/src/Chat/{useChatDictation.test.ts → useChatDictation.test.tsx} +29 -14
- package/src/Chat/useChatDictation.ts +1 -1
- package/src/Chat/useSpeechRecognition.ts +5 -3
- package/src/CheckboxInput/CheckboxInput.tsx +6 -0
- package/src/Citation/Citation.tsx +3 -3
- package/src/ClickableCard/ClickableCard.tsx +2 -2
- package/src/CommandPalette/CommandPaletteItem.tsx +1 -1
- package/src/ComplexSelector/ComplexSelector.doc.mjs +10 -0
- package/src/ComplexSelector/ComplexSelector.tsx +8 -5
- package/src/DateInput/DateInput.test.tsx +17 -1
- package/src/DateInput/DateInput.tsx +13 -9
- package/src/DateRangeInput/DateRangeInput.doc.mjs +16 -0
- package/src/DateRangeInput/DateRangeInput.test.tsx +81 -1
- package/src/DateRangeInput/DateRangeInput.tsx +88 -8
- package/src/DateTimeInput/DateTimeInput.tsx +23 -8
- package/src/Dialog/Dialog.test.tsx +24 -0
- package/src/Dialog/Dialog.tsx +3 -0
- package/src/Field/Field.doc.mjs +3 -0
- package/src/Field/FieldLabel.tsx +20 -4
- package/src/Field/InputClearButton.test.tsx +134 -2
- package/src/Field/InputClearButton.tsx +42 -1
- package/src/Field/inputStyles.stylex.ts +16 -12
- package/src/FileInput/FileInput.tsx +5 -4
- package/src/FormLayout/FormLayout.doc.mjs +13 -0
- package/src/FormLayout/FormLayout.test.tsx +181 -4
- package/src/FormLayout/FormLayout.tsx +33 -2
- package/src/FormLayout/FormLayoutContext.ts +22 -7
- package/src/FormLayout/index.ts +1 -1
- package/src/HoverCard/HoverCard.doc.mjs +18 -0
- package/src/HoverCard/HoverCard.test.tsx +189 -0
- package/src/HoverCard/HoverCard.tsx +22 -2
- package/src/HoverCard/index.ts +1 -0
- package/src/HoverCard/useHoverCard.doc.mjs +2 -0
- package/src/HoverCard/useHoverCard.tsx +103 -7
- package/src/Item/Item.doc.mjs +1 -0
- package/src/Item/Item.test.tsx +50 -0
- package/src/Item/Item.tsx +41 -2
- package/src/Layer/index.ts +10 -0
- package/src/Layer/useLayer.tsx +13 -2
- package/src/Layer/useTouchTrigger.test.ts +70 -0
- package/src/Layer/useTouchTrigger.ts +366 -0
- package/src/Layout/Layout.doc.mjs +18 -0
- package/src/Layout/container.stylex.ts +11 -3
- package/src/Layout/index.ts +4 -0
- package/src/Layout/overlayPaddingReset.test.tsx +222 -0
- package/src/Layout/padding.stylex.ts +192 -12
- package/src/Lightbox/Lightbox.tsx +2 -1
- package/src/Link/Link.tsx +4 -4
- package/src/Markdown/Markdown.doc.mjs +3 -0
- package/src/Markdown/Markdown.test.tsx +78 -0
- package/src/Markdown/Markdown.tsx +41 -1
- package/src/Markdown/parser.ts +60 -2
- package/src/MobileNav/MobileNav.tsx +61 -5
- package/src/MobileNav/MobileNavEntryAnimation.test.tsx +212 -0
- package/src/MobileNav/MobileNavScrollbarGutter.test.tsx +109 -0
- package/src/MultiSelector/MultiSelector.test.tsx +1 -1
- package/src/MultiSelector/MultiSelector.tsx +8 -5
- package/src/NavItem/navItemStyles.stylex.ts +2 -2
- package/src/NavMenu/NavHeadingMenuItem.tsx +1 -1
- package/src/NumberInput/NumberInput.doc.mjs +24 -0
- package/src/NumberInput/NumberInput.test.tsx +138 -0
- package/src/NumberInput/NumberInput.tsx +44 -6
- package/src/Outline/Outline.doc.mjs +2 -0
- package/src/Outline/Outline.tsx +1 -1
- package/src/Outline/parseOutlineFromMarkdown.ts +10 -40
- package/src/PowerSearch/PowerSearch.tsx +17 -8
- package/src/PowerSearch/PowerSearchToken.tsx +3 -0
- package/src/PowerSearch/formatFilterValue.test.ts +22 -2
- package/src/PowerSearch/formatFilterValue.ts +31 -6
- package/src/RadioList/RadioList.doc.mjs +1 -1
- package/src/RadioList/RadioList.test.tsx +170 -2
- package/src/RadioList/RadioList.tsx +9 -1
- package/src/RadioList/RadioListItem.tsx +83 -51
- package/src/Resizable/ResizeHandle.test.tsx +22 -0
- package/src/Resizable/ResizeHandle.tsx +9 -3
- package/src/Section/Section.doc.mjs +55 -0
- package/src/Section/Section.test.tsx +201 -4
- package/src/Section/Section.tsx +69 -0
- package/src/SegmentedControl/SegmentedControl.test.tsx +31 -0
- package/src/SegmentedControl/SegmentedControlItem.tsx +11 -2
- package/src/SelectableCard/SelectableCard.tsx +1 -1
- package/src/Selector/Selector.doc.mjs +16 -2
- package/src/Selector/Selector.test.tsx +525 -1
- package/src/Selector/Selector.tsx +166 -21
- package/src/Selector/SelectorOption.doc.mjs +7 -0
- package/src/Selector/SelectorOption.tsx +21 -0
- package/src/Selector/SelectorRowLayoutContext.ts +32 -0
- package/src/Selector/types.ts +4 -4
- package/src/SideNav/SideNavHeading.tsx +2 -2
- package/src/SideNav/SideNavItem.tsx +1 -1
- package/src/Slider/Slider.tsx +11 -1
- package/src/Stack/Stack.doc.mjs +72 -0
- package/src/Stack/Stack.test.tsx +156 -0
- package/src/Stack/Stack.tsx +50 -8
- package/src/StatusDot/StatusDot.doc.mjs +13 -0
- package/src/StatusDot/StatusDot.test.tsx +115 -8
- package/src/StatusDot/StatusDot.tsx +74 -9
- package/src/Switch/Switch.tsx +6 -0
- package/src/TabList/TabMenu.tsx +1 -1
- package/src/Table/TableRow.tsx +4 -4
- package/src/Table/plugins/columnResize/useTableColumnResize.tsx +2 -1
- package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +2 -1
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +22 -4
- package/src/Table/plugins/sortable/useTableSortableState.test.tsx +34 -0
- package/src/Table/plugins/sortable/useTableSortableState.tsx +26 -23
- package/src/Table/plugins/tree/useTableTreeData.tsx +2 -2
- package/src/TextArea/TextArea.doc.mjs +1 -1
- package/src/TextArea/TextArea.tsx +3 -1
- package/src/TextInput/TextInput.tsx +3 -1
- package/src/Thumbnail/Thumbnail.tsx +1 -1
- package/src/TimeInput/TimeInput.tsx +3 -1
- package/src/Timestamp/Timestamp.test.tsx +27 -7
- package/src/Timestamp/Timestamp.tsx +8 -4
- package/src/Timestamp/formatInstant.ts +9 -7
- package/src/Timestamp/tooltipEntries.test.ts +11 -6
- package/src/Timestamp/tooltipEntries.ts +6 -2
- package/src/Token/Token.tsx +1 -1
- package/src/Tooltip/Tooltip.doc.mjs +18 -0
- package/src/Tooltip/Tooltip.test.tsx +254 -1
- package/src/Tooltip/Tooltip.tsx +22 -2
- package/src/Tooltip/index.ts +1 -0
- package/src/Tooltip/useTooltip.doc.mjs +2 -0
- package/src/Tooltip/useTooltip.tsx +99 -21
- package/src/TopNav/TopNavHeading.tsx +1 -1
- package/src/TopNav/TopNavItem.tsx +2 -2
- package/src/TopNav/TopNavMegaMenu.tsx +1 -1
- package/src/TopNav/TopNavMegaMenuItem.tsx +1 -1
- package/src/TopNav/TopNavMenu.tsx +2 -2
- package/src/TreeList/TreeListItem.tsx +1 -1
- package/src/hooks/containerReveal.stylex.ts +54 -18
- package/src/hooks/scrollbarGutter.test.ts +167 -0
- package/src/hooks/scrollbarGutter.ts +121 -0
- package/src/hooks/useResolvedRequired.ts +42 -0
- package/src/hooks/useScrollLock.doc.mjs +4 -4
- package/src/hooks/useScrollLock.test.ts +73 -0
- package/src/hooks/useScrollLock.ts +14 -0
- package/src/i18n/InternationalizationContext.ts +2 -0
- package/src/i18n/__tests__/e2e-powersearch.test.tsx +37 -1
- package/src/i18n/__tests__/useCollator.test.tsx +115 -0
- package/src/i18n/__tests__/useLocale.test.tsx +52 -0
- package/src/i18n/index.ts +6 -0
- package/src/i18n/useCollator.doc.mjs +84 -0
- package/src/i18n/useCollator.ts +44 -0
- package/src/i18n/useLocale.doc.mjs +70 -0
- package/src/i18n/useLocale.ts +34 -0
- package/src/i18n/useTranslator.doc.mjs +1 -1
- package/src/theme/defineTheme.test.ts +9 -3
- package/src/theme/defineTheme.ts +5 -0
- package/src/theme/derivedVarRegistry.test.ts +19 -5
- package/src/theme/derivedVarRegistry.ts +4 -0
- package/src/theme/generateThemeRules.test.ts +118 -0
- package/src/theme/generateThemeRules.ts +83 -5
- package/src/utils/dateParser.test.ts +20 -0
- package/src/utils/dateParser.ts +14 -7
- package/src/utils/plainDate.test.ts +56 -0
- package/src/utils/plainDate.ts +17 -4
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import {
|
|
20
|
+
use,
|
|
20
21
|
useId,
|
|
21
22
|
useState,
|
|
22
23
|
useCallback,
|
|
@@ -84,10 +85,11 @@ import {
|
|
|
84
85
|
import type {BaseProps} from '../BaseProps';
|
|
85
86
|
import type {SizeValue} from '../utils/types';
|
|
86
87
|
import {useAnnounce} from '../hooks/useAnnounce';
|
|
88
|
+
import {useResolvedRequired} from '../hooks/useResolvedRequired';
|
|
87
89
|
import {useSize} from '../SizeContext/SizeContext';
|
|
88
90
|
import {themeProps} from '../utils/themeProps';
|
|
89
91
|
import {focusOutlineStyles} from '../utils/focusOutline.stylex';
|
|
90
|
-
import {useTranslator} from '../i18n';
|
|
92
|
+
import {useTranslator, InternationalizationContext} from '../i18n';
|
|
91
93
|
|
|
92
94
|
export type ISODateTimeString = string & {
|
|
93
95
|
readonly __brand: 'ISODateTimeString';
|
|
@@ -448,6 +450,8 @@ export function DateTimeInput({
|
|
|
448
450
|
...rest
|
|
449
451
|
}: DateTimeInputProps) {
|
|
450
452
|
const t = useTranslator();
|
|
453
|
+
const isEffectivelyRequired = useResolvedRequired({isRequired, isOptional});
|
|
454
|
+
const {locale} = use(InternationalizationContext);
|
|
451
455
|
// Speaks arrow-key stepping results through the persistent live regions:
|
|
452
456
|
// stepping programmatically rewrites a plain textbox's value, which screen
|
|
453
457
|
// readers do not announce on their own (WCAG 4.1.2).
|
|
@@ -558,13 +562,17 @@ export function DateTimeInput({
|
|
|
558
562
|
datePendingInput !== null
|
|
559
563
|
? datePendingInput
|
|
560
564
|
: valueParts.date && /^\d{4}-\d{2}-\d{2}$/.test(valueParts.date)
|
|
561
|
-
? plainDateFormat(
|
|
565
|
+
? plainDateFormat(
|
|
566
|
+
plainDateFromISO(valueParts.date),
|
|
567
|
+
DATE_FORMAT_LONG,
|
|
568
|
+
locale,
|
|
569
|
+
)
|
|
562
570
|
: '';
|
|
563
571
|
|
|
564
572
|
const isDateInputValid =
|
|
565
573
|
datePendingInput === null || !datePendingInput.trim()
|
|
566
574
|
? true
|
|
567
|
-
: parseDateInput(datePendingInput) !== null;
|
|
575
|
+
: parseDateInput(datePendingInput, locale) !== null;
|
|
568
576
|
|
|
569
577
|
// --- Time input state ---
|
|
570
578
|
const [timePendingInput, setTimePendingInput] = useState<string | null>(null);
|
|
@@ -692,7 +700,7 @@ export function DateTimeInput({
|
|
|
692
700
|
const text = e.target.value;
|
|
693
701
|
setDatePendingInput(text);
|
|
694
702
|
|
|
695
|
-
const parsed = parseDateInput(text);
|
|
703
|
+
const parsed = parseDateInput(text, locale);
|
|
696
704
|
if (
|
|
697
705
|
parsed &&
|
|
698
706
|
plainDateToISO(parsed) !== valueParts.date &&
|
|
@@ -704,7 +712,13 @@ export function DateTimeInput({
|
|
|
704
712
|
calendarRef.current?.navigateTo(parsedISO);
|
|
705
713
|
}
|
|
706
714
|
},
|
|
707
|
-
[
|
|
715
|
+
[
|
|
716
|
+
valueParts.date,
|
|
717
|
+
isDateDisabled,
|
|
718
|
+
handleDateChange,
|
|
719
|
+
isEffectivelyDisabled,
|
|
720
|
+
locale,
|
|
721
|
+
],
|
|
708
722
|
);
|
|
709
723
|
|
|
710
724
|
const commitDatePendingInput = useCallback(() => {
|
|
@@ -720,7 +734,7 @@ export function DateTimeInput({
|
|
|
720
734
|
return;
|
|
721
735
|
}
|
|
722
736
|
|
|
723
|
-
const parsed = parseDateInput(datePendingInput);
|
|
737
|
+
const parsed = parseDateInput(datePendingInput, locale);
|
|
724
738
|
if (parsed && !isDateDisabled(parsed)) {
|
|
725
739
|
const parsedISO = plainDateToISO(parsed);
|
|
726
740
|
if (parsedISO !== valueParts.date) {
|
|
@@ -735,6 +749,7 @@ export function DateTimeInput({
|
|
|
735
749
|
fireChange,
|
|
736
750
|
isDateDisabled,
|
|
737
751
|
handleDateChange,
|
|
752
|
+
locale,
|
|
738
753
|
]);
|
|
739
754
|
|
|
740
755
|
const handleDateBlur = useCallback(() => {
|
|
@@ -1009,7 +1024,7 @@ export function DateTimeInput({
|
|
|
1009
1024
|
aria-disabled={showsDisabledMessage ? 'true' : undefined}
|
|
1010
1025
|
readOnly={showsDisabledMessage || undefined}
|
|
1011
1026
|
aria-describedby={ariaDescribedBy}
|
|
1012
|
-
aria-required={
|
|
1027
|
+
aria-required={isEffectivelyRequired ? 'true' : undefined}
|
|
1013
1028
|
aria-invalid={
|
|
1014
1029
|
status?.type === 'error' || !isDateInputValid ? 'true' : undefined
|
|
1015
1030
|
}
|
|
@@ -1098,7 +1113,7 @@ export function DateTimeInput({
|
|
|
1098
1113
|
timeLabel ?? t('@astryx.dateTimeInput.timeSuffix', {label})
|
|
1099
1114
|
}
|
|
1100
1115
|
aria-describedby={ariaDescribedBy}
|
|
1101
|
-
aria-required={
|
|
1116
|
+
aria-required={isEffectivelyRequired ? 'true' : undefined}
|
|
1102
1117
|
aria-invalid={
|
|
1103
1118
|
status?.type === 'error' || !isTimeInputValid ? 'true' : undefined
|
|
1104
1119
|
}
|
|
@@ -461,4 +461,28 @@ describe('Dialog', () => {
|
|
|
461
461
|
before.remove();
|
|
462
462
|
});
|
|
463
463
|
});
|
|
464
|
+
|
|
465
|
+
describe('container padding isolation', () => {
|
|
466
|
+
it('resets container padding custom properties on the root dialog element', () => {
|
|
467
|
+
render(
|
|
468
|
+
<Dialog isOpen={true} onOpenChange={() => {}}>
|
|
469
|
+
Content
|
|
470
|
+
</Dialog>,
|
|
471
|
+
);
|
|
472
|
+
const dialog = screen.getByRole('dialog');
|
|
473
|
+
const computed = window.getComputedStyle(dialog);
|
|
474
|
+
expect(
|
|
475
|
+
computed.getPropertyValue('--container-padding-inline-start'),
|
|
476
|
+
).toBe('0px');
|
|
477
|
+
expect(computed.getPropertyValue('--container-padding-inline-end')).toBe(
|
|
478
|
+
'0px',
|
|
479
|
+
);
|
|
480
|
+
expect(computed.getPropertyValue('--container-padding-block-start')).toBe(
|
|
481
|
+
'0px',
|
|
482
|
+
);
|
|
483
|
+
expect(computed.getPropertyValue('--container-padding-block-end')).toBe(
|
|
484
|
+
'0px',
|
|
485
|
+
);
|
|
486
|
+
});
|
|
487
|
+
});
|
|
464
488
|
});
|
package/src/Dialog/Dialog.tsx
CHANGED
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
containerPaddingBlockStartVarStyles,
|
|
45
45
|
containerPaddingBlockEndVarStyles,
|
|
46
46
|
spacingStepToToken,
|
|
47
|
+
overlayPaddingReset,
|
|
47
48
|
} from '../Layout/padding.stylex';
|
|
48
49
|
import type {SpacingStep} from '../utils/types';
|
|
49
50
|
import {mergeProps, mergeRefs} from '../utils';
|
|
@@ -610,6 +611,7 @@ export function Dialog({
|
|
|
610
611
|
themeProps('dialog', {variant}),
|
|
611
612
|
stylex.props(
|
|
612
613
|
styles.inlineWrapper,
|
|
614
|
+
overlayPaddingReset.reset,
|
|
613
615
|
!isFullscreen && dynamicStyles.sizing(width, maxHeight),
|
|
614
616
|
isFullscreen && styles.fullscreen,
|
|
615
617
|
xstyle,
|
|
@@ -636,6 +638,7 @@ export function Dialog({
|
|
|
636
638
|
themeProps('dialog', {variant}),
|
|
637
639
|
focusOutlineProps.focusVisible(
|
|
638
640
|
styles.dialog,
|
|
641
|
+
overlayPaddingReset.reset,
|
|
639
642
|
isOpen && styles.open,
|
|
640
643
|
styles.backdrop,
|
|
641
644
|
!isFullscreen && dynamicStyles.sizing(width, maxHeight),
|
package/src/Field/Field.doc.mjs
CHANGED
|
@@ -34,10 +34,13 @@ export const docs = {
|
|
|
34
34
|
className: 'astryx-input-status-icon',
|
|
35
35
|
visualProps: ['size', 'status'],
|
|
36
36
|
},
|
|
37
|
+
{className: 'astryx-input-clear-button'},
|
|
37
38
|
{className: 'astryx-input-clear-icon'},
|
|
38
39
|
],
|
|
39
40
|
vars: [
|
|
40
41
|
{name: '--_field-radius', description: 'Border radius of input fields', default: 'var(--radius-element)', private: true},
|
|
42
|
+
{name: '--_input-clear-hit-inset', description: 'Outset of the clear (\u2715) button\u2019s invisible hit area, applied to a ::after overlay. 0 on a fine pointer; negative on a coarse one, which grows the 20px button to the 24px touch target without changing what is drawn.', default: '0px', private: true},
|
|
43
|
+
{name: '--_input-clear-hit-content', description: 'Whether the clear (\u2715) button\u2019s invisible hit overlay exists. `none` on a fine pointer, so no ::after is generated and hover still reaches the glyph; `""` on a coarse one, where the overlay provides the 24px touch target.', default: 'none', private: true},
|
|
41
44
|
],
|
|
42
45
|
derived: [
|
|
43
46
|
{property: 'borderRadius', vars: ['--_field-radius']},
|
package/src/Field/FieldLabel.tsx
CHANGED
|
@@ -4,18 +4,19 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file FieldLabel.tsx
|
|
7
|
-
* @input Uses React, Icon, IconType, useTranslator
|
|
7
|
+
* @input Uses React, Icon, IconType, useTranslator, FormLayoutContext
|
|
8
8
|
* @output Exports FieldLabel component, FieldLabelProps
|
|
9
9
|
* @position Core label implementation; used by Field, CheckboxInput, Switch
|
|
10
10
|
*
|
|
11
11
|
* SYNC: When modified, update these files to stay in sync:
|
|
12
12
|
* - /packages/core/src/Field/Field.doc.mjs (props table, features, implementation notes)
|
|
13
13
|
* - /packages/core/src/Field/index.ts (exports if types change)
|
|
14
|
+
* - /packages/core/src/FormLayout/FormLayoutContext.ts (defaultOptionality drives the indicator)
|
|
14
15
|
* - /packages/cli/assets/templates/blocks/components/Field/ (showcase blocks)
|
|
15
16
|
* - /packages/core/locales/en.json (@astryx.field.required / @astryx.field.optional)
|
|
16
17
|
*/
|
|
17
18
|
|
|
18
|
-
import {useMemo, useRef, type ReactNode, type RefObject} from 'react';
|
|
19
|
+
import {use, useMemo, useRef, type ReactNode, type RefObject} from 'react';
|
|
19
20
|
import * as stylex from '@stylexjs/stylex';
|
|
20
21
|
import type {BaseProps} from '../BaseProps';
|
|
21
22
|
import {mergeProps} from '../utils';
|
|
@@ -32,6 +33,7 @@ import {Tooltip} from '../Tooltip';
|
|
|
32
33
|
import {useTranslator} from '../i18n';
|
|
33
34
|
import {themeProps} from '../utils/themeProps';
|
|
34
35
|
import {useInputContainer} from '../hooks';
|
|
36
|
+
import {FormLayoutContext} from '../FormLayout/FormLayoutContext';
|
|
35
37
|
|
|
36
38
|
const styles = stylex.create({
|
|
37
39
|
label: {
|
|
@@ -188,9 +190,23 @@ export function FieldLabel({
|
|
|
188
190
|
...rest
|
|
189
191
|
}: FieldLabelProps) {
|
|
190
192
|
const t = useTranslator();
|
|
191
|
-
const
|
|
193
|
+
const {defaultOptionality} = use(FormLayoutContext);
|
|
194
|
+
|
|
195
|
+
// A form-level `defaultOptionality` means "only the exception is marked": a
|
|
196
|
+
// field that merely restates the form's default shows no indicator, and only
|
|
197
|
+
// a deviation from it does. This is the *visible indicator* only; the
|
|
198
|
+
// matching `aria-required` is resolved on each control (see
|
|
199
|
+
// useResolvedRequired) so the unmarked majority is still announced.
|
|
200
|
+
//
|
|
201
|
+
// defaultOptionality isRequired isOptional
|
|
202
|
+
// 'optional' → required indicator → (matches default, hidden)
|
|
203
|
+
// 'required' → (matches, hidden) → optional indicator
|
|
204
|
+
// unset → required indicator → optional indicator
|
|
205
|
+
const showRequired = isRequired && defaultOptionality !== 'required';
|
|
206
|
+
const showOptional = isOptional && defaultOptionality !== 'optional';
|
|
207
|
+
const statusText = showOptional
|
|
192
208
|
? t('@astryx.field.optional')
|
|
193
|
-
:
|
|
209
|
+
: showRequired
|
|
194
210
|
? t('@astryx.field.required')
|
|
195
211
|
: null;
|
|
196
212
|
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @file InputClearButton.test.tsx
|
|
5
|
-
* @input Uses vitest, @testing-library/react, InputClearButton, Icon,
|
|
5
|
+
* @input Uses node:fs, node:path, vitest, @testing-library/react, InputClearButton, Icon,
|
|
6
|
+
* theme
|
|
6
7
|
* @output Unit tests for the shared clear-button primitive
|
|
7
8
|
* @position Testing; validates InputClearButton.tsx — the single home for the
|
|
8
9
|
* clearable input family's clear (✕) affordance and its theme target.
|
|
@@ -10,6 +11,8 @@
|
|
|
10
11
|
* SYNC: When InputClearButton.tsx changes, update tests to match new behavior.
|
|
11
12
|
*/
|
|
12
13
|
|
|
14
|
+
import {readFileSync} from 'node:fs';
|
|
15
|
+
import path from 'node:path';
|
|
13
16
|
import {describe, it, expect, vi} from 'vitest';
|
|
14
17
|
import {render, screen, fireEvent} from '@testing-library/react';
|
|
15
18
|
import {InputClearButton} from './InputClearButton';
|
|
@@ -22,6 +25,39 @@ function generateThemeTestCSS(theme: Parameters<typeof generateThemeCSS>[0]) {
|
|
|
22
25
|
return [prose, component].filter(Boolean).join('\n\n');
|
|
23
26
|
}
|
|
24
27
|
|
|
28
|
+
interface InjectedRule {
|
|
29
|
+
selector: string;
|
|
30
|
+
text: string;
|
|
31
|
+
media: string | null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Every style rule StyleX has injected at runtime (`runtimeInjection` in the
|
|
36
|
+
* root vitest config), flattened out of any at-rule wrapper and tagged with
|
|
37
|
+
* that wrapper's condition — so a `@media (pointer: coarse)` rule stays
|
|
38
|
+
* distinguishable from the unconditional one.
|
|
39
|
+
*/
|
|
40
|
+
function injectedRules(): InjectedRule[] {
|
|
41
|
+
const walk = (rules: CSSRuleList, condition: string | null) =>
|
|
42
|
+
[...rules].flatMap((rule): InjectedRule[] => {
|
|
43
|
+
const {selectorText} = rule as CSSStyleRule;
|
|
44
|
+
if (typeof selectorText === 'string') {
|
|
45
|
+
return [{selector: selectorText, text: rule.cssText, media: condition}];
|
|
46
|
+
}
|
|
47
|
+
// A grouping rule (@media, @keyframes, ...): descend, carrying the
|
|
48
|
+
// innermost condition down. jsdom hangs an empty `cssRules` off plain
|
|
49
|
+
// style rules too, which is why the leaf check comes first.
|
|
50
|
+
const nested = (rule as CSSGroupingRule).cssRules;
|
|
51
|
+
if (nested == null) {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
const own = (rule as CSSMediaRule).media?.mediaText;
|
|
55
|
+
return walk(nested, own != null && own !== '' ? own : condition);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return [...document.styleSheets].flatMap(sheet => walk(sheet.cssRules, null));
|
|
59
|
+
}
|
|
60
|
+
|
|
25
61
|
const getGlyph = (): HTMLElement => {
|
|
26
62
|
const button = screen.getByRole('button', {name: 'Clear'});
|
|
27
63
|
const icon = button.querySelector('.astryx-icon');
|
|
@@ -111,7 +147,103 @@ describe('InputClearButton', () => {
|
|
|
111
147
|
const css = generateThemeTestCSS(theme);
|
|
112
148
|
expect(css).toContain('.astryx-input-clear-icon {');
|
|
113
149
|
expect(css).toContain('width: 12px');
|
|
114
|
-
expect(css).toContain('.astryx-input-clear-icon:hover
|
|
150
|
+
expect(css).toContain('.astryx-input-clear-icon:hover');
|
|
115
151
|
expect(css).toContain('color: var(--color-icon-primary)');
|
|
116
152
|
});
|
|
153
|
+
|
|
154
|
+
it('renders the astryx-input-clear-button target on the button wrapper', () => {
|
|
155
|
+
render(<InputClearButton label="Clear" onClick={() => {}} />);
|
|
156
|
+
const button = screen.getByRole('button', {name: 'Clear'});
|
|
157
|
+
expect(button).toHaveClass('astryx-input-clear-button');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('grows the hit area to 24px on a coarse pointer only (WCAG 2.5.8 AA)', () => {
|
|
161
|
+
// The visual glyph stays 20px; an ::after overlay expands only the
|
|
162
|
+
// tappable region, and only under a coarse pointer. Asserted against the
|
|
163
|
+
// CSS StyleX injects at runtime, because jsdom resolves neither media
|
|
164
|
+
// queries nor pseudo-element boxes.
|
|
165
|
+
render(<InputClearButton label="Clear" onClick={() => {}} />);
|
|
166
|
+
const button = screen.getByRole('button', {name: 'Clear'});
|
|
167
|
+
const classes = new Set(button.className.split(' ').filter(Boolean));
|
|
168
|
+
|
|
169
|
+
const css = injectedRules();
|
|
170
|
+
const rulesForButton = css.filter(({selector}) =>
|
|
171
|
+
[...classes].some(c => selector.includes(`.${c}`)),
|
|
172
|
+
);
|
|
173
|
+
// Guards every assertion below against silently passing if StyleX's
|
|
174
|
+
// runtime injection or the class plumbing ever changes shape.
|
|
175
|
+
expect(rulesForButton.length).toBeGreaterThan(0);
|
|
176
|
+
|
|
177
|
+
const decl = (pattern: RegExp, inMedia?: string) =>
|
|
178
|
+
rulesForButton.some(
|
|
179
|
+
({text, media}) =>
|
|
180
|
+
pattern.test(text) &&
|
|
181
|
+
(inMedia == null ? media == null : (media ?? '').includes(inMedia)),
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
// Fine pointer: hit area == the 20px visual glyph, no expansion.
|
|
185
|
+
expect(decl(/--_input-clear-hit-inset\s*:\s*0px/)).toBe(true);
|
|
186
|
+
// Coarse pointer: 20px + 2px on each side = 24x24, the AA floor.
|
|
187
|
+
expect(decl(/--_input-clear-hit-inset\s*:\s*-2px/, 'pointer: coarse')).toBe(
|
|
188
|
+
true,
|
|
189
|
+
);
|
|
190
|
+
// ...and nothing wider than that, which would reach into the adornment
|
|
191
|
+
// gap and the input's caret area. Scoped to the coarse block, because
|
|
192
|
+
// that is the only place a wider value can appear.
|
|
193
|
+
expect(
|
|
194
|
+
decl(
|
|
195
|
+
/--_input-clear-hit-inset\s*:\s*-(?:[3-9]|\d{2,})px/,
|
|
196
|
+
'pointer: coarse',
|
|
197
|
+
),
|
|
198
|
+
).toBe(false);
|
|
199
|
+
// The overlay exists only on touch. On a fine pointer a generated
|
|
200
|
+
// ::after would cover the button and take hover away from the glyph
|
|
201
|
+
// target, so `content` is gated the same way the inset is.
|
|
202
|
+
expect(decl(/--_input-clear-hit-content\s*:\s*none/)).toBe(true);
|
|
203
|
+
expect(decl(/--_input-clear-hit-content\s*:\s*""/, 'pointer: coarse')).toBe(
|
|
204
|
+
true,
|
|
205
|
+
);
|
|
206
|
+
// The overlay itself is what carries the expansion, and what is gated.
|
|
207
|
+
expect(
|
|
208
|
+
decl(/::after\s*\{[^}]*inset\s*:\s*var\(--_input-clear-hit-inset\)/),
|
|
209
|
+
).toBe(true);
|
|
210
|
+
expect(
|
|
211
|
+
decl(/::after\s*\{[^}]*content\s*:\s*var\(--_input-clear-hit-content\)/),
|
|
212
|
+
).toBe(true);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('declares its own containing block for the hit overlay', () => {
|
|
216
|
+
// The ::after overlay must resolve against this button. Button happens to
|
|
217
|
+
// set `position: relative` on itself, so at runtime the overlay is
|
|
218
|
+
// correctly placed either way — and StyleX compiles both declarations to
|
|
219
|
+
// the same atomic class, so the rendered CSS cannot tell the two apart.
|
|
220
|
+
// Assert on the source instead, so a future edit can't quietly leave the
|
|
221
|
+
// overlay depending on another component's internal.
|
|
222
|
+
const source = readFileSync(
|
|
223
|
+
path.resolve(__dirname, './InputClearButton.tsx'),
|
|
224
|
+
'utf-8',
|
|
225
|
+
);
|
|
226
|
+
const buttonStyle = source.match(/button:\s*\{[\s\S]*?\n {2}\},/)?.[0];
|
|
227
|
+
expect(buttonStyle).toBeDefined();
|
|
228
|
+
expect(buttonStyle).toMatch(/position:\s*'relative'/);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('exposes input-clear-button so a theme controls the button size and hover', () => {
|
|
232
|
+
const theme = defineTheme({
|
|
233
|
+
name: 'input-clear-button-test',
|
|
234
|
+
components: {
|
|
235
|
+
'input-clear-button': {
|
|
236
|
+
base: {
|
|
237
|
+
height: '28px',
|
|
238
|
+
':hover': {backgroundImage: 'none'},
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
const css = generateThemeTestCSS(theme);
|
|
244
|
+
expect(css).toContain('.astryx-input-clear-button {');
|
|
245
|
+
expect(css).toContain('height: 28px');
|
|
246
|
+
expect(css).toContain('.astryx-input-clear-button:hover');
|
|
247
|
+
expect(css).toContain('background-image: none');
|
|
248
|
+
});
|
|
117
249
|
});
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* TextInput, NumberInput, TimeInput, DateInput, DateTimeInput,
|
|
12
12
|
* DateRangeInput, Selector, MultiSelector, Typeahead, Tokenizer, FileInput —
|
|
13
13
|
* routes it through here, so the glyph is themed in one place via the
|
|
14
|
-
* `astryx-input-clear-icon` target
|
|
14
|
+
* `astryx-input-clear-icon` target and the button wrapper is themed via the
|
|
15
|
+
* `astryx-input-clear-button` target.
|
|
15
16
|
*/
|
|
16
17
|
|
|
17
18
|
import type {ReactNode} from 'react';
|
|
@@ -24,6 +25,44 @@ const styles = stylex.create({
|
|
|
24
25
|
button: {
|
|
25
26
|
height: '20px',
|
|
26
27
|
flexShrink: 0,
|
|
28
|
+
// Containing block for the ::after hit overlay below. Button sets its own
|
|
29
|
+
// `position: relative`, so the overlay would resolve correctly without
|
|
30
|
+
// this — but that is another component's internal, and if it ever changes
|
|
31
|
+
// the overlay silently reattaches to some ancestor and the hit area lands
|
|
32
|
+
// in the wrong place. Declaring it here keeps the containing block
|
|
33
|
+
// colocated with the thing that depends on it. No-op at runtime.
|
|
34
|
+
position: 'relative',
|
|
35
|
+
// Expand the tap target to 24px ONLY on touch (WCAG 2.5.8 AA is a touch
|
|
36
|
+
// requirement, and its floor is 24x24). On a fine pointer the 20px glyph
|
|
37
|
+
// is precise enough, and an unconditional overlay could overlap
|
|
38
|
+
// neighboring controls in dense layouts. The inset is 0 by default (hit
|
|
39
|
+
// area == visual glyph) and grows to -2px (=> 24x24) under a coarse
|
|
40
|
+
// pointer — no further, because at -4px the overlay reaches into the 8px
|
|
41
|
+
// adornment gap and, on the inline-start side, over the input's own caret
|
|
42
|
+
// area. Driven through a custom property because StyleX only allows plain
|
|
43
|
+
// values inside a pseudo-element; the conditional lives on this top-level
|
|
44
|
+
// property instead. Private (--_) because it is an internal implementation
|
|
45
|
+
// detail, not a themeable target.
|
|
46
|
+
'--_input-clear-hit-inset': {
|
|
47
|
+
default: '0px',
|
|
48
|
+
'@media (pointer: coarse)': '-2px',
|
|
49
|
+
},
|
|
50
|
+
// The overlay itself is gated too, not just its size. An ::after that is
|
|
51
|
+
// generated on a fine pointer sits exactly over the button at inset 0,
|
|
52
|
+
// where it adds no hit area but is the topmost hit-test box — so hover
|
|
53
|
+
// stops reaching the descendants and `.astryx-input-clear-icon:hover`, a
|
|
54
|
+
// public theme target, no longer matches. `content: none` means the
|
|
55
|
+
// pseudo-element is not generated at all, so on a fine pointer the
|
|
56
|
+
// overlay does not exist.
|
|
57
|
+
'--_input-clear-hit-content': {
|
|
58
|
+
default: 'none',
|
|
59
|
+
'@media (pointer: coarse)': '""',
|
|
60
|
+
},
|
|
61
|
+
'::after': {
|
|
62
|
+
content: 'var(--_input-clear-hit-content)',
|
|
63
|
+
position: 'absolute',
|
|
64
|
+
inset: 'var(--_input-clear-hit-inset)',
|
|
65
|
+
},
|
|
27
66
|
},
|
|
28
67
|
});
|
|
29
68
|
|
|
@@ -48,11 +87,13 @@ export function InputClearButton({
|
|
|
48
87
|
iconClassName,
|
|
49
88
|
}: InputClearButtonProps): ReactNode {
|
|
50
89
|
const {className: iconTargetClassName} = themeProps('input-clear-icon');
|
|
90
|
+
const {className: buttonTargetClassName} = themeProps('input-clear-button');
|
|
51
91
|
return (
|
|
52
92
|
<Button
|
|
53
93
|
variant="ghost"
|
|
54
94
|
size="sm"
|
|
55
95
|
label={label}
|
|
96
|
+
className={buttonTargetClassName}
|
|
56
97
|
icon={
|
|
57
98
|
<Icon
|
|
58
99
|
icon="close"
|
|
@@ -56,9 +56,10 @@ export const inputWrapperStyles = stylex.create({
|
|
|
56
56
|
transitionTimingFunction: easeVars['--ease-standard'],
|
|
57
57
|
boxShadow: {
|
|
58
58
|
default: 'none',
|
|
59
|
-
':hover:not(:focus-within)':
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
':hover:not(:focus-within):where(:not(:disabled,[aria-disabled="true"]))':
|
|
60
|
+
{
|
|
61
|
+
'@media (hover: hover)': `inset 0px 0px 0px 2px color-mix(in srgb, ${colorVars['--color-border-emphasized']} 30%, transparent)`,
|
|
62
|
+
},
|
|
62
63
|
':focus-within': `inset 0px 0px 0px 2px ${colorVars['--color-accent-muted']}`,
|
|
63
64
|
},
|
|
64
65
|
outline: 'none',
|
|
@@ -96,25 +97,28 @@ export const inputStatusHoverShadowStyles = stylex.create({
|
|
|
96
97
|
warning: {
|
|
97
98
|
boxShadow: {
|
|
98
99
|
default: 'none',
|
|
99
|
-
':hover:not(:focus-within)':
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
':hover:not(:focus-within):where(:not(:disabled,[aria-disabled="true"]))':
|
|
101
|
+
{
|
|
102
|
+
'@media (hover: hover)': shadowVars['--shadow-inset-warning'],
|
|
103
|
+
},
|
|
102
104
|
},
|
|
103
105
|
},
|
|
104
106
|
error: {
|
|
105
107
|
boxShadow: {
|
|
106
108
|
default: 'none',
|
|
107
|
-
':hover:not(:focus-within)':
|
|
108
|
-
|
|
109
|
-
|
|
109
|
+
':hover:not(:focus-within):where(:not(:disabled,[aria-disabled="true"]))':
|
|
110
|
+
{
|
|
111
|
+
'@media (hover: hover)': shadowVars['--shadow-inset-error'],
|
|
112
|
+
},
|
|
110
113
|
},
|
|
111
114
|
},
|
|
112
115
|
success: {
|
|
113
116
|
boxShadow: {
|
|
114
117
|
default: 'none',
|
|
115
|
-
':hover:not(:focus-within)':
|
|
116
|
-
|
|
117
|
-
|
|
118
|
+
':hover:not(:focus-within):where(:not(:disabled,[aria-disabled="true"]))':
|
|
119
|
+
{
|
|
120
|
+
'@media (hover: hover)': shadowVars['--shadow-inset-success'],
|
|
121
|
+
},
|
|
118
122
|
},
|
|
119
123
|
},
|
|
120
124
|
});
|
|
@@ -158,7 +158,7 @@ const styles = stylex.create({
|
|
|
158
158
|
dropzoneHover: {
|
|
159
159
|
boxShadow: {
|
|
160
160
|
default: null,
|
|
161
|
-
':hover': {
|
|
161
|
+
':hover:where(:not(:disabled,[aria-disabled="true"]))': {
|
|
162
162
|
'@media (hover: hover)': `inset 0 0 0 2px color-mix(in srgb, ${colorVars['--color-accent']} 20%, transparent)`,
|
|
163
163
|
},
|
|
164
164
|
},
|
|
@@ -197,9 +197,10 @@ const styles = stylex.create({
|
|
|
197
197
|
transitionTimingFunction: easeVars['--ease-standard'],
|
|
198
198
|
boxShadow: {
|
|
199
199
|
default: 'none',
|
|
200
|
-
':hover:not(:focus-within)':
|
|
201
|
-
|
|
202
|
-
|
|
200
|
+
':hover:not(:focus-within):where(:not(:disabled,[aria-disabled="true"]))':
|
|
201
|
+
{
|
|
202
|
+
'@media (hover: hover)': `inset 0 0 0 2px color-mix(in srgb, ${colorVars['--color-accent']} 20%, transparent)`,
|
|
203
|
+
},
|
|
203
204
|
},
|
|
204
205
|
cursor: 'pointer',
|
|
205
206
|
height: sizeVars['--size-element-md'],
|
|
@@ -16,6 +16,12 @@ export const docs = {
|
|
|
16
16
|
'Controls field arrangement. Vertical stacks top-to-bottom, horizontal arranges left-to-right with equal flex-grow, and horizontal-labels uses CSS Grid with labels to the left of inputs (collapses to vertical on narrow viewports <=480px).',
|
|
17
17
|
default: "'vertical'",
|
|
18
18
|
},
|
|
19
|
+
{
|
|
20
|
+
name: 'defaultOptionality',
|
|
21
|
+
type: "'optional' | 'required'",
|
|
22
|
+
description:
|
|
23
|
+
'The state the form treats as its default, so only the exception shows an optional/required indicator. With "optional", only fields marked isRequired show an indicator; with "required", only fields marked isOptional do. A field that restates the default shows nothing. Under "required" the unmarked fields also expose aria-required so screen readers match the visual default; aria-required only, never the native required attribute. Leave unset for today\'s per-field behavior.',
|
|
24
|
+
},
|
|
19
25
|
{
|
|
20
26
|
name: 'children',
|
|
21
27
|
type: 'ReactNode',
|
|
@@ -64,6 +70,12 @@ export const docsZh = {
|
|
|
64
70
|
'控制字段排列方式。vertical 从上到下堆叠,horizontal 从左到右排列且等比弹性增长,horizontal-labels 使用 CSS Grid 将标签放在输入框左侧(在窄视口 <=480px 时折叠为垂直布局)。',
|
|
65
71
|
default: "'vertical'",
|
|
66
72
|
},
|
|
73
|
+
{
|
|
74
|
+
name: 'defaultOptionality',
|
|
75
|
+
type: "'optional' | 'required'",
|
|
76
|
+
description:
|
|
77
|
+
'表单视为默认的状态,因此仅例外字段显示可选/必填指示器。设为 "optional" 时,仅标记 isRequired 的字段显示指示器;设为 "required" 时,仅标记 isOptional 的字段显示。与默认一致的字段不显示任何内容。设为 "required" 时,未标记的字段仍会暴露 aria-required,使屏幕阅读器与视觉默认一致——仅作用于 aria-required,不改变原生 required 属性。不设置则保持当前逐字段行为。',
|
|
78
|
+
},
|
|
67
79
|
{
|
|
68
80
|
name: 'children',
|
|
69
81
|
type: 'ReactNode',
|
|
@@ -122,6 +134,7 @@ export const docsDense = {
|
|
|
122
134
|
},
|
|
123
135
|
propDescriptions: {
|
|
124
136
|
direction: 'Field arrangement. Vertical stacks top-to-bottom, horizontal arranges left-to-right w/ equal flex-grow, horizontal-labels uses CSS Grid w/ labels left of inputs (collapses <=480px).',
|
|
137
|
+
defaultOptionality: 'State the form treats as default, so only the exception is marked. "optional" → only isRequired fields show an indicator; "required" → only isOptional fields do. Under "required" unmarked fields also expose aria-required (aria only, not native required). Unset = per-field behavior.',
|
|
125
138
|
children: 'Form fields to arrange. Accepts Astryx inputs + Field-wrapped custom controls.',
|
|
126
139
|
xstyle: 'StyleX styles for layout customization. Must be stylex.create() value.',
|
|
127
140
|
},
|