@astryxdesign/core 0.7.7-canary.bf14580 → 0.7.7-canary.c19ca12
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 +4 -1
- package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
- package/dist/AlertDialog/AlertDialog.js +36 -6
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheet.js +76 -6
- package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetPanel.js +23 -4
- package/dist/BottomSheet/bottomSheetStack.d.ts.map +1 -1
- package/dist/BottomSheet/bottomSheetStack.js +5 -4
- package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
- package/dist/BottomSheet/useSheetGestures.js +3 -1
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +47 -3
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +13 -2
- package/dist/ComplexSelector/ComplexSelector.d.ts +4 -1
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +14 -4
- package/dist/ContextMenu/ContextMenu.js +2 -2
- package/dist/DateInput/MonthScroller.d.ts.map +1 -1
- package/dist/DateInput/MonthScroller.js +13 -27
- package/dist/DateInput/Wheel.d.ts.map +1 -1
- package/dist/DateInput/Wheel.js +22 -41
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +7 -31
- package/dist/Field/Field.js +2 -2
- package/dist/HoverCard/HoverCard.d.ts +4 -3
- package/dist/HoverCard/HoverCard.d.ts.map +1 -1
- package/dist/HoverCard/HoverCard.js +27 -2
- package/dist/HoverCard/useHoverCard.d.ts +5 -4
- package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +17 -31
- package/dist/Icon/Icon.d.ts +5 -1
- package/dist/Icon/Icon.d.ts.map +1 -1
- package/dist/Icon/Icon.js +5 -2
- package/dist/Layer/layerAnimations.stylex.d.ts +7 -8
- package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
- package/dist/Layer/layerAnimations.stylex.js +25 -20
- package/dist/Layer/usePressPin.d.ts +17 -5
- package/dist/Layer/usePressPin.d.ts.map +1 -1
- package/dist/Layer/usePressPin.js +62 -12
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +18 -0
- package/dist/PowerSearch/PowerSearch.d.ts +36 -3
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +522 -269
- package/dist/PowerSearch/PowerSearchBottomSheet.d.ts +100 -0
- package/dist/PowerSearch/PowerSearchBottomSheet.d.ts.map +1 -0
- package/dist/PowerSearch/PowerSearchBottomSheet.js +1913 -0
- package/dist/PowerSearch/PowerSearchEditPopover.d.ts +32 -1
- package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchEditPopover.js +17 -6
- package/dist/PowerSearch/PowerSearchValueEditor.d.ts +38 -3
- package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchValueEditor.js +170 -61
- package/dist/PowerSearch/formatFilterValue.d.ts +7 -0
- package/dist/PowerSearch/formatFilterValue.d.ts.map +1 -1
- package/dist/PowerSearch/formatFilterValue.js +11 -1
- package/dist/PowerSearch/formatTokenValue.d.ts +28 -0
- package/dist/PowerSearch/formatTokenValue.d.ts.map +1 -0
- package/dist/PowerSearch/formatTokenValue.js +160 -0
- package/dist/PowerSearch/powerSearchSheetUtils.d.ts +65 -0
- package/dist/PowerSearch/powerSearchSheetUtils.d.ts.map +1 -0
- package/dist/PowerSearch/powerSearchSheetUtils.js +168 -0
- package/dist/RadioList/RadioList.d.ts.map +1 -1
- package/dist/RadioList/RadioList.js +3 -2
- package/dist/RadioList/RadioListItem.d.ts +1 -1
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +5 -3
- package/dist/SegmentedControl/useSegmentedSelectionMotion.d.ts.map +1 -1
- package/dist/SegmentedControl/useSegmentedSelectionMotion.js +3 -3
- package/dist/Slider/sliderTouchController.js +2 -2
- package/dist/Spinner/Spinner.d.ts +1 -1
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +60 -58
- package/dist/Switch/useSwitchTouch.js +4 -4
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +29 -8
- package/dist/TextInput/TextInput.d.ts +2 -2
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +10 -7
- package/dist/Toast/Toast.d.ts +11 -1
- package/dist/Toast/Toast.d.ts.map +1 -1
- package/dist/Toast/Toast.js +25 -15
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +94 -34
- package/dist/Toast/types.d.ts +9 -3
- package/dist/Toast/types.d.ts.map +1 -1
- package/dist/Token/Token.d.ts +2 -2
- package/dist/Token/Token.d.ts.map +1 -1
- package/dist/Token/Token.js +12 -4
- package/dist/Token/TokenNamingContext.d.ts +14 -0
- package/dist/Token/TokenNamingContext.d.ts.map +1 -0
- package/dist/Token/TokenNamingContext.js +23 -0
- package/dist/Tooltip/useTooltip.d.ts +19 -1
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +34 -53
- package/dist/Typeahead/TypeaheadItem.d.ts +2 -1
- package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
- package/dist/animator/createAnimator.d.ts +98 -0
- package/dist/animator/createAnimator.d.ts.map +1 -0
- package/dist/animator/createAnimator.js +259 -0
- package/dist/astryx.css +22 -6
- package/dist/gestures/GestureHost.d.ts +27 -1
- package/dist/gestures/GestureHost.d.ts.map +1 -1
- package/dist/gestures/GestureHost.js +90 -8
- package/dist/gestures/PressTracker.d.ts.map +1 -1
- package/dist/gestures/PressTracker.js +4 -3
- package/dist/hooks/actionGroupTracker.d.ts +4 -1
- package/dist/hooks/actionGroupTracker.d.ts.map +1 -1
- package/dist/hooks/actionGroupTracker.js +13 -3
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/pressGroupContext.d.ts +6 -0
- package/dist/hooks/pressGroupContext.d.ts.map +1 -0
- package/dist/hooks/pressGroupContext.js +20 -0
- package/dist/hooks/useActionGroupTracking.d.ts +53 -0
- package/dist/hooks/useActionGroupTracking.d.ts.map +1 -0
- package/dist/hooks/useActionGroupTracking.js +174 -0
- package/dist/hooks/useAnimator.d.ts +41 -0
- package/dist/hooks/useAnimator.d.ts.map +1 -0
- package/dist/hooks/useAnimator.js +70 -0
- package/dist/hooks/useAttachment.d.ts +1 -1
- package/dist/hooks/useAttachment.js +1 -1
- package/dist/hooks/useInputStatusIcon.d.ts +1 -1
- package/dist/hooks/useInputStatusIcon.d.ts.map +1 -1
- package/dist/hooks/useInputStatusIcon.js +2 -2
- package/dist/hooks/useLongPress.d.ts +6 -2
- package/dist/hooks/useLongPress.d.ts.map +1 -1
- package/dist/hooks/useLongPress.js +95 -15
- package/dist/hooks/useMenuPress.d.ts.map +1 -1
- package/dist/hooks/useMenuPress.js +4 -2
- package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.d.ts +8 -0
- package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.d.ts.map +1 -0
- package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.js +42 -0
- package/dist/i18n/generated-english/PowerSearch/PowerSearchValueEditor.generated.d.ts.map +1 -1
- package/dist/i18n/generated-english/PowerSearch/PowerSearchValueEditor.generated.js +6 -0
- package/dist/i18n/generated-english/PowerSearch/formatTokenValue.generated.d.ts +8 -0
- package/dist/i18n/generated-english/PowerSearch/formatTokenValue.generated.d.ts.map +1 -0
- package/dist/i18n/generated-english/PowerSearch/formatTokenValue.generated.js +14 -0
- package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/en.generated.js +30 -0
- package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pseudo.generated.js +30 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/internal/indicatorPress.stylex.d.ts +20 -0
- package/dist/internal/indicatorPress.stylex.d.ts.map +1 -0
- package/dist/internal/indicatorPress.stylex.js +26 -0
- package/dist/motion/animator.d.ts +14 -14
- package/dist/motion/animator.d.ts.map +1 -1
- package/dist/motion/animator.js +41 -18
- package/dist/motion/reducedMotion.d.ts +24 -0
- package/dist/motion/reducedMotion.d.ts.map +1 -1
- package/dist/motion/reducedMotion.js +78 -1
- package/dist/motion/uikit/alert.d.ts +41 -0
- package/dist/motion/uikit/alert.d.ts.map +1 -0
- package/dist/motion/uikit/alert.js +43 -0
- package/dist/privateAttributes/pressGroupItem.stylex.d.ts +15 -0
- package/dist/privateAttributes/pressGroupItem.stylex.d.ts.map +1 -0
- package/dist/privateAttributes/pressGroupItem.stylex.js +23 -0
- package/dist/utils/clickGuard.d.ts +8 -1
- package/dist/utils/clickGuard.d.ts.map +1 -1
- package/dist/utils/clickGuard.js +24 -1
- package/dist/utils/dateParser.d.ts.map +1 -1
- package/dist/utils/dateParser.js +12 -0
- package/dist/utils/focusReturn.d.ts +15 -0
- package/dist/utils/focusReturn.d.ts.map +1 -1
- package/dist/utils/focusReturn.js +28 -0
- package/dist/utils/interactionOverlay.stylex.d.ts +3 -2
- package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
- package/dist/utils/interactionOverlay.stylex.js +3 -2
- package/dist/utils/pressFeedback.d.ts.map +1 -1
- package/dist/utils/pressFeedback.js +19 -6
- package/dist/utils/triggerFocus.d.ts +36 -0
- package/dist/utils/triggerFocus.d.ts.map +1 -0
- package/dist/utils/triggerFocus.js +65 -0
- package/locales/en.json +120 -0
- package/locales/pseudo.json +90 -0
- package/package.json +9 -4
- package/src/AlertDialog/AlertDialog.doc.mjs +2 -2
- package/src/AlertDialog/AlertDialog.tsx +37 -5
- package/src/BottomSheet/BottomSheet.tsx +85 -5
- package/src/BottomSheet/BottomSheetPanel.tsx +40 -5
- package/src/BottomSheet/bottomSheetStack.ts +8 -4
- package/src/BottomSheet/useSheetGestures.ts +3 -1
- package/src/Button/Button.tsx +63 -5
- package/src/CheckboxInput/CheckboxInput.doc.mjs +18 -0
- package/src/CheckboxInput/CheckboxInput.tsx +36 -9
- package/src/ComplexSelector/ComplexSelector.tsx +20 -5
- package/src/ContextMenu/ContextMenu.tsx +2 -2
- package/src/DateInput/MonthScroller.tsx +18 -22
- package/src/DateInput/Wheel.tsx +30 -32
- package/src/Dialog/Dialog.tsx +10 -31
- package/src/Field/Field.tsx +2 -2
- package/src/HoverCard/HoverCard.doc.mjs +4 -2
- package/src/HoverCard/HoverCard.tsx +35 -4
- package/src/HoverCard/useHoverCard.doc.mjs +178 -36
- package/src/HoverCard/useHoverCard.tsx +46 -52
- package/src/Icon/Icon.doc.mjs +49 -8
- package/src/Icon/Icon.tsx +15 -7
- package/src/Layer/layerAnimations.stylex.ts +24 -16
- package/src/Layer/usePressPin.ts +81 -15
- package/src/Lightbox/Lightbox.tsx +18 -0
- package/src/PowerSearch/PowerSearch.doc.mjs +54 -5
- package/src/PowerSearch/PowerSearch.tsx +734 -288
- package/src/PowerSearch/PowerSearchBottomSheet.tsx +2515 -0
- package/src/PowerSearch/PowerSearchEditPopover.tsx +28 -6
- package/src/PowerSearch/PowerSearchValueEditor.tsx +203 -38
- package/src/PowerSearch/formatFilterValue.ts +18 -1
- package/src/PowerSearch/formatTokenValue.ts +215 -0
- package/src/PowerSearch/powerSearchSheetUtils.ts +224 -0
- package/src/RadioList/RadioList.tsx +3 -2
- package/src/RadioList/RadioListItem.tsx +5 -3
- package/src/SegmentedControl/useSegmentedSelectionMotion.ts +6 -3
- package/src/Slider/sliderTouchController.ts +2 -2
- package/src/Spinner/Spinner.tsx +77 -63
- package/src/Switch/useSwitchTouch.ts +4 -4
- package/src/Table/BaseTable.tsx +46 -12
- package/src/TextInput/TextInput.tsx +11 -10
- package/src/Toast/Toast.doc.mjs +4 -3
- package/src/Toast/Toast.tsx +52 -32
- package/src/Toast/ToastViewport.tsx +121 -35
- package/src/Toast/types.ts +9 -3
- package/src/Token/Token.tsx +13 -4
- package/src/Token/TokenNamingContext.ts +28 -0
- package/src/Tooltip/useTooltip.doc.mjs +24 -0
- package/src/Tooltip/useTooltip.tsx +61 -56
- package/src/Typeahead/TypeaheadItem.doc.mjs +6 -3
- package/src/Typeahead/TypeaheadItem.tsx +2 -1
- package/src/animator/createAnimator.doc.mjs +153 -0
- package/src/animator/createAnimator.ts +324 -0
- package/src/gestures/GestureHost.ts +105 -10
- package/src/gestures/PressTracker.ts +4 -3
- package/src/gestures/README.md +7 -1
- package/src/hooks/actionGroupTracker.ts +20 -5
- package/src/hooks/index.ts +2 -0
- package/src/hooks/pressGroupContext.ts +21 -0
- package/src/hooks/useActionGroupTracking.ts +224 -0
- package/src/hooks/useAnimator.doc.mjs +109 -0
- package/src/hooks/useAnimator.ts +67 -0
- package/src/hooks/useAttachment.ts +1 -1
- package/src/hooks/useInputStatusIcon.tsx +3 -2
- package/src/hooks/useLongPress.doc.mjs +2 -2
- package/src/hooks/useLongPress.ts +136 -15
- package/src/hooks/useMenuPress.ts +8 -2
- package/src/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.ts +43 -0
- package/src/i18n/generated-english/PowerSearch/PowerSearchValueEditor.generated.ts +6 -0
- package/src/i18n/generated-english/PowerSearch/formatTokenValue.generated.ts +15 -0
- package/src/i18n/generated-locales/en.generated.ts +41 -0
- package/src/i18n/generated-locales/pseudo.generated.ts +42 -0
- package/src/index.ts +13 -0
- package/src/internal/indicatorPress.stylex.ts +24 -0
- package/src/motion/README.md +8 -5
- package/src/motion/animator.ts +61 -29
- package/src/motion/reducedMotion.ts +79 -1
- package/src/motion/uikit/alert.ts +43 -0
- package/src/privateAttributes/pressGroupItem.stylex.ts +23 -0
- package/src/utils/clickGuard.ts +37 -1
- package/src/utils/dateParser.ts +16 -0
- package/src/utils/focusReturn.ts +30 -0
- package/src/utils/interactionOverlay.stylex.ts +3 -2
- package/src/utils/pressFeedback.ts +19 -6
- package/src/utils/triggerFocus.ts +80 -0
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
useRef,
|
|
39
39
|
useState,
|
|
40
40
|
} from 'react';
|
|
41
|
+
import {flushSync} from 'react-dom';
|
|
41
42
|
import * as stylex from '@stylexjs/stylex';
|
|
42
43
|
import {useCalendarDays} from '../Calendar';
|
|
43
44
|
import {useDirection, useLocale} from '../i18n';
|
|
@@ -62,7 +63,11 @@ import {
|
|
|
62
63
|
plainDateToISO,
|
|
63
64
|
DATE_FORMAT_WITH_WEEKDAY,
|
|
64
65
|
} from '../utils';
|
|
65
|
-
import {
|
|
66
|
+
import {
|
|
67
|
+
playReducedMotionDip,
|
|
68
|
+
reducedMotionFade,
|
|
69
|
+
type ReducedMotionDip,
|
|
70
|
+
} from '../motion/reducedMotion';
|
|
66
71
|
import {dateInputTouchSizes, dateInputTouchGeometry} from './tokens.stylex';
|
|
67
72
|
import {useOwnScrollGesture} from './useOwnScrollGesture';
|
|
68
73
|
import {useScrollSettle} from './useScrollSettle';
|
|
@@ -434,17 +439,12 @@ export function MonthScroller({
|
|
|
434
439
|
|
|
435
440
|
// The Reduced Motion dip of a step on a touch screen, owned here so a
|
|
436
441
|
// second step or the unmount clears it.
|
|
437
|
-
const dipRef = useRef<
|
|
442
|
+
const dipRef = useRef<ReducedMotionDip | null>(null);
|
|
438
443
|
const stopDip = useCallback(() => {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
window.clearTimeout(dip.timer);
|
|
442
|
-
dip.animation.cancel();
|
|
443
|
-
dipRef.current = null;
|
|
444
|
-
}
|
|
444
|
+
dipRef.current?.cancel();
|
|
445
|
+
dipRef.current = null;
|
|
445
446
|
}, []);
|
|
446
|
-
// Synchronizes with the dip's
|
|
447
|
-
// scroller.
|
|
447
|
+
// Synchronizes with the dip's animations: they do not outlive the scroller.
|
|
448
448
|
useEffect(() => stopDip, [stopDip]);
|
|
449
449
|
|
|
450
450
|
const scrollToMonth = useCallback(
|
|
@@ -505,18 +505,14 @@ export function MonthScroller({
|
|
|
505
505
|
land();
|
|
506
506
|
return;
|
|
507
507
|
}
|
|
508
|
-
// A
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
)
|
|
515
|
-
|
|
516
|
-
dipRef.current = null;
|
|
517
|
-
land();
|
|
518
|
-
}, fade.duration / 2),
|
|
519
|
-
};
|
|
508
|
+
// A newer step replaces this one: only its scroll lands.
|
|
509
|
+
dipRef.current = playReducedMotionDip(scroller, fade, () => {
|
|
510
|
+
dipRef.current = null;
|
|
511
|
+
// The fade-in starts as soon as this returns. So render now: the
|
|
512
|
+
// scroll lands in the layout effect, before the fade-in.
|
|
513
|
+
// eslint-disable-next-line @eslint-react/dom-no-flush-sync -- the month must change before the dip's fade-in starts
|
|
514
|
+
flushSync(land);
|
|
515
|
+
});
|
|
520
516
|
return;
|
|
521
517
|
}
|
|
522
518
|
steeredRowRef.current = row;
|
package/src/DateInput/Wheel.tsx
CHANGED
|
@@ -49,7 +49,11 @@ import {
|
|
|
49
49
|
durationVars,
|
|
50
50
|
} from '../theme/tokens.stylex';
|
|
51
51
|
import {focusOutlineStyles} from '../utils';
|
|
52
|
-
import {
|
|
52
|
+
import {
|
|
53
|
+
playReducedMotionDip,
|
|
54
|
+
reducedMotionFade,
|
|
55
|
+
type ReducedMotionDip,
|
|
56
|
+
} from '../motion/reducedMotion';
|
|
53
57
|
import {dateInputTouchSizes, dateInputTouchGeometry} from './tokens.stylex';
|
|
54
58
|
import {useOwnScrollGesture} from './useOwnScrollGesture';
|
|
55
59
|
import {usePointerDragScroll} from './usePointerDragScroll';
|
|
@@ -331,19 +335,19 @@ export function Wheel({
|
|
|
331
335
|
// once on a fine one.
|
|
332
336
|
const lastPointerRef = useRef<string | null>(null);
|
|
333
337
|
|
|
334
|
-
// A reduced-motion dip whose rows have not landed yet:
|
|
335
|
-
//
|
|
336
|
-
//
|
|
337
|
-
const pendingFadeRef = useRef<{
|
|
338
|
+
// A reduced-motion dip whose rows have not landed yet: the row they land
|
|
339
|
+
// on. While one is pending the rows stay put for it, so they never move at
|
|
340
|
+
// full opacity, and a further step retargets it.
|
|
341
|
+
const pendingFadeRef = useRef<{top: number} | null>(null);
|
|
338
342
|
// The running dip, so a step during its fade-in starts the next one where
|
|
339
343
|
// the column is.
|
|
340
|
-
const dipRef = useRef<
|
|
344
|
+
const dipRef = useRef<ReducedMotionDip | null>(null);
|
|
345
|
+
// Synchronizes with the dip's animations: an unmounted wheel lands nothing.
|
|
341
346
|
useEffect(
|
|
342
347
|
() => () => {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
}
|
|
348
|
+
pendingFadeRef.current = null;
|
|
349
|
+
dipRef.current?.cancel();
|
|
350
|
+
dipRef.current = null;
|
|
347
351
|
},
|
|
348
352
|
[],
|
|
349
353
|
);
|
|
@@ -368,19 +372,15 @@ export function Wheel({
|
|
|
368
372
|
? reducedMotionFade(scroller, {touchRelease})
|
|
369
373
|
: null;
|
|
370
374
|
const pending = pendingFadeRef.current;
|
|
375
|
+
// A change that lands now replaces a pending landing. Its dip goes on
|
|
376
|
+
// and fades back in.
|
|
371
377
|
if (fadeTiming == null) {
|
|
372
|
-
|
|
373
|
-
window.clearTimeout(pending.timer);
|
|
374
|
-
pendingFadeRef.current = null;
|
|
375
|
-
}
|
|
378
|
+
pendingFadeRef.current = null;
|
|
376
379
|
scroller.scrollTo({top, behavior});
|
|
377
380
|
return;
|
|
378
381
|
}
|
|
379
382
|
if (fadeTiming.duration === 0 || typeof scroller.animate !== 'function') {
|
|
380
|
-
|
|
381
|
-
window.clearTimeout(pending.timer);
|
|
382
|
-
pendingFadeRef.current = null;
|
|
383
|
-
}
|
|
383
|
+
pendingFadeRef.current = null;
|
|
384
384
|
scroller.scrollTo({top, behavior: 'auto'});
|
|
385
385
|
return;
|
|
386
386
|
}
|
|
@@ -389,26 +389,24 @@ export function Wheel({
|
|
|
389
389
|
pending.top = top;
|
|
390
390
|
return;
|
|
391
391
|
}
|
|
392
|
-
const {duration, easing} = fadeTiming;
|
|
393
392
|
// A step during the last dip's fade-in starts the new dip at the
|
|
394
393
|
// column's current opacity, not at full opacity. A leaving element fades
|
|
395
394
|
// out from where it is.
|
|
396
395
|
const shown = Number.parseFloat(getComputedStyle(scroller).opacity);
|
|
397
396
|
dipRef.current?.cancel();
|
|
398
|
-
|
|
399
|
-
[
|
|
400
|
-
{opacity: Number.isFinite(shown) ? shown : 1, easing},
|
|
401
|
-
{opacity: 0, easing},
|
|
402
|
-
{opacity: 1},
|
|
403
|
-
],
|
|
404
|
-
{duration, easing: 'linear'},
|
|
405
|
-
);
|
|
406
|
-
const landing = {top, timer: 0};
|
|
407
|
-
landing.timer = window.setTimeout(() => {
|
|
408
|
-
pendingFadeRef.current = null;
|
|
409
|
-
scroller.scrollTo({top: landing.top, behavior: 'auto'});
|
|
410
|
-
}, duration / 2);
|
|
397
|
+
const landing = {top};
|
|
411
398
|
pendingFadeRef.current = landing;
|
|
399
|
+
dipRef.current = playReducedMotionDip(
|
|
400
|
+
scroller,
|
|
401
|
+
fadeTiming,
|
|
402
|
+
() => {
|
|
403
|
+
if (pendingFadeRef.current === landing) {
|
|
404
|
+
pendingFadeRef.current = null;
|
|
405
|
+
scroller.scrollTo({top: landing.top, behavior: 'auto'});
|
|
406
|
+
}
|
|
407
|
+
},
|
|
408
|
+
Number.isFinite(shown) ? shown : 1,
|
|
409
|
+
);
|
|
412
410
|
},
|
|
413
411
|
[itemBlockSize],
|
|
414
412
|
);
|
package/src/Dialog/Dialog.tsx
CHANGED
|
@@ -81,7 +81,10 @@ import {themeProps} from '../utils/themeProps';
|
|
|
81
81
|
import type {DialogVariantMap} from './index';
|
|
82
82
|
import {focusOutlineProps} from '../utils/focusOutline.stylex';
|
|
83
83
|
import {useInteractionModalityTracking} from '../utils/interactionModality';
|
|
84
|
-
import {
|
|
84
|
+
import {
|
|
85
|
+
focusForLastInput,
|
|
86
|
+
prepareFocusReturnOnRemoval,
|
|
87
|
+
} from '../utils/focusReturn';
|
|
85
88
|
import {ModalAnnounceProvider} from '../hooks/ModalAnnounceProvider';
|
|
86
89
|
|
|
87
90
|
import {useMergedRefs} from '../hooks/useMergedRefs';
|
|
@@ -653,39 +656,15 @@ export function Dialog({
|
|
|
653
656
|
}, [isOpen, isInline]);
|
|
654
657
|
|
|
655
658
|
// A dialog that unmounts while open (rendered only while open, or removed
|
|
656
|
-
// with the rest of a view) never runs the close branch above
|
|
657
|
-
//
|
|
658
|
-
// it, or dropped to the body by the removal, goes back to the opener.
|
|
659
|
+
// with the rest of a view) never runs the close branch above. Focus the
|
|
660
|
+
// removal lost goes back to the opener.
|
|
659
661
|
useEffect(() => {
|
|
660
|
-
|
|
662
|
+
const dialog = dialogRef.current;
|
|
663
|
+
if (isInline || dialog == null) {
|
|
661
664
|
return;
|
|
662
665
|
}
|
|
663
|
-
const
|
|
664
|
-
|
|
665
|
-
// element can no longer be found once the dialog is gone.
|
|
666
|
-
const host = dialog?.ownerDocument.defaultView?.frameElement ?? null;
|
|
667
|
-
return () => {
|
|
668
|
-
const trigger = triggerElementRef.current;
|
|
669
|
-
if (dialog == null || trigger == null) {
|
|
670
|
-
return;
|
|
671
|
-
}
|
|
672
|
-
// Checked once the commit has finished: StrictMode's simulated unmount
|
|
673
|
-
// leaves the dialog in the document, and only a real removal returns
|
|
674
|
-
// focus.
|
|
675
|
-
queueMicrotask(() => {
|
|
676
|
-
if (dialog.isConnected || !trigger.isConnected) {
|
|
677
|
-
return;
|
|
678
|
-
}
|
|
679
|
-
// isFocusLost reads the dialog's own document. For a dialog in an
|
|
680
|
-
// iframe, the document that hosts the frame must also have lost focus
|
|
681
|
-
// or keep it on that frame, wherever the opener is: a control the
|
|
682
|
-
// user pressed outside the frame to remove the dialog, or the frame
|
|
683
|
-
// itself, keeps its focus.
|
|
684
|
-
if (isFocusLost(dialog) && (host == null || isFocusLost(host))) {
|
|
685
|
-
focusForLastInput(trigger);
|
|
686
|
-
}
|
|
687
|
-
});
|
|
688
|
-
};
|
|
666
|
+
const returnFocusOnRemoval = prepareFocusReturnOnRemoval(dialog);
|
|
667
|
+
return () => returnFocusOnRemoval(triggerElementRef.current);
|
|
689
668
|
}, [isInline]);
|
|
690
669
|
|
|
691
670
|
// Host app-global surfaces (toasts) inside this modal while it is open, so
|
package/src/Field/Field.tsx
CHANGED
|
@@ -25,11 +25,11 @@ import {FieldStatus} from '../FieldStatus/FieldStatus';
|
|
|
25
25
|
import {useFieldStatusAnnouncement} from '../FieldStatus/useFieldStatusAnnouncement';
|
|
26
26
|
import {spacingVars, borderVars, sizeVars} from '../theme/tokens.stylex';
|
|
27
27
|
import type {IconType} from '../Icon';
|
|
28
|
-
import {mergeProps} from '../utils';
|
|
28
|
+
import {mergeProps} from '../utils/mergeProps';
|
|
29
29
|
import {useDevWarning} from '../hooks/useDevWarning';
|
|
30
30
|
import {FormLayoutContext} from '../FormLayout/FormLayoutContext';
|
|
31
31
|
import {Text} from '../Text/Text';
|
|
32
|
-
import {VisuallyHidden} from '../VisuallyHidden';
|
|
32
|
+
import {VisuallyHidden} from '../VisuallyHidden/VisuallyHidden';
|
|
33
33
|
import {themeProps} from '../utils/themeProps';
|
|
34
34
|
import type {FieldStatusPlacement} from './fieldStatusPlacement';
|
|
35
35
|
|
|
@@ -98,7 +98,8 @@ export const docs = {
|
|
|
98
98
|
{
|
|
99
99
|
name: 'focusTrigger',
|
|
100
100
|
type: "'auto' | 'always' | 'never'",
|
|
101
|
-
description:
|
|
101
|
+
description:
|
|
102
|
+
'When focus should open the hover card. auto opens when the trigger itself takes focus, or focus lands inside a trigger that is focusable at that moment, so a roving-tabindex member opens when the arrow keys reach it.',
|
|
102
103
|
default: "'auto'",
|
|
103
104
|
},
|
|
104
105
|
{
|
|
@@ -303,7 +304,8 @@ export const docsZh = {
|
|
|
303
304
|
{
|
|
304
305
|
name: 'focusTrigger',
|
|
305
306
|
type: "'auto' | 'always' | 'never'",
|
|
306
|
-
description:
|
|
307
|
+
description:
|
|
308
|
+
'焦点何时打开悬浮卡片。auto 在触发元素自身获得焦点,或焦点落在当时可聚焦的触发元素内部时打开,因此方向键到达漫游 tabindex 成员时也会打开。',
|
|
307
309
|
default: "'auto'",
|
|
308
310
|
},
|
|
309
311
|
{
|
|
@@ -16,7 +16,14 @@
|
|
|
16
16
|
* - /packages/cli/assets/templates/blocks/components/HoverCard/ (showcase blocks)
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
useCallback,
|
|
21
|
+
useRef,
|
|
22
|
+
useState,
|
|
23
|
+
type ReactElement,
|
|
24
|
+
type ReactNode,
|
|
25
|
+
} from 'react';
|
|
26
|
+
import {observeMutations} from '../utils/sharedMutationObserver';
|
|
20
27
|
import {useIsomorphicLayoutEffect} from '../hooks/useIsomorphicLayoutEffect';
|
|
21
28
|
import * as stylex from '@stylexjs/stylex';
|
|
22
29
|
import {
|
|
@@ -148,9 +155,10 @@ export interface HoverCardProps extends Pick<
|
|
|
148
155
|
|
|
149
156
|
/**
|
|
150
157
|
* When to trigger on focus:
|
|
151
|
-
* - `auto`:
|
|
152
|
-
*
|
|
153
|
-
* - `
|
|
158
|
+
* - `auto`: focus on the trigger itself, or within a trigger that is
|
|
159
|
+
* focusable when focus arrives
|
|
160
|
+
* - `always`: focus on the trigger or anything inside it
|
|
161
|
+
* - `never`: never on focus (for composite widgets)
|
|
154
162
|
*
|
|
155
163
|
* @default 'auto'
|
|
156
164
|
*/
|
|
@@ -303,6 +311,28 @@ export function HoverCard({
|
|
|
303
311
|
onHide: handleHide,
|
|
304
312
|
});
|
|
305
313
|
|
|
314
|
+
// The wrapper's first element is the trigger, and it can be replaced after
|
|
315
|
+
// mount: by new children, or by a child component that swaps its own root
|
|
316
|
+
// without a render here. Syncs with the DOM: on a replacement the wiring
|
|
317
|
+
// below re-runs, so the old element is released before the new one is wired.
|
|
318
|
+
const [triggerReplacements, setTriggerReplacements] = useState(0);
|
|
319
|
+
const observedTriggerRef = useRef<Element | null>(null);
|
|
320
|
+
useIsomorphicLayoutEffect(() => {
|
|
321
|
+
const wrapper = wrapperRef.current;
|
|
322
|
+
if (textOnly || wrapper == null) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
observedTriggerRef.current = wrapper.firstElementChild;
|
|
326
|
+
return observeMutations(wrapper, {childList: true}, () => {
|
|
327
|
+
const trigger = wrapper.firstElementChild;
|
|
328
|
+
if (trigger === observedTriggerRef.current) {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
observedTriggerRef.current = trigger;
|
|
332
|
+
setTriggerReplacements(count => count + 1);
|
|
333
|
+
});
|
|
334
|
+
}, [textOnly]);
|
|
335
|
+
|
|
306
336
|
// For element children with display:contents, attach ref to first child
|
|
307
337
|
useIsomorphicLayoutEffect(() => {
|
|
308
338
|
if (textOnly) {
|
|
@@ -407,6 +437,7 @@ export function HoverCard({
|
|
|
407
437
|
hoverCard.id,
|
|
408
438
|
hoverCard.isOpen,
|
|
409
439
|
hoverCard.describedBy,
|
|
440
|
+
triggerReplacements,
|
|
410
441
|
]);
|
|
411
442
|
|
|
412
443
|
// While closed, useLayer leaves only an inert <template> marker at this JSX
|
|
@@ -5,37 +5,159 @@ export const docs = {
|
|
|
5
5
|
name: 'useHoverCard',
|
|
6
6
|
displayName: 'useHoverCard',
|
|
7
7
|
group: 'HoverCard',
|
|
8
|
-
keywords: [
|
|
8
|
+
keywords: [
|
|
9
|
+
'hovercard',
|
|
10
|
+
'hover',
|
|
11
|
+
'preview',
|
|
12
|
+
'card',
|
|
13
|
+
'tooltip',
|
|
14
|
+
'popup',
|
|
15
|
+
'floating',
|
|
16
|
+
'anchor',
|
|
17
|
+
],
|
|
9
18
|
params: [
|
|
10
|
-
{
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
{
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
{
|
|
20
|
+
name: 'placement',
|
|
21
|
+
type: "'above' | 'below' | 'start' | 'end'",
|
|
22
|
+
description:
|
|
23
|
+
"Position relative to the trigger. Logical: start/end resolve against the popover\'s own inherited direction (RTL mirrors in pure CSS).",
|
|
24
|
+
default: "'above'",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'alignment',
|
|
28
|
+
type: "'start' | 'center' | 'end'",
|
|
29
|
+
description:
|
|
30
|
+
"Alignment along the placement axis. Logical: start/end resolve against the popover\'s own inherited direction (RTL mirrors in pure CSS).",
|
|
31
|
+
default: "'center'",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: 'delay',
|
|
35
|
+
type: 'number',
|
|
36
|
+
description:
|
|
37
|
+
'Delay before showing the hover card on hover, in milliseconds.',
|
|
38
|
+
default: '300',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'hideDelay',
|
|
42
|
+
type: 'number',
|
|
43
|
+
description:
|
|
44
|
+
'Delay before hiding after mouse or focus leaves, in milliseconds.',
|
|
45
|
+
default: '200',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'focusTrigger',
|
|
49
|
+
type: "'auto' | 'always' | 'never'",
|
|
50
|
+
description:
|
|
51
|
+
'When focus should open the hover card. auto opens when the trigger itself takes focus, or focus lands inside a trigger that is focusable at that moment, so a roving-tabindex member opens when the arrow keys reach it.',
|
|
52
|
+
default: "'auto'",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'touchTrigger',
|
|
56
|
+
type: "'auto' | 'tap' | 'none'",
|
|
57
|
+
description:
|
|
58
|
+
'What a tap does where there is no hover. auto opens on tap unless the trigger performs an action of its own (an aria-disabled control performs none); tap always opens; none never opens on touch.',
|
|
59
|
+
default: "'auto'",
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'isEnabled',
|
|
63
|
+
type: 'boolean',
|
|
64
|
+
description: 'Whether hover and focus triggers are enabled.',
|
|
65
|
+
default: 'true',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'label',
|
|
69
|
+
type: 'string',
|
|
70
|
+
description:
|
|
71
|
+
'Accessible name for the hover card popup. When provided, the popup is exposed as a named role="dialog"; when omitted, it falls back to role="group" (a group may validly be unnamed).',
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: 'isOpen',
|
|
75
|
+
type: 'boolean',
|
|
76
|
+
description:
|
|
77
|
+
'Controlled open state. true force-shows, false force-hides, undefined lets hover/focus manage visibility.',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'isDefaultOpen',
|
|
81
|
+
type: 'boolean',
|
|
82
|
+
description: 'Whether the hover card should be shown on mount.',
|
|
83
|
+
default: 'false',
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: 'onShow',
|
|
87
|
+
type: '() => void',
|
|
88
|
+
description: 'Callback fired when the hover card becomes visible.',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: 'onHide',
|
|
92
|
+
type: '() => void',
|
|
93
|
+
description: 'Callback fired when the hover card is hidden.',
|
|
94
|
+
},
|
|
22
95
|
],
|
|
23
96
|
returns: [
|
|
24
|
-
{
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
{
|
|
31
|
-
|
|
97
|
+
{
|
|
98
|
+
name: 'ref',
|
|
99
|
+
type: 'RefCallback<HTMLElement>',
|
|
100
|
+
description:
|
|
101
|
+
'Combined ref that sets both position and interaction on the same trigger element.',
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: 'positionRef',
|
|
105
|
+
type: 'RefCallback<HTMLElement>',
|
|
106
|
+
description:
|
|
107
|
+
'Ref for the positioning anchor element. Use when position and interaction live on different elements.',
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: 'interactionRef',
|
|
111
|
+
type: 'RefCallback<HTMLElement>',
|
|
112
|
+
description:
|
|
113
|
+
'Ref for the hover/focus interaction element. Use with positionRef for split trigger patterns.',
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: 'anchorId',
|
|
117
|
+
type: 'string',
|
|
118
|
+
description: 'CSS anchor name for advanced positioning cases.',
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: 'describedBy',
|
|
122
|
+
type: 'string',
|
|
123
|
+
description: 'ID to compose into aria-describedby on the trigger.',
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: 'renderHoverCard',
|
|
127
|
+
type: "(children: ReactNode, props?: Omit<ContextRenderProps, 'positioning'>) => ReactNode",
|
|
128
|
+
description:
|
|
129
|
+
'Render function for the anchor-positioned hover card content. The positioning opt-out is excluded: the hover card always derives its position from placement/alignment.',
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: 'show',
|
|
133
|
+
type: '() => void',
|
|
134
|
+
description: 'Imperatively show the hover card immediately.',
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
name: 'hide',
|
|
138
|
+
type: '() => void',
|
|
139
|
+
description: 'Imperatively hide the hover card immediately.',
|
|
140
|
+
},
|
|
32
141
|
],
|
|
33
142
|
usage: {
|
|
34
|
-
description:
|
|
143
|
+
description:
|
|
144
|
+
'Headless hook for hover-triggered floating cards. Builds on useLayer with hover/focus intent detection, configurable delays, safe hover behavior, and accessible aria-describedby linking. Use for rich previews on hover when you need full control over the trigger or rendered content.',
|
|
35
145
|
bestPractices: [
|
|
36
|
-
{
|
|
37
|
-
|
|
38
|
-
|
|
146
|
+
{
|
|
147
|
+
guidance: true,
|
|
148
|
+
description:
|
|
149
|
+
'Use for rich content previews such as user profiles, entity summaries, and link previews.',
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
guidance: true,
|
|
153
|
+
description:
|
|
154
|
+
'Prefer the HoverCard component for standard trigger-content pairs; use the hook for custom trigger patterns.',
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
guidance: false,
|
|
158
|
+
description:
|
|
159
|
+
'Use for simple text hints: use Tooltip or useTooltip instead.',
|
|
160
|
+
},
|
|
39
161
|
],
|
|
40
162
|
},
|
|
41
163
|
relatedComponents: ['HoverCard', 'Tooltip', 'Popover'],
|
|
@@ -46,17 +168,24 @@ export const docs = {
|
|
|
46
168
|
|
|
47
169
|
/** @type {import('@astryxdesign/cli/authoring').HookTranslationDoc} */
|
|
48
170
|
export const docsDense = {
|
|
49
|
-
description:
|
|
171
|
+
description:
|
|
172
|
+
'Headless hover-triggered floating cards. Builds on useLayer w/ hover/focus intent, delays, safe hover behavior, aria-describedby. Use for rich previews when trigger/content rendering needs full control.',
|
|
50
173
|
paramDescriptions: {
|
|
51
|
-
placement:
|
|
52
|
-
|
|
174
|
+
placement:
|
|
175
|
+
"position relative to trigger. logical: start/end follow the popover's inherited direction (RTL mirrors).",
|
|
176
|
+
alignment:
|
|
177
|
+
"alignment along placement axis. logical: start/end follow the popover's inherited direction (RTL mirrors).",
|
|
53
178
|
delay: 'show delay in ms.',
|
|
54
179
|
hideDelay: 'hide delay after mouse/focus leave in ms.',
|
|
55
|
-
focusTrigger:
|
|
56
|
-
|
|
180
|
+
focusTrigger:
|
|
181
|
+
'when focus opens hover card; auto = focus on the trigger, or inside a trigger focusable when focus arrives.',
|
|
182
|
+
touchTrigger:
|
|
183
|
+
'tap behavior with no hover; auto = tap unless trigger acts, tap = always, none = never.',
|
|
57
184
|
isEnabled: 'whether hover/focus triggers are enabled.',
|
|
58
|
-
label:
|
|
59
|
-
|
|
185
|
+
label:
|
|
186
|
+
'accessible name for popup. with label: named role="dialog"; without: role="group".',
|
|
187
|
+
isOpen:
|
|
188
|
+
'controlled open state: true force-show, false force-hide, undefined unmanaged.',
|
|
60
189
|
isDefaultOpen: 'show on mount.',
|
|
61
190
|
onShow: 'fires when hover card becomes visible.',
|
|
62
191
|
onHide: 'fires when hover card hides.',
|
|
@@ -72,11 +201,24 @@ export const docsDense = {
|
|
|
72
201
|
hide: 'hide immediately.',
|
|
73
202
|
},
|
|
74
203
|
usage: {
|
|
75
|
-
description:
|
|
204
|
+
description:
|
|
205
|
+
'Headless hover-triggered floating cards. Builds on useLayer w/ hover/focus intent, delays, safe hover behavior, aria-describedby. Use for rich previews when trigger/content rendering needs full control.',
|
|
76
206
|
bestPractices: [
|
|
77
|
-
{
|
|
78
|
-
|
|
79
|
-
|
|
207
|
+
{
|
|
208
|
+
guidance: true,
|
|
209
|
+
description:
|
|
210
|
+
'Use for rich previews: profiles, entity summaries, link previews.',
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
guidance: true,
|
|
214
|
+
description:
|
|
215
|
+
'Prefer HoverCard for standard trigger-content pairs; use hook for custom trigger patterns.',
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
guidance: false,
|
|
219
|
+
description:
|
|
220
|
+
'Use for simple text hints: use Tooltip / useTooltip instead.',
|
|
221
|
+
},
|
|
80
222
|
],
|
|
81
223
|
},
|
|
82
224
|
};
|