@astryxdesign/core 0.7.5 → 0.7.6-canary.00507ba
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AlertDialog/AlertDialog.d.ts +1 -1
- package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
- package/dist/AlertDialog/AlertDialog.js +5 -5
- package/dist/AppShell/AppShell.d.ts.map +1 -1
- package/dist/AppShell/AppShell.js +183 -5
- package/dist/AppShell/observeMainOverflow.d.ts +9 -0
- package/dist/AppShell/observeMainOverflow.d.ts.map +1 -0
- package/dist/AppShell/observeMainOverflow.js +125 -0
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +12 -3
- package/dist/Badge/Badge.d.ts.map +1 -1
- package/dist/Badge/Badge.js +7 -1
- package/dist/BottomSheet/BottomSheet.js +1 -1
- package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetPanel.js +2 -1
- package/dist/BottomSheet/useSheetGestures.d.ts +12 -0
- package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
- package/dist/BottomSheet/useSheetGestures.js +30 -3
- package/dist/Button/Button.js +4 -4
- package/dist/Carousel/Carousel.d.ts +3 -1
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +12 -3
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +6 -5
- package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
- package/dist/ClickableCard/ClickableCard.js +37 -4
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +12 -4
- package/dist/DateInput/MonthScroller.d.ts.map +1 -1
- package/dist/DateInput/MonthScroller.js +101 -15
- package/dist/DateInput/TouchDateField.d.ts.map +1 -1
- package/dist/DateInput/TouchDateField.js +8 -3
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +1 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts +5 -2
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +23 -12
- package/dist/DateTimeInput/NativeTimeSegment.d.ts.map +1 -1
- package/dist/DateTimeInput/NativeTimeSegment.js +5 -4
- package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
- package/dist/DateTimeInput/TouchDateTimeField.js +4 -3
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +34 -9
- package/dist/Dialog/DialogHeader.d.ts.map +1 -1
- package/dist/Dialog/DialogHeader.js +5 -4
- package/dist/Divider/Divider.d.ts.map +1 -1
- package/dist/Divider/Divider.js +2 -1
- package/dist/Drawer/Drawer.d.ts +1 -0
- package/dist/Drawer/Drawer.d.ts.map +1 -1
- package/dist/Drawer/Drawer.js +7 -2
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +7 -3
- package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts.map +1 -1
- package/dist/DropdownMenu/MenuBottomSheetActionList.js +6 -5
- package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts +20 -2
- package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts.map +1 -1
- package/dist/DropdownMenu/lazyMenuBottomSheet.js +31 -3
- package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
- package/dist/DropdownMenu/renderDropdownItems.js +21 -16
- package/dist/EmptyState/EmptyState.d.ts.map +1 -1
- package/dist/EmptyState/EmptyState.js +2 -1
- package/dist/Field/FieldLabel.d.ts +1 -1
- package/dist/Field/FieldLabel.d.ts.map +1 -1
- package/dist/Field/FieldLabel.js +4 -4
- package/dist/Field/InputClearButton.js +3 -3
- package/dist/Field/PanelSearchInput.js +2 -2
- package/dist/FieldStatus/FieldStatus.js +2 -2
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +7 -1
- package/dist/Heading/Heading.js +1 -1
- package/dist/Icon/Icon.js +1 -1
- package/dist/Indicator/CheckIndicator.d.ts.map +1 -1
- package/dist/Indicator/CheckIndicator.js +2 -1
- package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
- package/dist/Indicator/CheckboxIndicator.js +3 -1
- package/dist/Indicator/RadioIndicator.d.ts.map +1 -1
- package/dist/Indicator/RadioIndicator.js +3 -1
- package/dist/Item/Item.d.ts.map +1 -1
- package/dist/Item/Item.js +2 -1
- package/dist/Item/ItemSwipeLayer.js +1 -1
- package/dist/Layer/popoverOperations.d.ts +27 -0
- package/dist/Layer/popoverOperations.d.ts.map +1 -0
- package/dist/Layer/popoverOperations.js +159 -0
- package/dist/Layer/useLayer.d.ts +1 -1
- package/dist/Layer/useLayer.d.ts.map +1 -1
- package/dist/Layer/useLayer.js +93 -59
- package/dist/Layout/Layout.js +1 -1
- package/dist/Layout/LayoutContent.js +1 -1
- package/dist/Layout/LayoutFooter.js +1 -1
- package/dist/Layout/LayoutHeader.js +1 -1
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +5 -0
- package/dist/Lightbox/LightboxViewport.a11y.browser.spec.js +5 -4
- package/dist/List/List.js +1 -1
- package/dist/List/ListItem.js +3 -3
- package/dist/MobileNav/MobileNav.d.ts.map +1 -1
- package/dist/MobileNav/MobileNav.js +7 -2
- package/dist/MultiSelector/CreateRow.a11y.chromium.spec.js +7 -6
- package/dist/MultiSelector/MultiSelector.d.ts +1 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +26 -10
- package/dist/NumberInput/NumberInput.d.ts +3 -1
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +3 -3
- package/dist/NumberInput/numberParser.d.ts +3 -0
- package/dist/NumberInput/numberParser.d.ts.map +1 -1
- package/dist/NumberInput/numberParser.js +7 -44
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +16 -12
- package/dist/Resizable/useResizable.d.ts.map +1 -1
- package/dist/Resizable/useResizable.js +28 -2
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +4 -1
- package/dist/Selector/useSelectorPresentation.d.ts +8 -1
- package/dist/Selector/useSelectorPresentation.d.ts.map +1 -1
- package/dist/Selector/useSelectorPresentation.js +20 -4
- package/dist/Skeleton/Skeleton.d.ts.map +1 -1
- package/dist/Skeleton/Skeleton.js +1 -1
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +10 -2
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +3 -3
- package/dist/Stack/Stack.js +1 -1
- package/dist/Stack/StackItem.js +1 -1
- package/dist/TabList/TabList.d.ts.map +1 -1
- package/dist/TabList/TabList.js +16 -1
- package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
- package/dist/Table/plugins/columnResize/useTableColumnResize.js +9 -1
- package/dist/Text/Text.js +1 -1
- package/dist/Text/useTruncation.d.ts.map +1 -1
- package/dist/Text/useTruncation.js +23 -5
- package/dist/TextArea/TextArea.d.ts +1 -1
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +13 -2
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +3 -3
- package/dist/TimeInput/TimeInput.d.ts +6 -2
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +21 -14
- package/dist/TimeInput/TouchTimeField.d.ts.map +1 -1
- package/dist/TimeInput/TouchTimeField.js +5 -4
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +2 -2
- package/dist/Timestamp/TimestampHoverCard.js +4 -4
- package/dist/Toast/Toast.d.ts.map +1 -1
- package/dist/Toast/Toast.js +60 -20
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +82 -51
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +13 -2
- package/dist/Tokenizer/TokenizerBottomSheet.js +3 -3
- package/dist/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/Toolbar/Toolbar.js +2 -1
- package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenu.js +3 -0
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +1 -0
- package/dist/Typeahead/Typeahead.d.ts +1 -1
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +9 -1
- package/dist/astryx.css +21 -4
- package/dist/gestures/GestureArena.d.ts +6 -0
- package/dist/gestures/GestureArena.d.ts.map +1 -1
- package/dist/gestures/GestureArena.js +39 -0
- package/dist/gestures/GestureHost.d.ts +352 -0
- package/dist/gestures/GestureHost.d.ts.map +1 -0
- package/dist/gestures/GestureHost.js +1773 -0
- package/dist/gestures/GestureRecognizer.d.ts +10 -0
- package/dist/gestures/GestureRecognizer.d.ts.map +1 -1
- package/dist/gestures/GestureRecognizer.js +16 -0
- package/dist/gestures/LongPressGestureRecognizer.d.ts +1 -0
- package/dist/gestures/LongPressGestureRecognizer.d.ts.map +1 -1
- package/dist/gestures/LongPressGestureRecognizer.js +3 -0
- package/dist/gestures/PanGestureRecognizer.d.ts +1 -0
- package/dist/gestures/PanGestureRecognizer.d.ts.map +1 -1
- package/dist/gestures/PanGestureRecognizer.js +3 -0
- package/dist/gestures/PinchGestureRecognizer.d.ts +31 -0
- package/dist/gestures/PinchGestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/PinchGestureRecognizer.js +128 -0
- package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts +71 -0
- package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/ScreenEdgePanGestureRecognizer.js +223 -0
- package/dist/gestures/SwipeGestureRecognizer.d.ts +37 -0
- package/dist/gestures/SwipeGestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/SwipeGestureRecognizer.js +148 -0
- package/dist/gestures/TapGestureRecognizer.d.ts +1 -0
- package/dist/gestures/TapGestureRecognizer.d.ts.map +1 -1
- package/dist/gestures/TapGestureRecognizer.js +3 -0
- package/dist/gestures/VelocityTracker.d.ts +6 -2
- package/dist/gestures/VelocityTracker.d.ts.map +1 -1
- package/dist/gestures/VelocityTracker.js +7 -3
- package/dist/gestures/haptics.d.ts +65 -0
- package/dist/gestures/haptics.d.ts.map +1 -0
- package/dist/gestures/haptics.js +182 -0
- package/dist/gestures/nativeScroll.d.ts +42 -0
- package/dist/gestures/nativeScroll.d.ts.map +1 -0
- package/dist/gestures/nativeScroll.js +228 -0
- package/dist/gestures/touchAction.d.ts +60 -0
- package/dist/gestures/touchAction.d.ts.map +1 -0
- package/dist/gestures/touchAction.js +157 -0
- package/dist/gestures/touchActionRule.d.ts +34 -0
- package/dist/gestures/touchActionRule.d.ts.map +1 -0
- package/dist/gestures/touchActionRule.js +140 -0
- package/dist/gestures/types.d.ts +18 -7
- package/dist/gestures/types.d.ts.map +1 -1
- package/dist/gestures/types.js +22 -2
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/modalAnnounce.d.ts.map +1 -1
- package/dist/hooks/modalAnnounce.js +107 -61
- package/dist/hooks/scrollKeyboardDelegation.d.ts.map +1 -1
- package/dist/hooks/scrollKeyboardDelegation.js +7 -3
- package/dist/hooks/useAdaptivePresentation.d.ts.map +1 -1
- package/dist/hooks/useAdaptivePresentation.js +4 -1
- package/dist/hooks/useAnnounce.d.ts +101 -42
- package/dist/hooks/useAnnounce.d.ts.map +1 -1
- package/dist/hooks/useAnnounce.js +161 -75
- package/dist/hooks/useAnnounceRenderedText.d.ts +11 -3
- package/dist/hooks/useAnnounceRenderedText.d.ts.map +1 -1
- package/dist/hooks/useAnnounceRenderedText.js +5 -3
- package/dist/hooks/useBusyAnnouncement.d.ts.map +1 -1
- package/dist/hooks/useBusyAnnouncement.js +6 -12
- package/dist/hooks/useMenuHover.d.ts.map +1 -1
- package/dist/hooks/useMenuHover.js +12 -1
- package/dist/hooks/useScrollOverflow.d.ts +2 -1
- package/dist/hooks/useScrollOverflow.d.ts.map +1 -1
- package/dist/hooks/useScrollOverflow.js +56 -10
- package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/en.generated.js +6 -0
- package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pseudo.generated.js +6 -0
- package/dist/motion/animator.d.ts +7 -1
- package/dist/motion/animator.d.ts.map +1 -1
- package/dist/motion/animator.js +119 -27
- package/dist/theme/defineTheme.d.ts +19 -5
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/defineTheme.js +64 -4
- package/dist/theme/domainTokens/domainTokenDefaults.d.ts +3 -0
- package/dist/theme/domainTokens/domainTokenDefaults.d.ts.map +1 -0
- package/dist/theme/domainTokens/domainTokenDefaults.js +19 -0
- package/dist/theme/domainTokens/index.d.ts +5 -4
- package/dist/theme/domainTokens/index.d.ts.map +1 -1
- package/dist/theme/domainTokens/index.js +5 -9
- package/dist/theme/index.d.ts +1 -0
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/motionKnobs.d.ts +56 -0
- package/dist/theme/motionKnobs.d.ts.map +1 -0
- package/dist/theme/motionKnobs.js +48 -0
- package/dist/theme/resolveThemeValues.d.ts +11 -4
- package/dist/theme/resolveThemeValues.d.ts.map +1 -1
- package/dist/theme/resolveThemeValues.js +40 -4
- package/dist/theme/themeAdaptations.d.ts +7 -3
- package/dist/theme/themeAdaptations.d.ts.map +1 -1
- package/dist/theme/themeAdaptations.js +36 -7
- package/dist/theme/themeMotion.d.ts +96 -0
- package/dist/theme/themeMotion.d.ts.map +1 -0
- package/dist/theme/themeMotion.js +177 -0
- package/dist/theme/tokenReferences.d.ts +31 -0
- package/dist/theme/tokenReferences.d.ts.map +1 -0
- package/dist/theme/tokenReferences.js +132 -0
- package/dist/theme/tokens.d.ts +1 -0
- package/dist/theme/tokens.d.ts.map +1 -1
- package/dist/theme/tokens.js +9 -129
- package/dist/theme/tokens.stylex.d.ts +12 -0
- package/dist/theme/tokens.stylex.d.ts.map +1 -1
- package/dist/theme/tokens.stylex.js +12 -0
- package/dist/utils/focusReturn.d.ts +17 -1
- package/dist/utils/focusReturn.d.ts.map +1 -1
- package/dist/utils/focusReturn.js +11 -4
- package/dist/utils/getNodeText.d.ts +8 -0
- package/dist/utils/getNodeText.d.ts.map +1 -0
- package/dist/utils/getNodeText.js +37 -0
- package/dist/utils/interactionModality.d.ts +11 -2
- package/dist/utils/interactionModality.d.ts.map +1 -1
- package/dist/utils/interactionModality.js +58 -5
- package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
- package/dist/utils/interactionOverlay.stylex.js +17 -8
- package/dist/utils/plainDate.d.ts +2 -0
- package/dist/utils/plainDate.d.ts.map +1 -1
- package/dist/utils/plainDate.js +46 -3
- package/dist/utils/presentation.d.ts +50 -0
- package/dist/utils/presentation.d.ts.map +1 -0
- package/dist/utils/presentation.js +125 -0
- package/dist/utils/pressFeedback.d.ts.map +1 -1
- package/dist/utils/pressFeedback.js +20 -1
- package/dist/utils/primaryClick.d.ts +11 -0
- package/dist/utils/primaryClick.d.ts.map +1 -0
- package/dist/utils/primaryClick.js +25 -0
- package/dist/utils/sharedMutationObserver.d.ts +19 -11
- package/dist/utils/sharedMutationObserver.d.ts.map +1 -1
- package/dist/utils/sharedMutationObserver.js +66 -26
- package/dist/utils/timeFormat.d.ts +47 -0
- package/dist/utils/timeFormat.d.ts.map +1 -0
- package/dist/utils/timeFormat.js +240 -0
- package/dist/utils/touchPresentation.d.ts.map +1 -1
- package/dist/utils/touchPresentation.js +5 -3
- package/dist/utils/touchPresentation.stylex.d.ts +15 -0
- package/dist/utils/touchPresentation.stylex.d.ts.map +1 -0
- package/dist/utils/touchPresentation.stylex.js +33 -0
- package/dist/utils/unicodeDigits.d.ts +20 -0
- package/dist/utils/unicodeDigits.d.ts.map +1 -0
- package/dist/utils/unicodeDigits.js +70 -0
- package/locales/en.json +27 -3
- package/locales/pseudo.json +18 -0
- package/package.json +5 -4
- package/src/AlertDialog/AlertDialog.tsx +6 -5
- package/src/AlertDialog/__tests__/AlertDialog.a11y.chromium.spec.ts +18 -6
- package/src/AppShell/AppShell.doc.mjs +1 -1
- package/src/AppShell/AppShell.spec.md +9 -12
- package/src/AppShell/AppShell.test.tsx +164 -2
- package/src/AppShell/AppShell.tsx +225 -5
- package/src/AppShell/__tests__/AppShellMainKeyboard.a11y.browser.spec.ts +534 -0
- package/src/AppShell/observeMainOverflow.test.ts +173 -0
- package/src/AppShell/observeMainOverflow.ts +143 -0
- package/src/Avatar/Avatar.spec.md +22 -22
- package/src/Avatar/Avatar.tsx +18 -1
- package/src/Avatar/__tests__/AvatarForcedColors.a11y.chromium.spec.ts +126 -0
- package/src/Badge/Badge.doc.mjs +1 -1
- package/src/Badge/Badge.tsx +19 -2
- package/src/Badge/__tests__/BadgeLargerText.a11y.chromium.spec.ts +260 -0
- package/src/BottomSheet/BottomSheet.test.tsx +46 -0
- package/src/BottomSheet/BottomSheet.tsx +1 -1
- package/src/BottomSheet/BottomSheetPanel.tsx +2 -1
- package/src/BottomSheet/useSheetGestures.test.ts +52 -0
- package/src/BottomSheet/useSheetGestures.ts +37 -6
- package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.chromium.spec.ts +18 -6
- package/src/Button/Button.tsx +4 -4
- package/src/Calendar/Calendar.spec.md +87 -48
- package/src/Calendar/__tests__/Calendar.a11y.chromium.spec.ts +21 -7
- package/src/Carousel/Carousel.doc.mjs +1 -1
- package/src/Carousel/Carousel.spec.md +9 -9
- package/src/Carousel/Carousel.test.tsx +124 -3
- package/src/Carousel/Carousel.tsx +10 -2
- package/src/Carousel/__tests__/Carousel.a11y.chromium.spec.ts +21 -7
- package/src/Carousel/__tests__/Carousel.a11y.known-failures.ts +1 -16
- package/src/Chat/ChatComposerTokenElement.spec.md +45 -33
- package/src/CheckboxInput/CheckboxInput.tsx +6 -5
- package/src/ClickableCard/ClickableCard.test.tsx +96 -0
- package/src/ClickableCard/ClickableCard.tsx +48 -4
- package/src/Collapsible/Collapsible.spec.md +2 -0
- package/src/Collapsible/CollapsibleGroup.spec.md +57 -29
- package/src/Collapsible/__tests__/Collapsible.a11y.chromium.spec.ts +18 -7
- package/src/Collapsible/__tests__/CollapsibleGroupAccordion.a11y.chromium.spec.ts +18 -6
- package/src/ComplexSelector/ComplexSelector.test.tsx +37 -0
- package/src/ContextMenu/ContextMenu.test.tsx +35 -0
- package/src/DateInput/DateInput.test.tsx +115 -3
- package/src/DateInput/DateInput.tsx +29 -14
- package/src/DateInput/DateInputTouch.test.tsx +76 -0
- package/src/DateInput/MonthScroller.tsx +106 -20
- package/src/DateInput/MonthScrollerSettle.test.tsx +165 -0
- package/src/DateInput/TouchDateField.tsx +14 -3
- package/src/DateInput/__tests__/MonthScrollerSteps.interaction.browser.spec.ts +312 -0
- package/src/DateRangeInput/DateRangeInput.test.tsx +46 -3
- package/src/DateRangeInput/DateRangeInput.tsx +16 -12
- package/src/DateTimeInput/DateTimeInput.doc.mjs +10 -8
- package/src/DateTimeInput/DateTimeInput.spec.md +75 -51
- package/src/DateTimeInput/DateTimeInput.test.tsx +81 -0
- package/src/DateTimeInput/DateTimeInput.tsx +46 -24
- package/src/DateTimeInput/NativeTimeSegment.tsx +4 -6
- package/src/DateTimeInput/TouchDateTimeField.tsx +3 -7
- package/src/Dialog/Dialog.test.tsx +216 -2
- package/src/Dialog/Dialog.tsx +42 -9
- package/src/Dialog/DialogHeader.tsx +5 -4
- package/src/Dialog/__tests__/DialogSecondDocument.a11y.browser.spec.ts +202 -0
- package/src/Divider/Divider.tsx +2 -1
- package/src/Drawer/Drawer.spec.md +111 -80
- package/src/Drawer/Drawer.test.tsx +35 -0
- package/src/Drawer/Drawer.tsx +13 -2
- package/src/Drawer/DrawerHeader.spec.md +181 -0
- package/src/Drawer/__tests__/DrawerNonModalPageScroll.a11y.chromium.spec.ts +267 -0
- package/src/DropdownMenu/DropdownMenu.test.tsx +41 -0
- package/src/DropdownMenu/DropdownMenu.tsx +10 -3
- package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +34 -0
- package/src/DropdownMenu/MenuBottomSheetActionList.tsx +6 -5
- package/src/DropdownMenu/__tests__/Menu.a11y.chromium.spec.ts +19 -7
- package/src/DropdownMenu/__tests__/MenuButton.a11y.chromium.spec.ts +21 -9
- package/src/DropdownMenu/lazyMenuBottomSheet.test.ts +60 -0
- package/src/DropdownMenu/lazyMenuBottomSheet.ts +36 -7
- package/src/DropdownMenu/renderDropdownItems.tsx +24 -19
- package/src/EmptyState/EmptyState.tsx +2 -1
- package/src/Field/FieldLabel.tsx +5 -4
- package/src/Field/InputClearButton.tsx +3 -3
- package/src/Field/PanelSearchInput.tsx +2 -2
- package/src/FieldStatus/FieldStatus.tsx +2 -2
- package/src/FileInput/FileInput.test.tsx +21 -0
- package/src/FileInput/FileInput.tsx +4 -1
- package/src/FormLayout/FormLayout.spec.md +7 -8
- package/src/Heading/Heading.tsx +1 -1
- package/src/Icon/Icon.tsx +1 -1
- package/src/Indicator/CheckIndicator.spec.md +47 -42
- package/src/Indicator/CheckIndicator.tsx +2 -1
- package/src/Indicator/CheckboxIndicator.spec.md +40 -37
- package/src/Indicator/CheckboxIndicator.tsx +3 -1
- package/src/Indicator/Indicator.spec.md +188 -0
- package/src/Indicator/RadioIndicator.tsx +3 -1
- package/src/Item/Item.tsx +2 -1
- package/src/Item/ItemSwipeLayer.tsx +1 -1
- package/src/Kbd/Kbd.spec.md +81 -38
- package/src/Layer/__tests__/LayerBrowserHide.a11y.chromium.spec.ts +126 -0
- package/src/Layer/popoverOperations.test.ts +205 -0
- package/src/Layer/popoverOperations.ts +172 -0
- package/src/Layer/useLayer.test.tsx +348 -1
- package/src/Layer/useLayer.tsx +118 -71
- package/src/Layout/Layout.spec.md +127 -131
- package/src/Layout/Layout.tsx +1 -1
- package/src/Layout/LayoutContent.spec.md +205 -0
- package/src/Layout/LayoutContent.tsx +1 -1
- package/src/Layout/LayoutFooter.spec.md +190 -0
- package/src/Layout/LayoutFooter.tsx +1 -1
- package/src/Layout/LayoutHeader.spec.md +193 -0
- package/src/Layout/LayoutHeader.tsx +1 -1
- package/src/Layout/LayoutPanel.spec.md +194 -0
- package/src/Layout/__tests__/Layout.a11y.chromium.spec.ts +18 -6
- package/src/Lightbox/Lightbox.test.tsx +18 -0
- package/src/Lightbox/Lightbox.tsx +5 -0
- package/src/Lightbox/LightboxViewport.a11y.browser.spec.ts +8 -4
- package/src/Link/__tests__/Link.a11y.chromium.spec.ts +18 -8
- package/src/List/List.tsx +1 -1
- package/src/List/ListItem.spec.md +205 -0
- package/src/List/ListItem.tsx +3 -3
- package/src/MetadataList/MetadataList.spec.md +187 -0
- package/src/MetadataList/MetadataListItem.spec.md +165 -0
- package/src/MobileNav/MobileNav.test.tsx +15 -0
- package/src/MobileNav/MobileNav.tsx +9 -1
- package/src/MobileNav/__tests__/MobileNavHeader.a11y.chromium.spec.ts +89 -0
- package/src/MoreMenu/MoreMenu.test.tsx +11 -0
- package/src/MultiSelector/CreateRow.a11y.chromium.spec.ts +7 -8
- package/src/MultiSelector/MultiSelector.spec.md +7 -4
- package/src/MultiSelector/MultiSelector.test.tsx +270 -1
- package/src/MultiSelector/MultiSelector.tsx +25 -10
- package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +8 -1
- package/src/NavMenu/NavHeadingMenu.spec.md +1 -5
- package/src/NumberInput/NumberInput.doc.mjs +13 -7
- package/src/NumberInput/NumberInput.spec.md +59 -37
- package/src/NumberInput/NumberInput.tsx +13 -3
- package/src/NumberInput/__tests__/NumberInput.coarse-pointer.a11y.chromium.spec.ts +102 -0
- package/src/NumberInput/numberParser.ts +7 -45
- package/src/ProgressBar/ProgressBar.test.tsx +108 -32
- package/src/ProgressBar/ProgressBar.tsx +58 -10
- package/src/ProgressBar/__tests__/ProgressBar.a11y.chromium.spec.ts +21 -9
- package/src/ProgressBar/__tests__/ProgressBarMotion.a11y.chromium.spec.ts +831 -0
- package/src/Resizable/__tests__/ResizeHandle.a11y.chromium.spec.ts +21 -14
- package/src/Resizable/useResizable.test.ts +43 -0
- package/src/Resizable/useResizable.ts +32 -9
- package/src/SegmentedControl/SegmentedControl.test.tsx +84 -0
- package/src/SegmentedControl/SegmentedControlItem.tsx +11 -5
- package/src/Selector/Selector.test.tsx +90 -1
- package/src/Selector/__tests__/Selector.combobox.a11y.test.tsx +8 -1
- package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +8 -1
- package/src/Selector/useSelectorPresentation.ts +31 -3
- package/src/SideNav/SideNav.test.tsx +27 -0
- package/src/SideNav/__tests__/SideNavRailMotion.interaction.browser.spec.ts +176 -51
- package/src/Skeleton/Skeleton.tsx +3 -1
- package/src/Slider/Slider.test.tsx +31 -0
- package/src/Slider/Slider.tsx +8 -3
- package/src/Slider/__tests__/Slider.a11y.chromium.spec.ts +19 -7
- package/src/Spinner/Spinner.tsx +5 -3
- package/src/Stack/Stack.tsx +1 -1
- package/src/Stack/StackItem.tsx +1 -1
- package/src/TabList/TabList.test.tsx +58 -4
- package/src/TabList/TabList.tsx +16 -1
- package/src/TabList/__tests__/TabPressPointer.interaction.browser.spec.ts +163 -0
- package/src/Table/__tests__/Table.a11y.chromium.spec.ts +18 -6
- package/src/Table/plugins/columnResize/useTableColumnResize.test.tsx +22 -0
- package/src/Table/plugins/columnResize/useTableColumnResize.tsx +8 -4
- package/src/Text/Text.tsx +1 -1
- package/src/Text/useTruncation.test.tsx +128 -1
- package/src/Text/useTruncation.ts +23 -5
- package/src/TextArea/TextArea.spec.md +10 -13
- package/src/TextArea/TextArea.test.tsx +60 -0
- package/src/TextArea/TextArea.tsx +15 -1
- package/src/TextInput/TextInput.test.tsx +21 -0
- package/src/TextInput/TextInput.tsx +3 -1
- package/src/TimeInput/NativeTimeInput.test.tsx +17 -0
- package/src/TimeInput/Presentation.test.tsx +21 -0
- package/src/TimeInput/TimeInput.doc.mjs +8 -8
- package/src/TimeInput/TimeInput.spec.md +45 -48
- package/src/TimeInput/TimeInput.test.tsx +76 -0
- package/src/TimeInput/TimeInput.tsx +36 -18
- package/src/TimeInput/TouchTimeField.tsx +4 -6
- package/src/Timestamp/Timestamp.spec.md +6 -12
- package/src/Timestamp/Timestamp.test.tsx +30 -5
- package/src/Timestamp/Timestamp.tsx +6 -2
- package/src/Timestamp/TimestampHoverCard.tsx +4 -4
- package/src/Toast/Toast.spec.md +14 -20
- package/src/Toast/Toast.tsx +104 -36
- package/src/Toast/ToastViewport.test.tsx +719 -1
- package/src/Toast/ToastViewport.tsx +100 -50
- package/src/Tokenizer/Tokenizer.spec.md +5 -3
- package/src/Tokenizer/Tokenizer.test.tsx +68 -0
- package/src/Tokenizer/Tokenizer.tsx +14 -2
- package/src/Tokenizer/TokenizerBottomSheet.test.tsx +15 -4
- package/src/Tokenizer/TokenizerBottomSheet.tsx +3 -3
- package/src/Toolbar/Toolbar.spec.md +5 -6
- package/src/Toolbar/Toolbar.test.tsx +30 -0
- package/src/Toolbar/Toolbar.tsx +2 -1
- package/src/Toolbar/__tests__/Toolbar.a11y.chromium.spec.ts +18 -6
- package/src/Tooltip/__tests__/Tooltip.a11y.chromium.spec.ts +18 -7
- package/src/TopNav/TopNav.test.tsx +27 -0
- package/src/TopNav/TopNavMegaMenu.test.tsx +21 -0
- package/src/TopNav/TopNavMegaMenu.tsx +4 -1
- package/src/TopNav/TopNavMenu.test.tsx +17 -0
- package/src/TopNav/TopNavMenu.tsx +1 -0
- package/src/Typeahead/Typeahead.test.tsx +40 -0
- package/src/Typeahead/Typeahead.tsx +12 -1
- package/src/__tests__/pressState.ts +8 -4
- package/src/gestures/GestureArena.ts +35 -0
- package/src/gestures/GestureHost.ts +2065 -0
- package/src/gestures/GestureRecognizer.ts +18 -0
- package/src/gestures/LongPressGestureRecognizer.ts +4 -0
- package/src/gestures/PanGestureRecognizer.ts +4 -0
- package/src/gestures/PinchGestureRecognizer.ts +148 -0
- package/src/gestures/ScreenEdgePanGestureRecognizer.ts +256 -0
- package/src/gestures/SwipeGestureRecognizer.ts +197 -0
- package/src/gestures/TapGestureRecognizer.ts +4 -0
- package/src/gestures/VelocityTracker.ts +10 -3
- package/src/gestures/__tests__/GestureHost.test.ts +2087 -0
- package/src/gestures/__tests__/PanGestureRecognizer.test.ts +4 -4
- package/src/gestures/__tests__/PinchGestureRecognizer.test.ts +74 -0
- package/src/gestures/__tests__/ScreenEdgePanGestureRecognizer.test.ts +498 -0
- package/src/gestures/__tests__/SwipeGestureRecognizer.test.ts +108 -0
- package/src/gestures/__tests__/arena.test.ts +5 -0
- package/src/gestures/__tests__/arenaPinchSwipeEdge.test.ts +193 -0
- package/src/gestures/__tests__/browser/build-fixture.mjs +46 -0
- package/src/gestures/__tests__/browser/fixture.ts +307 -0
- package/src/gestures/__tests__/browser/gestures.interaction.browser.spec.ts +1137 -0
- package/src/gestures/__tests__/browserNegotiation.test.ts +293 -0
- package/src/gestures/__tests__/haptics.test.ts +187 -0
- package/src/gestures/__tests__/harness.ts +21 -2
- package/src/gestures/__tests__/recognizerKind.test.ts +78 -0
- package/src/gestures/__tests__/touchActionRule.test.ts +54 -0
- package/src/gestures/haptics.ts +203 -0
- package/src/gestures/nativeScroll.ts +277 -0
- package/src/gestures/touchAction.ts +191 -0
- package/src/gestures/touchActionRule.ts +157 -0
- package/src/gestures/types.ts +30 -9
- package/src/hooks/index.ts +5 -1
- package/src/hooks/modalAnnounce.ts +129 -72
- package/src/hooks/scrollKeyboardDelegation.test.ts +33 -0
- package/src/hooks/scrollKeyboardDelegation.ts +9 -3
- package/src/hooks/useAdaptivePresentation.ts +4 -1
- package/src/hooks/useAnnounce.doc.mjs +35 -3
- package/src/hooks/useAnnounce.replace.test.tsx +516 -0
- package/src/hooks/useAnnounce.ts +209 -102
- package/src/hooks/useAnnounceRenderedText.adopters.test.tsx +10 -1
- package/src/hooks/useAnnounceRenderedText.ts +17 -3
- package/src/hooks/useBusyAnnouncement.ts +7 -10
- package/src/hooks/useMenuHover.ts +15 -1
- package/src/hooks/useScrollOverflow.doc.mjs +1 -1
- package/src/hooks/useScrollOverflow.test.tsx +102 -1
- package/src/hooks/useScrollOverflow.ts +59 -10
- package/src/i18n/generated-locales/en.generated.ts +7 -0
- package/src/i18n/generated-locales/pseudo.generated.ts +7 -0
- package/src/motion/README.md +11 -1
- package/src/motion/animator.test.ts +151 -3
- package/src/motion/animator.ts +149 -33
- package/src/theme/Theme.doc.mjs +1 -1
- package/src/theme/defineTheme.ts +117 -8
- package/src/theme/derivedVarRegistry.test.ts +4 -0
- package/src/theme/domainTokens/domainTokenDefaults.ts +19 -0
- package/src/theme/domainTokens/index.ts +6 -10
- package/src/theme/expandMotionScale.test.ts +1 -1
- package/src/theme/index.ts +5 -0
- package/src/theme/motionKnobs.ts +82 -0
- package/src/theme/resolveThemeValues.ts +68 -6
- package/src/theme/themeAdaptations.ts +52 -12
- package/src/theme/themeMotion.test.ts +620 -0
- package/src/theme/themeMotion.ts +221 -0
- package/src/theme/tokenReferences.ts +149 -0
- package/src/theme/tokens.stylex.ts +10 -0
- package/src/theme/tokens.ts +15 -158
- package/src/utils/focusReturn.ts +17 -6
- package/src/utils/getNodeText.test.ts +24 -0
- package/src/utils/getNodeText.ts +37 -0
- package/src/utils/interactionModality.test.ts +45 -1
- package/src/utils/interactionModality.ts +64 -5
- package/src/utils/interactionOverlay.stylex.ts +12 -3
- package/src/utils/plainDate.test.ts +90 -0
- package/src/utils/plainDate.ts +58 -3
- package/src/utils/presentation.test.ts +380 -0
- package/src/utils/presentation.ts +180 -0
- package/src/utils/pressFeedback.test.ts +138 -0
- package/src/utils/pressFeedback.ts +34 -11
- package/src/utils/pressPaintPriority.test.ts +80 -0
- package/src/utils/primaryClick.test.ts +38 -0
- package/src/utils/primaryClick.ts +28 -0
- package/src/utils/sharedMutationObserver.test.ts +54 -1
- package/src/utils/sharedMutationObserver.ts +87 -29
- package/src/utils/timeFormat.test.ts +204 -0
- package/src/utils/timeFormat.ts +314 -0
- package/src/utils/touchPresentation.stylex.ts +43 -0
- package/src/utils/touchPresentation.test.ts +140 -1
- package/src/utils/touchPresentation.ts +5 -3
- package/src/utils/unicodeDigits.test.ts +44 -0
- package/src/utils/unicodeDigits.ts +71 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file timeFormat.test.ts
|
|
5
|
+
* @input Uses vitest and the timeFormat helpers
|
|
6
|
+
* @output Unit tests for locale hour cycles, locale time text, and parsing it back
|
|
7
|
+
* @position Testing; validates utils/timeFormat.ts
|
|
8
|
+
*
|
|
9
|
+
* SYNC: When timeFormat.ts changes, update tests to match new behavior
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import {describe, expect, it} from 'vitest';
|
|
13
|
+
import {
|
|
14
|
+
formatLocaleTime,
|
|
15
|
+
parseLocaleTime,
|
|
16
|
+
resolveHourFormat,
|
|
17
|
+
type HourFormat,
|
|
18
|
+
} from './timeFormat';
|
|
19
|
+
import {parseTimeInput, type ISOTimeString} from './timeParser';
|
|
20
|
+
|
|
21
|
+
const iso = (time: string) => time as ISOTimeString;
|
|
22
|
+
|
|
23
|
+
describe('resolveHourFormat', () => {
|
|
24
|
+
it.each([
|
|
25
|
+
['en', '12h'],
|
|
26
|
+
['en-US', '12h'],
|
|
27
|
+
['ko-KR', '12h'],
|
|
28
|
+
['en-GB', '24h'],
|
|
29
|
+
['de-DE', '24h'],
|
|
30
|
+
['vi-VN', '24h'],
|
|
31
|
+
['fr-FR', '24h'],
|
|
32
|
+
] as const)('reads the hour cycle of %s as %s', (locale, expected) => {
|
|
33
|
+
expect(resolveHourFormat(locale)).toBe(expected);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('honors a -u-hc- preference carried on the locale tag', () => {
|
|
37
|
+
expect(resolveHourFormat('en-US-u-hc-h23')).toBe('24h');
|
|
38
|
+
expect(resolveHourFormat('de-DE-u-hc-h12')).toBe('12h');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('lets an explicit hour format win over the locale', () => {
|
|
42
|
+
expect(resolveHourFormat('de-DE', '12h')).toBe('12h');
|
|
43
|
+
expect(resolveHourFormat('en-US', '24h')).toBe('24h');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('falls back to twelve-hour for a malformed tag', () => {
|
|
47
|
+
expect(resolveHourFormat('not a locale')).toBe('12h');
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('formatLocaleTime', () => {
|
|
52
|
+
it('keeps the released English text exactly, with a plain space', () => {
|
|
53
|
+
expect(formatLocaleTime(iso('14:30'), 'en', '12h')).toBe('2:30 PM');
|
|
54
|
+
expect(formatLocaleTime(iso('00:05'), 'en', '12h')).toBe('12:05 AM');
|
|
55
|
+
expect(formatLocaleTime(iso('14:30:05'), 'en', '12h', true)).toBe(
|
|
56
|
+
'2:30:05 PM',
|
|
57
|
+
);
|
|
58
|
+
expect(formatLocaleTime(iso('09:05'), 'en', '24h')).toBe('09:05');
|
|
59
|
+
expect(formatLocaleTime(iso('09:05:07'), 'en', '24h', true)).toBe(
|
|
60
|
+
'09:05:07',
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("uses the locale's day-period words and their position", () => {
|
|
65
|
+
expect(formatLocaleTime(iso('14:30'), 'ko-KR', '12h')).toBe('오후 2:30');
|
|
66
|
+
expect(formatLocaleTime(iso('09:05'), 'zh-CN', '12h')).toBe('上午9:05');
|
|
67
|
+
expect(formatLocaleTime(iso('14:30'), 'es-ES', '12h')).toBe('2:30 p. m.');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('writes noon as 12 in a twelve-hour reading, not 0', () => {
|
|
71
|
+
expect(formatLocaleTime(iso('12:30'), 'ja-JP', '12h')).toBe('午後12:30');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('writes English twelve-hour text when the locale would drop the day period', () => {
|
|
75
|
+
// fr-CM's twelve-hour reading writes 02:30 and 14:30 both as "2:30".
|
|
76
|
+
expect(formatLocaleTime(iso('14:30'), 'fr-CM', '12h')).toBe('2:30 PM');
|
|
77
|
+
expect(formatLocaleTime(iso('02:30'), 'fr-CM', '12h')).toBe('2:30 AM');
|
|
78
|
+
expect(formatLocaleTime(iso('14:30'), 'fr-CM', '24h')).toBe('14:30');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("uses the locale's separator and digits", () => {
|
|
82
|
+
expect(formatLocaleTime(iso('14:30'), 'fi-FI', '24h')).toBe('14.30');
|
|
83
|
+
expect(formatLocaleTime(iso('14:30'), 'ar-EG', '12h')).toBe('٢:٣٠ م');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('returns empty text for an empty or malformed time', () => {
|
|
87
|
+
expect(formatLocaleTime(undefined, 'en', '12h')).toBe('');
|
|
88
|
+
expect(formatLocaleTime(iso('25:00'), 'en', '12h')).toBe('');
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe('parseLocaleTime', () => {
|
|
93
|
+
const LOCALES = [
|
|
94
|
+
'en-US',
|
|
95
|
+
'en-GB',
|
|
96
|
+
'de-DE',
|
|
97
|
+
'fi-FI',
|
|
98
|
+
'fr-CA',
|
|
99
|
+
'es-ES',
|
|
100
|
+
'sv-SE',
|
|
101
|
+
'vi-VN',
|
|
102
|
+
'ja-JP',
|
|
103
|
+
'ko-KR',
|
|
104
|
+
'zh-TW',
|
|
105
|
+
'ar-EG',
|
|
106
|
+
'fa-IR',
|
|
107
|
+
'he-IL',
|
|
108
|
+
// Twelve-hour readings whose separators or surrounding words differ from
|
|
109
|
+
// the twenty-four-hour reading: a dot (as, nds), "h" (oc), a word between
|
|
110
|
+
// the day period and the hour (brx, ee), a word before the hour (dz, nds,
|
|
111
|
+
// tok), and Tibetan digits (dz).
|
|
112
|
+
'as',
|
|
113
|
+
'brx',
|
|
114
|
+
'dz',
|
|
115
|
+
'ee',
|
|
116
|
+
'nds',
|
|
117
|
+
'oc',
|
|
118
|
+
'tok',
|
|
119
|
+
// A one-letter day period that English input contains ("m" in "pm").
|
|
120
|
+
'gd',
|
|
121
|
+
// A twelve-hour reading with no day period at all.
|
|
122
|
+
'fr-CM',
|
|
123
|
+
];
|
|
124
|
+
const TIMES = ['00:00', '00:30', '09:05', '11:59', '12:00', '12:30', '23:45'];
|
|
125
|
+
const FORMATS: HourFormat[] = ['12h', '24h'];
|
|
126
|
+
|
|
127
|
+
it.each(LOCALES)('reads back every time %s writes', locale => {
|
|
128
|
+
for (const hourFormat of FORMATS) {
|
|
129
|
+
for (const time of TIMES) {
|
|
130
|
+
const text = formatLocaleTime(iso(time), locale, hourFormat);
|
|
131
|
+
expect(
|
|
132
|
+
parseLocaleTime(text, locale),
|
|
133
|
+
`${locale} ${hourFormat} "${text}"`,
|
|
134
|
+
).toBe(time);
|
|
135
|
+
}
|
|
136
|
+
const withSeconds = formatLocaleTime(
|
|
137
|
+
iso('14:30:05'),
|
|
138
|
+
locale,
|
|
139
|
+
hourFormat,
|
|
140
|
+
true,
|
|
141
|
+
);
|
|
142
|
+
expect(parseLocaleTime(withSeconds, locale, true)).toBe('14:30:05');
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('reads a spaced day period as the half of the day it names', () => {
|
|
147
|
+
// Previously parsed as 02:30: the English matcher missed "p. m.".
|
|
148
|
+
expect(parseLocaleTime('2:30 p. m.', 'es-ES')).toBe('14:30');
|
|
149
|
+
expect(parseLocaleTime('午後2:30', 'ja-JP')).toBe('14:30');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('reads twelve-hour text whose pattern differs from the 24-hour one', () => {
|
|
153
|
+
expect(parseLocaleTime('অপৰাহ্ন ২.৩০', 'as')).toBe('14:30');
|
|
154
|
+
expect(parseLocaleTime('ɣetrɔ ga 2:30', 'ee')).toBe('14:30');
|
|
155
|
+
expect(parseLocaleTime('Klock 2.30:05 PM', 'nds', true)).toBe('14:30:05');
|
|
156
|
+
expect(parseLocaleTime('2h30:05 PM', 'oc', true)).toBe('14:30:05');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("reads the locale's separator", () => {
|
|
160
|
+
// Previously "14.30" read only its "14" prefix.
|
|
161
|
+
expect(parseLocaleTime('14.30', 'fi-FI')).toBe('14:30');
|
|
162
|
+
expect(parseLocaleTime('14 h 30', 'fr-CA')).toBe('14:30');
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('does not read a day-period word out of the middle of an English word', () => {
|
|
166
|
+
// gd writes the morning as "m": the "m" of "pm" is not that word.
|
|
167
|
+
expect(parseLocaleTime('2:30 PM', 'gd')).toBe('14:30');
|
|
168
|
+
expect(parseLocaleTime('12:00 PM', 'gd')).toBe('12:00');
|
|
169
|
+
expect(parseLocaleTime('2pm', 'gd')).toBe('14:00');
|
|
170
|
+
expect(parseLocaleTime('2:30 p.m', 'gd')).toBe('14:30');
|
|
171
|
+
expect(parseLocaleTime('12:00 p.m', 'gd')).toBe('12:00');
|
|
172
|
+
expect(parseLocaleTime('2 p.m', 'gd')).toBe('14:00');
|
|
173
|
+
expect(parseLocaleTime('11 a.m', 'gd')).toBe('11:00');
|
|
174
|
+
expect(parseLocaleTime('2:30m', 'gd')).toBe('02:30');
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('still reads English input in every locale exactly as before', () => {
|
|
178
|
+
const inputs = [
|
|
179
|
+
'2:30 PM',
|
|
180
|
+
'2:30 p.m',
|
|
181
|
+
'2:30 p.m.',
|
|
182
|
+
'12:00 PM',
|
|
183
|
+
'12:00 AM',
|
|
184
|
+
'2pm',
|
|
185
|
+
'2 p.m',
|
|
186
|
+
'11 a.m',
|
|
187
|
+
'14:30',
|
|
188
|
+
'1430',
|
|
189
|
+
];
|
|
190
|
+
for (const locale of LOCALES) {
|
|
191
|
+
for (const input of inputs) {
|
|
192
|
+
expect(parseLocaleTime(input, locale), `${locale} "${input}"`).toBe(
|
|
193
|
+
parseTimeInput(input),
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('rejects text no reading fits', () => {
|
|
200
|
+
expect(parseLocaleTime('', 'en-US')).toBeNull();
|
|
201
|
+
expect(parseLocaleTime('25:99', 'de-DE')).toBeNull();
|
|
202
|
+
expect(parseLocaleTime('午後14:30', 'ja-JP')).toBeNull();
|
|
203
|
+
});
|
|
204
|
+
});
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file timeFormat.ts
|
|
5
|
+
* @input An ISO time or typed text, plus the provider's BCP 47 locale
|
|
6
|
+
* @output Exports resolveHourFormat, formatLocaleTime, and parseLocaleTime
|
|
7
|
+
* @position Internal shared helper for TimeInput and DateTimeInput; not part
|
|
8
|
+
* of the public surface
|
|
9
|
+
*
|
|
10
|
+
* Time of day as the locale writes it. The hour cycle, digits, separator, and
|
|
11
|
+
* day-period words all come from Intl rather than a table, so a field reads
|
|
12
|
+
* the same way `Timestamp` does beside it.
|
|
13
|
+
*
|
|
14
|
+
* The hour cycle is the locale's own: `en-US` and `ko-KR` read twelve-hour,
|
|
15
|
+
* `en-GB`, `de-DE`, and `vi-VN` read twenty-four-hour. A `-u-hc-` extension
|
|
16
|
+
* on the provider locale (`en-US-u-hc-h23`) carries an app's per-user
|
|
17
|
+
* preference through the same path, so every Astryx time honors it.
|
|
18
|
+
*
|
|
19
|
+
* The parser reads back what the formatter writes: the locale's digits, its
|
|
20
|
+
* hour-minute separator, and its day-period words, on either side of the time.
|
|
21
|
+
* Whatever is left goes through the shared English-tolerant parser, so "2:30
|
|
22
|
+
* PM" and "14:30" keep working in every locale.
|
|
23
|
+
*
|
|
24
|
+
* SYNC: When modified, update these files to stay in sync:
|
|
25
|
+
* - /packages/core/src/utils/timeFormat.test.ts
|
|
26
|
+
* - /packages/core/src/TimeInput/TimeInput.tsx
|
|
27
|
+
* - /packages/core/src/DateTimeInput/DateTimeInput.tsx
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import type {Locale} from '../i18n/types';
|
|
31
|
+
import {parseISOTime, parseTimeInput, type ISOTimeString} from './timeParser';
|
|
32
|
+
import {INVISIBLE_CHARS, toAsciiDigits} from './unicodeDigits';
|
|
33
|
+
|
|
34
|
+
export type HourFormat = '12h' | '24h';
|
|
35
|
+
|
|
36
|
+
interface LocaleTimeSymbols {
|
|
37
|
+
hourFormat: HourFormat;
|
|
38
|
+
/**
|
|
39
|
+
* The text between hour and minute and between minute and second, trimmed:
|
|
40
|
+
* ":" nearly everywhere, "." in fi-FI, "h" and "min" in fr-CA. Read from
|
|
41
|
+
* both readings, because some locales write them differently: `as` writes
|
|
42
|
+
* "১৪:৩০" but "অপৰাহ্ন ২.৩০".
|
|
43
|
+
*/
|
|
44
|
+
separators: string[];
|
|
45
|
+
/**
|
|
46
|
+
* Words the locale writes before or after the digits other than the day
|
|
47
|
+
* period, trimmed: "s" after fr-CA seconds, "kl." before nds-DE times, "ga"
|
|
48
|
+
* between an ee day period and its hour. Longest first.
|
|
49
|
+
*/
|
|
50
|
+
edgeWords: string[];
|
|
51
|
+
/** Every day-period word the locale writes, longest first. */
|
|
52
|
+
dayPeriods: {word: string; isPM: boolean}[];
|
|
53
|
+
/**
|
|
54
|
+
* The locale's twelve-hour reading writes 02:00 and 14:00 the same (fr-CM
|
|
55
|
+
* writes no day period), so its twelve-hour text is English instead.
|
|
56
|
+
*/
|
|
57
|
+
twelveHourIsAmbiguous: boolean;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const symbolsByLocale = new Map<string, LocaleTimeSymbols>();
|
|
61
|
+
|
|
62
|
+
// The space family Intl writes between a time and its day period ("2:30 PM"
|
|
63
|
+
// uses U+202F). A typed field shows a plain space so people can edit it.
|
|
64
|
+
const SPACES = /[\u00A0\u2007\u202F]/g;
|
|
65
|
+
// What may sit on a peeled word's inner side: a digit (digits are already
|
|
66
|
+
// ASCII here), whitespace, or nothing.
|
|
67
|
+
const WORD_BOUNDARY = /^[\d\s]?$/;
|
|
68
|
+
|
|
69
|
+
function utcDate(hour: number, minute: number, second: number): Date {
|
|
70
|
+
return new Date(Date.UTC(2000, 0, 1, hour, minute, second));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function timeFormatter(
|
|
74
|
+
locale: Locale,
|
|
75
|
+
hourFormat: HourFormat,
|
|
76
|
+
includeSeconds: boolean,
|
|
77
|
+
): Intl.DateTimeFormat {
|
|
78
|
+
const options: Intl.DateTimeFormatOptions = {
|
|
79
|
+
hour: hourFormat === '12h' ? 'numeric' : '2-digit',
|
|
80
|
+
minute: '2-digit',
|
|
81
|
+
second: includeSeconds ? '2-digit' : undefined,
|
|
82
|
+
// `hourCycle`, not `hour12`: `hour12: true` resolves to h11 in ja-JP and
|
|
83
|
+
// writes noon as "午後0:30".
|
|
84
|
+
hourCycle: hourFormat === '12h' ? 'h12' : 'h23',
|
|
85
|
+
timeZone: 'UTC',
|
|
86
|
+
};
|
|
87
|
+
try {
|
|
88
|
+
return new Intl.DateTimeFormat(locale, options);
|
|
89
|
+
} catch {
|
|
90
|
+
return new Intl.DateTimeFormat('en', options);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function getLocaleTimeSymbols(locale: Locale): LocaleTimeSymbols {
|
|
95
|
+
let symbols = symbolsByLocale.get(locale);
|
|
96
|
+
if (symbols !== undefined) {
|
|
97
|
+
return symbols;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
let hourFormat: HourFormat;
|
|
101
|
+
let resolved: Intl.ResolvedDateTimeFormatOptions;
|
|
102
|
+
try {
|
|
103
|
+
resolved = new Intl.DateTimeFormat(locale, {
|
|
104
|
+
hour: 'numeric',
|
|
105
|
+
}).resolvedOptions();
|
|
106
|
+
} catch {
|
|
107
|
+
// A malformed tag formats as English everywhere else in this module.
|
|
108
|
+
resolved = new Intl.DateTimeFormat('en', {
|
|
109
|
+
hour: 'numeric',
|
|
110
|
+
}).resolvedOptions();
|
|
111
|
+
}
|
|
112
|
+
if (resolved.hourCycle !== undefined) {
|
|
113
|
+
hourFormat =
|
|
114
|
+
resolved.hourCycle === 'h11' || resolved.hourCycle === 'h12'
|
|
115
|
+
? '12h'
|
|
116
|
+
: '24h';
|
|
117
|
+
} else {
|
|
118
|
+
// Engines that leave hourCycle unresolved still write a day period for a
|
|
119
|
+
// twelve-hour locale when no cycle is forced.
|
|
120
|
+
let ambient: Intl.DateTimeFormat;
|
|
121
|
+
try {
|
|
122
|
+
ambient = new Intl.DateTimeFormat(locale, {hour: 'numeric'});
|
|
123
|
+
} catch {
|
|
124
|
+
ambient = new Intl.DateTimeFormat('en', {hour: 'numeric'});
|
|
125
|
+
}
|
|
126
|
+
hourFormat = ambient
|
|
127
|
+
.formatToParts(utcDate(13, 0, 0))
|
|
128
|
+
.some(part => part.type === 'dayPeriod')
|
|
129
|
+
? '12h'
|
|
130
|
+
: '24h';
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Literals of every reading this module writes: twelve- and twenty-four-hour,
|
|
134
|
+
// with and without seconds. A literal between two numbers is a separator;
|
|
135
|
+
// one outside them is an edge word.
|
|
136
|
+
const separatorSet = new Set<string>();
|
|
137
|
+
const edgeSet = new Set<string>();
|
|
138
|
+
for (const cycle of ['12h', '24h'] as const) {
|
|
139
|
+
for (const withSeconds of [false, true]) {
|
|
140
|
+
const parts = timeFormatter(locale, cycle, withSeconds).formatToParts(
|
|
141
|
+
utcDate(13, 30, 5),
|
|
142
|
+
);
|
|
143
|
+
const isNumber = (part: Intl.DateTimeFormatPart) =>
|
|
144
|
+
part.type === 'hour' ||
|
|
145
|
+
part.type === 'minute' ||
|
|
146
|
+
part.type === 'second';
|
|
147
|
+
const first = parts.findIndex(isNumber);
|
|
148
|
+
const last = parts.length - 1 - [...parts].reverse().findIndex(isNumber);
|
|
149
|
+
parts.forEach((part, index) => {
|
|
150
|
+
if (part.type !== 'literal') {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const text = normalizeText(part.value).trim();
|
|
154
|
+
if (text === '' || text === ':') {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
(index > first && index < last ? separatorSet : edgeSet).add(text);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const separators = [...separatorSet];
|
|
162
|
+
const edgeWords = [...edgeSet].sort((a, b) => b.length - a.length);
|
|
163
|
+
|
|
164
|
+
// Every hour, because some locales name several periods across the day
|
|
165
|
+
// (flexible day periods). A word that appears on both sides of noon cannot
|
|
166
|
+
// say which half it means, so it is left out.
|
|
167
|
+
const twelveHour = timeFormatter(locale, '12h', false);
|
|
168
|
+
const sides = new Map<string, Set<boolean>>();
|
|
169
|
+
for (let hour = 0; hour < 24; hour++) {
|
|
170
|
+
const word = twelveHour
|
|
171
|
+
.formatToParts(utcDate(hour, 0, 0))
|
|
172
|
+
.find(part => part.type === 'dayPeriod')?.value;
|
|
173
|
+
if (word === undefined) {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
const key = normalizeText(word).trim();
|
|
177
|
+
if (key === '') {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
const seen = sides.get(key) ?? new Set<boolean>();
|
|
181
|
+
seen.add(hour >= 12);
|
|
182
|
+
sides.set(key, seen);
|
|
183
|
+
}
|
|
184
|
+
const dayPeriods = [...sides]
|
|
185
|
+
.filter(([, seen]) => seen.size === 1)
|
|
186
|
+
.map(([word, seen]) => ({word, isPM: seen.has(true)}))
|
|
187
|
+
.sort((a, b) => b.word.length - a.word.length);
|
|
188
|
+
|
|
189
|
+
const twelveHourIsAmbiguous =
|
|
190
|
+
normalizeText(twelveHour.format(utcDate(2, 0, 0))) ===
|
|
191
|
+
normalizeText(twelveHour.format(utcDate(14, 0, 0)));
|
|
192
|
+
|
|
193
|
+
symbols = {
|
|
194
|
+
hourFormat,
|
|
195
|
+
separators,
|
|
196
|
+
edgeWords,
|
|
197
|
+
dayPeriods,
|
|
198
|
+
twelveHourIsAmbiguous,
|
|
199
|
+
};
|
|
200
|
+
symbolsByLocale.set(locale, symbols);
|
|
201
|
+
return symbols;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function normalizeText(text: string): string {
|
|
205
|
+
const folded = text
|
|
206
|
+
.normalize('NFKC')
|
|
207
|
+
.replace(INVISIBLE_CHARS, '')
|
|
208
|
+
.replace(SPACES, ' ');
|
|
209
|
+
return (toAsciiDigits(folded) ?? folded).toLowerCase();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function escapeRegExp(text: string): string {
|
|
213
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* The hour format a field shows: the caller's explicit choice, else the
|
|
218
|
+
* locale's hour cycle (including a `-u-hc-` extension on the tag).
|
|
219
|
+
*/
|
|
220
|
+
export function resolveHourFormat(
|
|
221
|
+
locale: Locale,
|
|
222
|
+
hourFormat?: HourFormat,
|
|
223
|
+
): HourFormat {
|
|
224
|
+
return hourFormat ?? getLocaleTimeSymbols(locale).hourFormat;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Formats an ISO time as the locale writes it, in the given hour format.
|
|
229
|
+
* en-US: "14:30" → "2:30 PM"; de-DE 24h: "14:30"; ko-KR 12h: "오후 2:30".
|
|
230
|
+
* Returns '' for an empty or malformed time.
|
|
231
|
+
*/
|
|
232
|
+
export function formatLocaleTime(
|
|
233
|
+
time: ISOTimeString | undefined,
|
|
234
|
+
locale: Locale,
|
|
235
|
+
hourFormat: HourFormat,
|
|
236
|
+
includeSeconds: boolean = false,
|
|
237
|
+
): string {
|
|
238
|
+
if (!time) {
|
|
239
|
+
return '';
|
|
240
|
+
}
|
|
241
|
+
const parsed = parseISOTime(time);
|
|
242
|
+
if (!parsed) {
|
|
243
|
+
return '';
|
|
244
|
+
}
|
|
245
|
+
const formatLocale =
|
|
246
|
+
hourFormat === '12h' && getLocaleTimeSymbols(locale).twelveHourIsAmbiguous
|
|
247
|
+
? 'en'
|
|
248
|
+
: locale;
|
|
249
|
+
return timeFormatter(formatLocale, hourFormat, includeSeconds)
|
|
250
|
+
.format(utcDate(parsed.hour, parsed.minute, parsed.second))
|
|
251
|
+
.replace(SPACES, ' ');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Parses typed text into an ISO time, accepting the locale's digits,
|
|
256
|
+
* separator, and day-period words as well as everything `parseTimeInput`
|
|
257
|
+
* accepts. Returns null when no reading fits.
|
|
258
|
+
*/
|
|
259
|
+
export function parseLocaleTime(
|
|
260
|
+
input: string,
|
|
261
|
+
locale: Locale,
|
|
262
|
+
includeSeconds: boolean = false,
|
|
263
|
+
): ISOTimeString | null {
|
|
264
|
+
if (!input) {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
const {separators, edgeWords, dayPeriods} = getLocaleTimeSymbols(locale);
|
|
268
|
+
let text = normalizeText(input).trim();
|
|
269
|
+
|
|
270
|
+
// Peel the day period and edge words off both ends, in whatever order the
|
|
271
|
+
// locale writes them ("ŋdi ga 2:30", "kl. 2.30 pm").
|
|
272
|
+
let isPM: boolean | null = null;
|
|
273
|
+
// A word counts only when a digit, whitespace, or nothing sits on its inner
|
|
274
|
+
// side, so gd's "m" (morning) is not read out of an English "pm" or "p.m".
|
|
275
|
+
const strip = (word: string) => {
|
|
276
|
+
if (text.startsWith(word) && WORD_BOUNDARY.test(text.charAt(word.length))) {
|
|
277
|
+
text = text.slice(word.length).trim();
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
if (
|
|
281
|
+
text.endsWith(word) &&
|
|
282
|
+
WORD_BOUNDARY.test(text.charAt(text.length - word.length - 1))
|
|
283
|
+
) {
|
|
284
|
+
text = text.slice(0, -word.length).trim();
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
287
|
+
return false;
|
|
288
|
+
};
|
|
289
|
+
for (let peeled = true; peeled;) {
|
|
290
|
+
peeled = false;
|
|
291
|
+
if (isPM === null) {
|
|
292
|
+
const period = dayPeriods.find(({word}) => strip(word));
|
|
293
|
+
if (period !== undefined) {
|
|
294
|
+
isPM = period.isPM;
|
|
295
|
+
peeled = true;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (edgeWords.some(strip)) {
|
|
299
|
+
peeled = true;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
for (const separator of separators) {
|
|
304
|
+
text = text.replace(
|
|
305
|
+
new RegExp(`(\\d)\\s*${escapeRegExp(separator)}\\s*(?=\\d)`, 'g'),
|
|
306
|
+
'$1:',
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (isPM !== null) {
|
|
311
|
+
text = `${text} ${isPM ? 'pm' : 'am'}`;
|
|
312
|
+
}
|
|
313
|
+
return parseTimeInput(text, includeSeconds);
|
|
314
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file touchPresentation.stylex.ts
|
|
5
|
+
* @input Uses StyleX defineConsts
|
|
6
|
+
* @output Exports touchKeys: the touch presentation resolver's queries as
|
|
7
|
+
* StyleX at-rule keys
|
|
8
|
+
* @position The reference spellings of the touch, compact and iOS-gate
|
|
9
|
+
* conditions. Core styles write these as literal keys wherever they need
|
|
10
|
+
* them (`spec:AST-094` FR13, `spec:AST-012` FR9), and a test pins each
|
|
11
|
+
* literal to these values; utils/touchPresentation.ts holds the same
|
|
12
|
+
* queries for script (matchMedia)
|
|
13
|
+
*
|
|
14
|
+
* These are the spellings to match. A style that changes with these
|
|
15
|
+
* conditions writes the same literal key (`'@media (pointer: coarse)'`, the
|
|
16
|
+
* iOS gate as that media key outside `'@supports (-webkit-touch-callout:
|
|
17
|
+
* none)'`), and a test pins every literal in core to these values. Do not use
|
|
18
|
+
* an imported key as a computed StyleX key: it compiles to a different
|
|
19
|
+
* priority than the literal (6000 against 3200) and reorders the cascade.
|
|
20
|
+
* The iOS supports gate is only for a style iOS WebKit itself requires (the
|
|
21
|
+
* 16px input floor), never a behaviour.
|
|
22
|
+
*
|
|
23
|
+
* SYNC: When modified, update touchPresentation.ts and
|
|
24
|
+
* touchPresentation.test.ts.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import * as stylex from '@stylexjs/stylex';
|
|
28
|
+
|
|
29
|
+
export const touchKeys = stylex.defineConsts({
|
|
30
|
+
/** The primary pointer is coarse (TOUCH_QUERY). */
|
|
31
|
+
touch: '@media (pointer: coarse)',
|
|
32
|
+
/** The viewport is compact (COMPACT_QUERY, `spec:AST-012` FR9). */
|
|
33
|
+
compact: '@media (width < 768px), (height < 500px)',
|
|
34
|
+
/** Both: a presentation such as a sheet in place of a popover. */
|
|
35
|
+
compactTouch:
|
|
36
|
+
'@media (width < 768px) and (pointer: coarse), (height < 500px) and (pointer: coarse)',
|
|
37
|
+
/**
|
|
38
|
+
* The iOS WebKit gate, inside `touch` (IOS_SUPPORTS_QUERY): only for a
|
|
39
|
+
* style iOS WebKit itself requires (the 16px input floor), never a
|
|
40
|
+
* behaviour; touch behaviour is one model on every touch device.
|
|
41
|
+
*/
|
|
42
|
+
iosSupports: '@supports (-webkit-touch-callout: none)',
|
|
43
|
+
});
|