@astryxdesign/core 0.4.4 → 0.4.5-canary.0472ec1
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/Blockquote/Blockquote.d.ts +4 -3
- package/dist/Blockquote/Blockquote.d.ts.map +1 -1
- package/dist/Blockquote/Blockquote.js +9 -10
- 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 +77 -18
- 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 +31 -2
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +92 -19
- package/dist/DateInput/MonthScroller.d.ts +35 -0
- package/dist/DateInput/MonthScroller.d.ts.map +1 -0
- package/dist/DateInput/MonthScroller.js +573 -0
- package/dist/DateInput/MonthYearWheels.d.ts +24 -0
- package/dist/DateInput/MonthYearWheels.d.ts.map +1 -0
- package/dist/DateInput/MonthYearWheels.js +114 -0
- package/dist/DateInput/TouchDateField.d.ts +10 -0
- package/dist/DateInput/TouchDateField.d.ts.map +1 -0
- package/dist/DateInput/TouchDateField.js +810 -0
- package/dist/DateInput/Wheel.d.ts +41 -0
- package/dist/DateInput/Wheel.d.ts.map +1 -0
- package/dist/DateInput/Wheel.js +315 -0
- package/dist/DateInput/monthGeometry.d.ts +65 -0
- package/dist/DateInput/monthGeometry.d.ts.map +1 -0
- package/dist/DateInput/monthGeometry.js +98 -0
- package/dist/DateInput/tokens.stylex.d.ts +63 -0
- package/dist/DateInput/tokens.stylex.d.ts.map +1 -0
- package/dist/DateInput/tokens.stylex.js +74 -0
- package/dist/DateInput/useOwnScrollGesture.d.ts +41 -0
- package/dist/DateInput/useOwnScrollGesture.d.ts.map +1 -0
- package/dist/DateInput/useOwnScrollGesture.js +202 -0
- package/dist/DateInput/usePointerDragScroll.d.ts +15 -0
- package/dist/DateInput/usePointerDragScroll.d.ts.map +1 -0
- package/dist/DateInput/usePointerDragScroll.js +216 -0
- package/dist/DateInput/useScrollSettle.d.ts +26 -0
- package/dist/DateInput/useScrollSettle.d.ts.map +1 -0
- package/dist/DateInput/useScrollSettle.js +149 -0
- 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 +123 -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/theme/syntax/index.d.ts +6 -0
- package/dist/theme/syntax/index.d.ts.map +1 -1
- package/dist/theme/syntax/index.js +7 -2
- 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/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/plainDate.d.ts +11 -2
- package/dist/utils/plainDate.d.ts.map +1 -1
- package/dist/utils/plainDate.js +32 -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 +32 -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 +24 -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/Blockquote/Blockquote.doc.mjs +30 -7
- package/src/Blockquote/Blockquote.test.tsx +34 -3
- package/src/Blockquote/Blockquote.tsx +11 -9
- 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 +91 -0
- package/src/BottomSheet/BottomSheetPanel.tsx +121 -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 +90 -19
- package/src/DateInput/DateInputTouch.test.tsx +2393 -0
- package/src/DateInput/MonthScroller.tsx +784 -0
- package/src/DateInput/MonthYearWheels.tsx +147 -0
- package/src/DateInput/TouchDateField.tsx +1214 -0
- package/src/DateInput/Wheel.tsx +470 -0
- package/src/DateInput/monthGeometry.ts +118 -0
- package/src/DateInput/tokens.stylex.ts +99 -0
- package/src/DateInput/useOwnScrollGesture.ts +227 -0
- package/src/DateInput/usePointerDragScroll.ts +220 -0
- package/src/DateInput/useScrollSettle.ts +164 -0
- 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/theme/syntax/index.ts +6 -2
- package/src/theme/syntax/serverSafeSyntax.test.ts +68 -0
- package/src/utils/dateParser.test.ts +20 -0
- package/src/utils/dateParser.ts +14 -7
- package/src/utils/index.ts +2 -0
- package/src/utils/plainDate.test.ts +56 -0
- package/src/utils/plainDate.ts +33 -4
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file scrollbarGutter.ts
|
|
5
|
+
* @input The element a scroll lock pins, plus live layout measurements
|
|
6
|
+
* @output Exports holdScrollbarGutter and the ScrollbarGutterHold it returns
|
|
7
|
+
* @position Internal hook utility; used by useScrollLock and MobileNav so that
|
|
8
|
+
* locking background scroll does not resize the layout viewport.
|
|
9
|
+
*
|
|
10
|
+
* Locking scroll hides the document's scrollbar. Where that scrollbar is a
|
|
11
|
+
* classic (space-taking) one — Windows/Linux desktop, and macOS set to
|
|
12
|
+
* "Always" show scroll bars — hiding it widens the layout viewport by the
|
|
13
|
+
* scrollbar's width and the whole page jumps sideways behind the overlay.
|
|
14
|
+
*
|
|
15
|
+
* The fix is `scrollbar-gutter: stable`, which holds the gutter open once the
|
|
16
|
+
* scrollbar goes away. Being a real layout change it holds `position: fixed`
|
|
17
|
+
* chrome (sticky headers, toast viewports) as well as in-flow content, which
|
|
18
|
+
* padding fundamentally cannot: fixed elements resolve against the viewport,
|
|
19
|
+
* not against the padded element.
|
|
20
|
+
*
|
|
21
|
+
* Padding is kept only as a fallback for engines without `scrollbar-gutter`
|
|
22
|
+
* (Safari shipped it in 18.2), and it is applied by measuring whether the
|
|
23
|
+
* element actually moved rather than by assuming it did.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
const NOOP: ScrollbarGutterHold = {
|
|
27
|
+
settle() {},
|
|
28
|
+
release() {},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export interface ScrollbarGutterHold {
|
|
32
|
+
/**
|
|
33
|
+
* Call once the lock's own styles are applied: measures whether the element
|
|
34
|
+
* grew anyway and, only then, pads the difference away.
|
|
35
|
+
*/
|
|
36
|
+
settle(): void;
|
|
37
|
+
/** Call on unlock: restores everything this hold changed. */
|
|
38
|
+
release(): void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Keeps `element`'s content box the width it is right now, across a scroll
|
|
43
|
+
* lock that is about to hide the document's scrollbar.
|
|
44
|
+
*
|
|
45
|
+
* Call it *before* applying the lock's styles, then {@link
|
|
46
|
+
* ScrollbarGutterHold.settle} immediately after, and {@link
|
|
47
|
+
* ScrollbarGutterHold.release} on unlock:
|
|
48
|
+
*
|
|
49
|
+
* ```
|
|
50
|
+
* const hold = holdScrollbarGutter(document.body);
|
|
51
|
+
* body.style.position = 'fixed';
|
|
52
|
+
* hold.settle();
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* Overlay scrollbars (mobile, default macOS) take no layout space, so there is
|
|
56
|
+
* nothing to hold and nothing is touched.
|
|
57
|
+
*/
|
|
58
|
+
export function holdScrollbarGutter(element: HTMLElement): ScrollbarGutterHold {
|
|
59
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
60
|
+
return NOOP;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const root = document.documentElement;
|
|
64
|
+
const viewportWidth = root.clientWidth;
|
|
65
|
+
|
|
66
|
+
// 0 means "no layout to measure" (jsdom, and any non-visual environment),
|
|
67
|
+
// not "the scrollbar is as wide as the window".
|
|
68
|
+
if (viewportWidth === 0) {
|
|
69
|
+
return NOOP;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const previousGutter = root.style.scrollbarGutter;
|
|
73
|
+
const previousPadding = element.style.paddingRight;
|
|
74
|
+
const widthBefore = element.getBoundingClientRect().width;
|
|
75
|
+
|
|
76
|
+
let heldGutter = false;
|
|
77
|
+
let padded = false;
|
|
78
|
+
let settled = false;
|
|
79
|
+
|
|
80
|
+
// Only when a space-taking scrollbar is actually there. Holding the gutter
|
|
81
|
+
// open on a page that never had one would carve out 15px that was never
|
|
82
|
+
// reserved — the same shift, in the other direction.
|
|
83
|
+
if (window.innerWidth > viewportWidth) {
|
|
84
|
+
root.style.scrollbarGutter = 'stable';
|
|
85
|
+
heldGutter = true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
settle() {
|
|
90
|
+
if (settled) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
settled = true;
|
|
94
|
+
|
|
95
|
+
// What the lock actually did to this element, rather than what we
|
|
96
|
+
// assumed it would do. 0 on any engine where the gutter held — and on a
|
|
97
|
+
// page that already sets `scrollbar-gutter: stable` itself, where the
|
|
98
|
+
// scrollbar was never taking the space back.
|
|
99
|
+
const grew = element.getBoundingClientRect().width - widthBefore;
|
|
100
|
+
if (grew <= 0) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const existing =
|
|
105
|
+
Number.parseFloat(window.getComputedStyle(element).paddingRight) || 0;
|
|
106
|
+
element.style.paddingRight = `${existing + grew}px`;
|
|
107
|
+
padded = true;
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
release() {
|
|
111
|
+
if (padded) {
|
|
112
|
+
element.style.paddingRight = previousPadding;
|
|
113
|
+
padded = false;
|
|
114
|
+
}
|
|
115
|
+
if (heldGutter) {
|
|
116
|
+
root.style.scrollbarGutter = previousGutter;
|
|
117
|
+
heldGutter = false;
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file useResolvedRequired.ts
|
|
7
|
+
* @input Uses React, FormLayoutContext
|
|
8
|
+
* @output Exports useResolvedRequired
|
|
9
|
+
* @position Resolves a field's effective aria-required against the form default
|
|
10
|
+
*
|
|
11
|
+
* SYNC: When modified, update these files to stay in sync:
|
|
12
|
+
* - /packages/core/src/FormLayout/FormLayoutContext.ts (defaultOptionality source)
|
|
13
|
+
* - /packages/core/src/FormLayout/FormLayout.tsx (prop doc: aria-required behavior)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {use} from 'react';
|
|
17
|
+
import {FormLayoutContext} from '../FormLayout/FormLayoutContext';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Resolve a field's effective *required* state for `aria-required`, honoring a
|
|
21
|
+
* surrounding `FormLayout`'s `defaultOptionality`.
|
|
22
|
+
*
|
|
23
|
+
* Under `defaultOptionality="required"` a field is required unless it opts out
|
|
24
|
+
* with `isOptional`. The visible indicator is suppressed for the unmarked
|
|
25
|
+
* majority, so those fields must still expose `aria-required` — otherwise a
|
|
26
|
+
* sighted user reads them as required (form-wide default, no indicator) while a
|
|
27
|
+
* screen reader hears "not required". This closes that mismatch.
|
|
28
|
+
*
|
|
29
|
+
* Semantics only: this drives `aria-required`, never the native `required`
|
|
30
|
+
* attribute, so a layout-level default can't silently switch on browser
|
|
31
|
+
* validation bubbles. `isOptional` takes precedence, matching `FieldLabel`.
|
|
32
|
+
*/
|
|
33
|
+
export function useResolvedRequired({
|
|
34
|
+
isRequired = false,
|
|
35
|
+
isOptional = false,
|
|
36
|
+
}: {
|
|
37
|
+
isRequired?: boolean;
|
|
38
|
+
isOptional?: boolean;
|
|
39
|
+
}): boolean {
|
|
40
|
+
const {defaultOptionality} = use(FormLayoutContext);
|
|
41
|
+
return !isOptional && (isRequired || defaultOptionality === 'required');
|
|
42
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
export const docs = {
|
|
5
5
|
name: 'useScrollLock',
|
|
6
6
|
displayName: 'useScrollLock',
|
|
7
|
-
keywords: ['scroll', 'lock', 'modal', 'dialog', 'body', 'prevent', 'background', 'ios', 'safari', 'fixed'],
|
|
7
|
+
keywords: ['scroll', 'lock', 'modal', 'dialog', 'body', 'prevent', 'background', 'ios', 'safari', 'fixed', 'scrollbar', 'gutter', 'layout shift', 'reflow'],
|
|
8
8
|
params: [
|
|
9
9
|
{
|
|
10
10
|
name: 'isLocked',
|
|
@@ -16,7 +16,7 @@ export const docs = {
|
|
|
16
16
|
returns: [],
|
|
17
17
|
usage: {
|
|
18
18
|
description:
|
|
19
|
-
'Locks body scroll when active by pinning the body with position: fixed. This prevents background scrolling behind modals and dialogs, which is necessary for iOS Safari where overscroll-behavior: contain does not work. Restores the original scroll position when unlocked.',
|
|
19
|
+
'Locks body scroll when active by pinning the body with position: fixed. This prevents background scrolling behind modals and dialogs, which is necessary for iOS Safari where overscroll-behavior: contain does not work. Restores the original scroll position when unlocked. Pinning hides the document scrollbar, so where that scrollbar takes layout space (desktop) the hook holds its gutter open with scrollbar-gutter: stable for the duration of the lock. The page, including any position: fixed chrome, does not shift sideways.',
|
|
20
20
|
bestPractices: [
|
|
21
21
|
{ guidance: true, description: 'Use when opening full-screen modals or dialogs to prevent background content from scrolling.' },
|
|
22
22
|
{ guidance: true, description: 'Pass the same boolean that controls dialog visibility (e.g., isOpen) as the isLocked parameter.' },
|
|
@@ -32,13 +32,13 @@ export const docs = {
|
|
|
32
32
|
/** @type {import('@astryxdesign/cli/authoring').HookTranslationDoc} */
|
|
33
33
|
export const docsDense = {
|
|
34
34
|
description:
|
|
35
|
-
'Locks body scroll when active by pinning body w/ position: fixed. Prevents background scrolling behind modals + dialogs, necessary for iOS Safari where overscroll-behavior: contain does not work. Restores original scroll position when unlocked.',
|
|
35
|
+
'Locks body scroll when active by pinning body w/ position: fixed. Prevents background scrolling behind modals + dialogs, necessary for iOS Safari where overscroll-behavior: contain does not work. Restores original scroll position when unlocked. Holds the hidden scrollbar\'s gutter open (scrollbar-gutter: stable) so the page (incl. position: fixed chrome) does not shift sideways.',
|
|
36
36
|
paramDescriptions: {
|
|
37
37
|
isLocked: 'whether body scroll should be locked.',
|
|
38
38
|
},
|
|
39
39
|
usage: {
|
|
40
40
|
description:
|
|
41
|
-
'Locks body scroll when active by pinning body w/ position: fixed. Prevents background scrolling behind modals + dialogs, necessary for iOS Safari where overscroll-behavior: contain does not work. Restores original scroll position when unlocked.',
|
|
41
|
+
'Locks body scroll when active by pinning body w/ position: fixed. Prevents background scrolling behind modals + dialogs, necessary for iOS Safari where overscroll-behavior: contain does not work. Restores original scroll position when unlocked. Holds the hidden scrollbar\'s gutter open (scrollbar-gutter: stable) so the page (incl. position: fixed chrome) does not shift sideways.',
|
|
42
42
|
bestPractices: [
|
|
43
43
|
{ guidance: true, description: 'Use when opening full-screen modals / dialogs to prevent background content from scrolling.' },
|
|
44
44
|
{ guidance: true, description: 'Pass same boolean that controls dialog visibility (e.g. isOpen) as isLocked parameter.' },
|
|
@@ -17,6 +17,9 @@ describe('useScrollLock', () => {
|
|
|
17
17
|
afterEach(() => {
|
|
18
18
|
cleanup();
|
|
19
19
|
document.body.style.cssText = '';
|
|
20
|
+
document.documentElement.style.cssText = '';
|
|
21
|
+
// @ts-expect-error -- drop the viewport stub so jsdom's own value comes back
|
|
22
|
+
delete document.documentElement.clientWidth;
|
|
20
23
|
vi.restoreAllMocks();
|
|
21
24
|
});
|
|
22
25
|
|
|
@@ -69,4 +72,74 @@ describe('useScrollLock', () => {
|
|
|
69
72
|
expect(document.body.style.left).toBe('');
|
|
70
73
|
expect(document.body.style.right).toBe('');
|
|
71
74
|
});
|
|
75
|
+
|
|
76
|
+
it('holds the page still across the scrollbar it hides', () => {
|
|
77
|
+
// A 1024px window over a 1009px layout viewport = a 15px classic
|
|
78
|
+
// scrollbar. Pinning the body hides it, which would widen the page by
|
|
79
|
+
// those 15px and reflow everything sideways.
|
|
80
|
+
Object.defineProperty(window, 'innerWidth', {
|
|
81
|
+
value: 1024,
|
|
82
|
+
configurable: true,
|
|
83
|
+
writable: true,
|
|
84
|
+
});
|
|
85
|
+
Object.defineProperty(document.documentElement, 'clientWidth', {
|
|
86
|
+
value: 1009,
|
|
87
|
+
configurable: true,
|
|
88
|
+
});
|
|
89
|
+
vi.spyOn(window, 'scrollTo').mockImplementation(() => {});
|
|
90
|
+
|
|
91
|
+
const lock = renderHook(() => useScrollLock(true));
|
|
92
|
+
|
|
93
|
+
expect(document.documentElement.style.scrollbarGutter).toBe('stable');
|
|
94
|
+
|
|
95
|
+
lock.unmount();
|
|
96
|
+
|
|
97
|
+
expect(document.documentElement.style.scrollbarGutter).toBe('');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('leaves the page alone when the scrollbar is an overlay one', () => {
|
|
101
|
+
Object.defineProperty(window, 'innerWidth', {
|
|
102
|
+
value: 1024,
|
|
103
|
+
configurable: true,
|
|
104
|
+
writable: true,
|
|
105
|
+
});
|
|
106
|
+
Object.defineProperty(document.documentElement, 'clientWidth', {
|
|
107
|
+
value: 1024,
|
|
108
|
+
configurable: true,
|
|
109
|
+
});
|
|
110
|
+
vi.spyOn(window, 'scrollTo').mockImplementation(() => {});
|
|
111
|
+
|
|
112
|
+
const lock = renderHook(() => useScrollLock(true));
|
|
113
|
+
|
|
114
|
+
expect(document.documentElement.style.scrollbarGutter).toBe('');
|
|
115
|
+
expect(document.body.style.paddingRight).toBe('');
|
|
116
|
+
|
|
117
|
+
lock.unmount();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('holds the gutter for the outermost overlay only, and gives it back once', () => {
|
|
121
|
+
Object.defineProperty(window, 'innerWidth', {
|
|
122
|
+
value: 1024,
|
|
123
|
+
configurable: true,
|
|
124
|
+
writable: true,
|
|
125
|
+
});
|
|
126
|
+
Object.defineProperty(document.documentElement, 'clientWidth', {
|
|
127
|
+
value: 1009,
|
|
128
|
+
configurable: true,
|
|
129
|
+
});
|
|
130
|
+
vi.spyOn(window, 'scrollTo').mockImplementation(() => {});
|
|
131
|
+
|
|
132
|
+
const first = renderHook(() => useScrollLock(true));
|
|
133
|
+
const second = renderHook(() => useScrollLock(true));
|
|
134
|
+
|
|
135
|
+
expect(document.documentElement.style.scrollbarGutter).toBe('stable');
|
|
136
|
+
|
|
137
|
+
first.unmount();
|
|
138
|
+
|
|
139
|
+
expect(document.documentElement.style.scrollbarGutter).toBe('stable');
|
|
140
|
+
|
|
141
|
+
second.unmount();
|
|
142
|
+
|
|
143
|
+
expect(document.documentElement.style.scrollbarGutter).toBe('');
|
|
144
|
+
});
|
|
72
145
|
});
|
|
@@ -13,9 +13,11 @@
|
|
|
13
13
|
*
|
|
14
14
|
* SYNC: When modified, update:
|
|
15
15
|
* - /packages/core/src/hooks/index.ts
|
|
16
|
+
* - /packages/core/src/hooks/scrollbarGutter.ts
|
|
16
17
|
*/
|
|
17
18
|
|
|
18
19
|
import {useEffect} from 'react';
|
|
20
|
+
import {holdScrollbarGutter, type ScrollbarGutterHold} from './scrollbarGutter';
|
|
19
21
|
|
|
20
22
|
interface ScrollLockSnapshot {
|
|
21
23
|
scrollX: number;
|
|
@@ -25,6 +27,7 @@ interface ScrollLockSnapshot {
|
|
|
25
27
|
top: string;
|
|
26
28
|
left: string;
|
|
27
29
|
right: string;
|
|
30
|
+
gutter: ScrollbarGutterHold;
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
let lockCount = 0;
|
|
@@ -38,6 +41,10 @@ let originalBodyState: ScrollLockSnapshot | null = null;
|
|
|
38
41
|
* does not prevent body scroll behind modals. Restores scroll position
|
|
39
42
|
* on unlock.
|
|
40
43
|
*
|
|
44
|
+
* Pinning also hides the document's scrollbar, so the gutter that scrollbar
|
|
45
|
+
* occupied is held open for the duration of the lock — without it the page
|
|
46
|
+
* reflows sideways by ~15px the moment an overlay opens.
|
|
47
|
+
*
|
|
41
48
|
* @example
|
|
42
49
|
* ```
|
|
43
50
|
* useScrollLock(isOpen);
|
|
@@ -55,6 +62,9 @@ export function useScrollLock(isLocked: boolean): void {
|
|
|
55
62
|
const scrollX = window.scrollX;
|
|
56
63
|
const scrollY = window.scrollY;
|
|
57
64
|
|
|
65
|
+
// Taken before the pinning styles below hide the scrollbar.
|
|
66
|
+
const gutter = holdScrollbarGutter(body);
|
|
67
|
+
|
|
58
68
|
originalBodyState = {
|
|
59
69
|
scrollX,
|
|
60
70
|
scrollY,
|
|
@@ -63,6 +73,7 @@ export function useScrollLock(isLocked: boolean): void {
|
|
|
63
73
|
top: body.style.top,
|
|
64
74
|
left: body.style.left,
|
|
65
75
|
right: body.style.right,
|
|
76
|
+
gutter,
|
|
66
77
|
};
|
|
67
78
|
|
|
68
79
|
body.style.overflow = 'hidden';
|
|
@@ -70,6 +81,8 @@ export function useScrollLock(isLocked: boolean): void {
|
|
|
70
81
|
body.style.top = `-${scrollY}px`;
|
|
71
82
|
body.style.left = '0';
|
|
72
83
|
body.style.right = '0';
|
|
84
|
+
|
|
85
|
+
gutter.settle();
|
|
73
86
|
}
|
|
74
87
|
|
|
75
88
|
lockCount += 1;
|
|
@@ -89,6 +102,7 @@ export function useScrollLock(isLocked: boolean): void {
|
|
|
89
102
|
body.style.top = state.top;
|
|
90
103
|
body.style.left = state.left;
|
|
91
104
|
body.style.right = state.right;
|
|
105
|
+
state.gutter.release();
|
|
92
106
|
window.scrollTo(state.scrollX, state.scrollY);
|
|
93
107
|
};
|
|
94
108
|
}, [isLocked]);
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
* - /packages/core/src/i18n/InternationalizationProvider.tsx
|
|
17
17
|
* - /packages/core/src/i18n/t.client.ts
|
|
18
18
|
* - /packages/core/src/i18n/useDirection.ts
|
|
19
|
+
* - /packages/core/src/i18n/useLocale.ts
|
|
20
|
+
* - /packages/core/src/i18n/useCollator.ts
|
|
19
21
|
* - /packages/core/src/i18n/getLocaleDirection.ts
|
|
20
22
|
* - /packages/core/src/i18n/index.ts
|
|
21
23
|
*/
|
|
@@ -23,7 +23,11 @@ import {describe, test, expect} from 'vitest';
|
|
|
23
23
|
import {render, screen} from '@testing-library/react';
|
|
24
24
|
import React, {useMemo} from 'react';
|
|
25
25
|
import {PowerSearch, usePowerSearchConfig} from '../../PowerSearch';
|
|
26
|
-
import type {
|
|
26
|
+
import type {
|
|
27
|
+
FieldDefinition,
|
|
28
|
+
PowerSearchConfig,
|
|
29
|
+
PowerSearchFilter,
|
|
30
|
+
} from '../../PowerSearch';
|
|
27
31
|
import {InternationalizationProvider} from '../InternationalizationProvider';
|
|
28
32
|
import pseudoCatalog from '../../../locales/pseudo.json';
|
|
29
33
|
|
|
@@ -112,6 +116,38 @@ describe('PowerSearch × i18n — end to end', () => {
|
|
|
112
116
|
expect(screen.getByText(/Name: contient/)).toBeInTheDocument();
|
|
113
117
|
});
|
|
114
118
|
|
|
119
|
+
test('date tokens update when the provider locale changes', () => {
|
|
120
|
+
const config: PowerSearchConfig = {
|
|
121
|
+
name: 'Dates',
|
|
122
|
+
fields: [
|
|
123
|
+
{
|
|
124
|
+
key: 'created',
|
|
125
|
+
label: 'Created',
|
|
126
|
+
defaultOperator: 'is',
|
|
127
|
+
operators: [{key: 'is', label: 'is', value: {type: 'date_absolute'}}],
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
};
|
|
131
|
+
const filters: PowerSearchFilter[] = [
|
|
132
|
+
{
|
|
133
|
+
field: 'created',
|
|
134
|
+
operator: 'is',
|
|
135
|
+
value: {type: 'date_absolute', unixSeconds: 1769342400},
|
|
136
|
+
},
|
|
137
|
+
];
|
|
138
|
+
const search = (locale: string) => (
|
|
139
|
+
<InternationalizationProvider locale={locale}>
|
|
140
|
+
<PowerSearch config={config} filters={filters} onChange={() => {}} />
|
|
141
|
+
</InternationalizationProvider>
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
const {rerender} = render(search('en-US'));
|
|
145
|
+
expect(screen.getByText('Jan 25, 2026')).toBeInTheDocument();
|
|
146
|
+
|
|
147
|
+
rerender(search('de-DE'));
|
|
148
|
+
expect(screen.getByText('25. Jan. 2026')).toBeInTheDocument();
|
|
149
|
+
});
|
|
150
|
+
|
|
115
151
|
test('resultCount ICU plural swaps under provider overrides', () => {
|
|
116
152
|
render(
|
|
117
153
|
<InternationalizationProvider
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file useCollator.test.tsx
|
|
5
|
+
* @input packages/core/src/i18n/useCollator.ts, InternationalizationProvider
|
|
6
|
+
* @output Unit tests for the internal provider-aware Intl.Collator hook
|
|
7
|
+
* @position Colocated tests; targets the default (no-provider) fallback,
|
|
8
|
+
* locale-driven comparison order, option threading, and memoization.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type {ReactNode} from 'react';
|
|
12
|
+
import {describe, expect, test} from 'vitest';
|
|
13
|
+
import {render, renderHook} from '@testing-library/react';
|
|
14
|
+
import {useCollator} from '../useCollator';
|
|
15
|
+
import {InternationalizationProvider} from '../InternationalizationProvider';
|
|
16
|
+
|
|
17
|
+
function wrapperFor(locale: string) {
|
|
18
|
+
return ({children}: {children: ReactNode}) => (
|
|
19
|
+
<InternationalizationProvider locale={locale}>
|
|
20
|
+
{children}
|
|
21
|
+
</InternationalizationProvider>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe('useCollator', () => {
|
|
26
|
+
test('compares using the en fallback when rendered without a provider', () => {
|
|
27
|
+
const {result} = renderHook(() => useCollator());
|
|
28
|
+
expect(result.current.compare('a', 'b')).toBeLessThan(0);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('orders "ä" before "z" under Swedish, and after "z" under German', () => {
|
|
32
|
+
const {result: sv} = renderHook(() => useCollator(), {
|
|
33
|
+
wrapper: wrapperFor('sv-SE'),
|
|
34
|
+
});
|
|
35
|
+
expect(['z', 'ä'].sort((a, b) => sv.current.compare(a, b))).toEqual([
|
|
36
|
+
'z',
|
|
37
|
+
'ä',
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
const {result: de} = renderHook(() => useCollator(), {
|
|
41
|
+
wrapper: wrapperFor('de-DE'),
|
|
42
|
+
});
|
|
43
|
+
expect(['z', 'ä'].sort((a, b) => de.current.compare(a, b))).toEqual([
|
|
44
|
+
'ä',
|
|
45
|
+
'z',
|
|
46
|
+
]);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('threads options through to the underlying Intl.Collator (numeric)', () => {
|
|
50
|
+
const {result} = renderHook(() => useCollator({numeric: true}));
|
|
51
|
+
expect(
|
|
52
|
+
['item2', 'item10'].sort((a, b) => result.current.compare(a, b)),
|
|
53
|
+
).toEqual(['item2', 'item10']);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('without numeric, orders "item10" before "item2" lexicographically', () => {
|
|
57
|
+
const {result} = renderHook(() => useCollator());
|
|
58
|
+
expect(
|
|
59
|
+
['item2', 'item10'].sort((a, b) => result.current.compare(a, b)),
|
|
60
|
+
).toEqual(['item10', 'item2']);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('memoizes the collator when locale and options identity are unchanged', () => {
|
|
64
|
+
const options = {numeric: true} satisfies Intl.CollatorOptions;
|
|
65
|
+
const {result, rerender} = renderHook(
|
|
66
|
+
({collatorOptions}) => useCollator(collatorOptions),
|
|
67
|
+
{
|
|
68
|
+
initialProps: {collatorOptions: options},
|
|
69
|
+
wrapper: wrapperFor('en'),
|
|
70
|
+
},
|
|
71
|
+
);
|
|
72
|
+
const first = result.current;
|
|
73
|
+
rerender({collatorOptions: options});
|
|
74
|
+
expect(result.current).toBe(first);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('rebuilds the collator when options identity changes', () => {
|
|
78
|
+
const {result, rerender} = renderHook(
|
|
79
|
+
({collatorOptions}) => useCollator(collatorOptions),
|
|
80
|
+
{
|
|
81
|
+
initialProps: {
|
|
82
|
+
collatorOptions: {numeric: true} satisfies Intl.CollatorOptions,
|
|
83
|
+
},
|
|
84
|
+
wrapper: wrapperFor('en'),
|
|
85
|
+
},
|
|
86
|
+
);
|
|
87
|
+
const first = result.current;
|
|
88
|
+
rerender({collatorOptions: {numeric: true}});
|
|
89
|
+
expect(result.current).not.toBe(first);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('re-renders with a new collator when the provider locale changes', () => {
|
|
93
|
+
let latestCollator: Intl.Collator | undefined;
|
|
94
|
+
function Probe() {
|
|
95
|
+
latestCollator = useCollator();
|
|
96
|
+
const order = ['z', 'ä'].sort((a, b) => latestCollator!.compare(a, b));
|
|
97
|
+
return <span data-testid="order">{order.join(',')}</span>;
|
|
98
|
+
}
|
|
99
|
+
const {getByTestId, rerender} = render(
|
|
100
|
+
<InternationalizationProvider locale="sv-SE">
|
|
101
|
+
<Probe />
|
|
102
|
+
</InternationalizationProvider>,
|
|
103
|
+
);
|
|
104
|
+
expect(getByTestId('order').textContent).toBe('z,ä');
|
|
105
|
+
const svCollator = latestCollator;
|
|
106
|
+
|
|
107
|
+
rerender(
|
|
108
|
+
<InternationalizationProvider locale="de-DE">
|
|
109
|
+
<Probe />
|
|
110
|
+
</InternationalizationProvider>,
|
|
111
|
+
);
|
|
112
|
+
expect(getByTestId('order').textContent).toBe('ä,z');
|
|
113
|
+
expect(latestCollator).not.toBe(svCollator);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file useLocale.test.tsx
|
|
5
|
+
* @input packages/core/src/i18n/useLocale.ts, InternationalizationProvider
|
|
6
|
+
* @output Unit tests for the internal provider-aware locale accessor
|
|
7
|
+
* @position Colocated tests; targets the default (no-provider) fallback, the
|
|
8
|
+
* provider locale, and re-rendering when the provider locale changes.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type {ReactNode} from 'react';
|
|
12
|
+
import {describe, expect, test} from 'vitest';
|
|
13
|
+
import {render, renderHook} from '@testing-library/react';
|
|
14
|
+
import {useLocale} from '../useLocale';
|
|
15
|
+
import {InternationalizationProvider} from '../InternationalizationProvider';
|
|
16
|
+
|
|
17
|
+
describe('useLocale', () => {
|
|
18
|
+
test('returns en when rendered without a provider', () => {
|
|
19
|
+
const {result} = renderHook(() => useLocale());
|
|
20
|
+
expect(result.current).toBe('en');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('returns the provider locale', () => {
|
|
24
|
+
const {result} = renderHook(() => useLocale(), {
|
|
25
|
+
wrapper: ({children}: {children: ReactNode}) => (
|
|
26
|
+
<InternationalizationProvider locale="fr">
|
|
27
|
+
{children}
|
|
28
|
+
</InternationalizationProvider>
|
|
29
|
+
),
|
|
30
|
+
});
|
|
31
|
+
expect(result.current).toBe('fr');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('re-renders with the new locale when the provider locale changes', () => {
|
|
35
|
+
function Probe() {
|
|
36
|
+
return <span data-testid="locale">{useLocale()}</span>;
|
|
37
|
+
}
|
|
38
|
+
const {getByTestId, rerender} = render(
|
|
39
|
+
<InternationalizationProvider locale="en">
|
|
40
|
+
<Probe />
|
|
41
|
+
</InternationalizationProvider>,
|
|
42
|
+
);
|
|
43
|
+
expect(getByTestId('locale').textContent).toBe('en');
|
|
44
|
+
|
|
45
|
+
rerender(
|
|
46
|
+
<InternationalizationProvider locale="ja-JP">
|
|
47
|
+
<Probe />
|
|
48
|
+
</InternationalizationProvider>,
|
|
49
|
+
);
|
|
50
|
+
expect(getByTestId('locale').textContent).toBe('ja-JP');
|
|
51
|
+
});
|
|
52
|
+
});
|
package/src/i18n/index.ts
CHANGED
|
@@ -10,12 +10,16 @@
|
|
|
10
10
|
* The public API is deliberately small:
|
|
11
11
|
* - InternationalizationProvider — provider component
|
|
12
12
|
* - useTranslator — hook returning a translator function
|
|
13
|
+
* - useLocale — hook returning the provider locale
|
|
14
|
+
* - useCollator — provider-bound locale-aware comparison
|
|
13
15
|
* - useDirection — hook returning the ambient text direction
|
|
14
16
|
* - getLocaleDirection — server-safe locale → direction helper
|
|
15
17
|
* - Translator — interface for consumer-injected runtimes
|
|
16
18
|
*
|
|
17
19
|
* SYNC: When modified, update these files to stay in sync:
|
|
18
20
|
* - /packages/core/src/index.ts
|
|
21
|
+
* - /packages/core/src/i18n/useLocale.ts
|
|
22
|
+
* - /packages/core/src/i18n/useCollator.ts
|
|
19
23
|
* - /packages/core/src/i18n/useDirection.ts
|
|
20
24
|
* - /packages/core/src/i18n/getLocaleDirection.ts
|
|
21
25
|
*/
|
|
@@ -26,6 +30,8 @@ export {InternationalizationContext} from './InternationalizationContext';
|
|
|
26
30
|
export type {InternationalizationContextValue} from './InternationalizationContext';
|
|
27
31
|
export {useTranslator} from './useTranslator';
|
|
28
32
|
export type {TranslatorFn} from './useTranslator';
|
|
33
|
+
export {useLocale} from './useLocale';
|
|
34
|
+
export {useCollator} from './useCollator';
|
|
29
35
|
export {useDirection} from './useDirection';
|
|
30
36
|
export {getLocaleDirection} from './getLocaleDirection';
|
|
31
37
|
export type {Translator} from './translator';
|