@astryxdesign/core 0.7.7-canary.3e09348 → 0.7.7-canary.45cc3fe

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.
Files changed (96) hide show
  1. package/dist/Dialog/Dialog.d.ts.map +1 -1
  2. package/dist/Dialog/Dialog.js +12 -11
  3. package/dist/Drawer/Drawer.d.ts.map +1 -1
  4. package/dist/Drawer/Drawer.js +15 -10
  5. package/dist/Layer/useBackdropPress.d.ts +27 -0
  6. package/dist/Layer/useBackdropPress.d.ts.map +1 -0
  7. package/dist/Layer/useBackdropPress.js +53 -0
  8. package/dist/SegmentedControl/SegmentedControl.d.ts +3 -1
  9. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  10. package/dist/SegmentedControl/SegmentedControl.js +115 -8
  11. package/dist/SegmentedControl/SegmentedControlContext.d.ts +6 -0
  12. package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
  13. package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -0
  14. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  15. package/dist/SegmentedControl/SegmentedControlItem.js +8 -1
  16. package/dist/SegmentedControl/useSegmentedSelectionMotion.d.ts +357 -0
  17. package/dist/SegmentedControl/useSegmentedSelectionMotion.d.ts.map +1 -0
  18. package/dist/SegmentedControl/useSegmentedSelectionMotion.js +1349 -0
  19. package/dist/SegmentedControl/useSegmentedTouchTracking.d.ts +70 -0
  20. package/dist/SegmentedControl/useSegmentedTouchTracking.d.ts.map +1 -0
  21. package/dist/SegmentedControl/useSegmentedTouchTracking.js +264 -0
  22. package/dist/Selector/Selector.d.ts.map +1 -1
  23. package/dist/Selector/Selector.js +96 -67
  24. package/dist/Selector/optionShape.d.ts +24 -0
  25. package/dist/Selector/optionShape.d.ts.map +1 -0
  26. package/dist/Selector/optionShape.js +48 -0
  27. package/dist/Selector/selectableOptionEntries.d.ts +20 -0
  28. package/dist/Selector/selectableOptionEntries.d.ts.map +1 -0
  29. package/dist/Selector/selectableOptionEntries.js +38 -0
  30. package/dist/Selector/utils.d.ts +2 -17
  31. package/dist/Selector/utils.d.ts.map +1 -1
  32. package/dist/Selector/utils.js +3 -51
  33. package/dist/Switch/switchTouch.d.ts +0 -7
  34. package/dist/Switch/switchTouch.d.ts.map +1 -1
  35. package/dist/Switch/switchTouch.js +2 -10
  36. package/dist/Switch/useSwitchTouch.d.ts +2 -11
  37. package/dist/Switch/useSwitchTouch.d.ts.map +1 -1
  38. package/dist/Switch/useSwitchTouch.js +26 -85
  39. package/dist/astryx.css +71 -0
  40. package/dist/gestures/GestureHost.d.ts.map +1 -1
  41. package/dist/gestures/GestureHost.js +53 -5
  42. package/dist/hooks/actionGroupTracker.d.ts +3 -3
  43. package/dist/hooks/actionGroupTracker.d.ts.map +1 -1
  44. package/dist/hooks/actionGroupTracker.js +6 -53
  45. package/dist/hooks/useHighlightedOptionScroll.d.ts.map +1 -1
  46. package/dist/hooks/useHighlightedOptionScroll.js +14 -11
  47. package/dist/hooks/useMediaQuery.d.ts +2 -1
  48. package/dist/hooks/useMediaQuery.d.ts.map +1 -1
  49. package/dist/hooks/useMediaQuery.js +7 -4
  50. package/dist/motion/roles.d.ts +7 -1
  51. package/dist/motion/roles.d.ts.map +1 -1
  52. package/dist/motion/roles.js +20 -1
  53. package/dist/motion/uikit/index.d.ts +6 -5
  54. package/dist/motion/uikit/index.d.ts.map +1 -1
  55. package/dist/motion/uikit/index.js +6 -5
  56. package/dist/motion/uikit/segmented.d.ts +55 -0
  57. package/dist/motion/uikit/segmented.d.ts.map +1 -0
  58. package/dist/motion/uikit/segmented.js +77 -0
  59. package/dist/motion/uikit/springs.d.ts +0 -18
  60. package/dist/motion/uikit/springs.d.ts.map +1 -1
  61. package/dist/motion/uikit/springs.js +5 -21
  62. package/dist/privateAttributes/segmentTitle.stylex.d.ts +5 -0
  63. package/dist/privateAttributes/segmentTitle.stylex.d.ts.map +1 -0
  64. package/dist/privateAttributes/segmentTitle.stylex.js +22 -0
  65. package/dist/utils/browserClickWait.d.ts +35 -0
  66. package/dist/utils/browserClickWait.d.ts.map +1 -0
  67. package/dist/utils/browserClickWait.js +123 -0
  68. package/package.json +4 -4
  69. package/src/Dialog/Dialog.tsx +25 -10
  70. package/src/Drawer/Drawer.tsx +27 -10
  71. package/src/Layer/useBackdropPress.ts +81 -0
  72. package/src/SegmentedControl/SegmentedControl.doc.mjs +3 -3
  73. package/src/SegmentedControl/SegmentedControl.tsx +179 -5
  74. package/src/SegmentedControl/SegmentedControlContext.ts +6 -0
  75. package/src/SegmentedControl/SegmentedControlItem.tsx +12 -1
  76. package/src/SegmentedControl/useSegmentedSelectionMotion.ts +1637 -0
  77. package/src/SegmentedControl/useSegmentedTouchTracking.ts +308 -0
  78. package/src/Selector/Selector.tsx +136 -89
  79. package/src/Selector/optionShape.ts +64 -0
  80. package/src/Selector/selectableOptionEntries.ts +39 -0
  81. package/src/Selector/utils.ts +9 -68
  82. package/src/Switch/switchTouch.ts +2 -10
  83. package/src/Switch/useSwitchTouch.ts +26 -96
  84. package/src/gestures/GestureHost.ts +73 -4
  85. package/src/hooks/actionGroupTracker.ts +12 -56
  86. package/src/hooks/useHighlightedOptionScroll.ts +15 -11
  87. package/src/hooks/useMediaQuery.doc.mjs +2 -2
  88. package/src/hooks/useMediaQuery.ts +13 -4
  89. package/src/motion/MEASUREMENTS.md +7 -0
  90. package/src/motion/README.md +1 -0
  91. package/src/motion/roles.ts +20 -1
  92. package/src/motion/uikit/index.ts +5 -5
  93. package/src/motion/uikit/segmented.ts +74 -0
  94. package/src/motion/uikit/springs.ts +5 -21
  95. package/src/privateAttributes/segmentTitle.stylex.ts +22 -0
  96. package/src/utils/browserClickWait.ts +133 -0
@@ -1 +1 @@
1
- {"version":3,"file":"Dialog.d.ts","sourceRoot":"","sources":["../../src/Dialog/Dialog.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AA4B5C,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,gBAAgB,CAAC;AAMhD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,SAAS,CAAC;AA6B9C;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,gBAAgB,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;AAEzD,4EAA4E;AAC5E,UAAU,mBAAmB;IAC3B,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAe,SAAQ,mBAAmB;IACzD,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,yEAAyE;IACzE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAuBD,2FAA2F;AAC3F,eAAO,MAAM,uCAAuC;;;;;;;;;;;CAW1C,CAAC;AAyKX;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,cAAc;;;;;EAUpE;AAED,MAAM,WAAW,WAAY,SAAQ,SAAS,CAAC,iBAAiB,CAAC;IAC/D,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACnC;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;;;;OAOG;IACH,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC;IAE3C;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAExB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAE5B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC;IAEpC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;IAExB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;IAExB;;;;OAIG;IACH,OAAO,CAAC,EAAE,WAAW,CAAC;IAEtB;;;OAGG;IACH,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,MAAM,CAAC,EACrB,MAAM,EAAE,UAAU,EAClB,QAAgB,EAChB,YAAY,EACZ,KAAW,EACX,SAAmB,EACnB,QAAQ,EACR,OAAoB,EACpB,OAAgB,EAChB,OAAO,EACP,QAAQ,EACR,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,KAAK,EACT,EAAE,WAAW,sCAoab;yBApbe,MAAM"}
1
+ {"version":3,"file":"Dialog.d.ts","sourceRoot":"","sources":["../../src/Dialog/Dialog.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AA6B5C,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,gBAAgB,CAAC;AAMhD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,SAAS,CAAC;AA+B9C;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,gBAAgB,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;AAEzD,4EAA4E;AAC5E,UAAU,mBAAmB;IAC3B,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAe,SAAQ,mBAAmB;IACzD,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,yEAAyE;IACzE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAuBD,2FAA2F;AAC3F,eAAO,MAAM,uCAAuC;;;;;;;;;;;CAW1C,CAAC;AAyKX;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,cAAc;;;;;EAUpE;AAED,MAAM,WAAW,WAAY,SAAQ,SAAS,CAAC,iBAAiB,CAAC;IAC/D,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACnC;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;;;;OAOG;IACH,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC;IAE3C;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAExB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAE5B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC;IAEpC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;IAExB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;IAExB;;;;OAIG;IACH,OAAO,CAAC,EAAE,WAAW,CAAC;IAEtB;;;OAGG;IACH,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,MAAM,CAAC,EACrB,MAAM,EAAE,UAAU,EAClB,QAAgB,EAChB,YAAY,EACZ,KAAW,EACX,SAAmB,EACnB,QAAQ,EACR,OAAoB,EACpB,OAAgB,EAChB,OAAO,EACP,QAAQ,EACR,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,KAAK,EACT,EAAE,WAAW,sCAgbb;yBAhce,MAAM"}
@@ -42,6 +42,7 @@ import { LayerContentBoundary } from "../Layer/layerScopedContext.js";
42
42
  import { layerClickBoundary } from "../Layer/layerClickBoundary.js";
43
43
  import { LayerDepthProvider } from "../Layer/LayerDepthContext.js";
44
44
  import { useLayerDismissal } from "../Layer/useLayerDismissal.js";
45
+ import { useBackdropPress } from "../Layer/useBackdropPress.js";
45
46
  import { useModalOutlet } from "../Layer/modalOutlet.js";
46
47
  import "../theme/tokens.stylex.js";
47
48
  import { colorVars, radiusVars, durationVars, easeVars, shadowVars, spacingVars } from "../theme/tokens.stylex.js";
@@ -57,12 +58,12 @@ import { useInteractionModalityTracking } from "../utils/interactionModality.js"
57
58
  import { focusForLastInput, isFocusLost } from "../utils/focusReturn.js";
58
59
  import { ModalAnnounceProvider } from "../hooks/ModalAnnounceProvider.js";
59
60
  import { useMergedRefs } from "../hooks/useMergedRefs.js";
61
+ import { jsx as _jsx } from "react/jsx-runtime";
60
62
  /**
61
63
  * Calculate a directional translate offset for dialog entry animation.
62
64
  * Returns a normalized vector from the trigger element toward the viewport
63
65
  * center, scaled to the given distance.
64
66
  */
65
- import { jsx as _jsx } from "react/jsx-runtime";
66
67
  function getDialogDirection(triggerEl, distance = 16) {
67
68
  const rect = triggerEl.getBoundingClientRect();
68
69
  // The trigger's own window: its rect is measured in that window's viewport,
@@ -538,16 +539,13 @@ export function Dialog({
538
539
  }
539
540
  }, [isOpen, isInline, hasConsumerName, titleId]);
540
541
 
541
- // Handle backdrop click — when the user clicks the ::backdrop pseudo-element,
542
- // the event target is the <dialog> element itself; clicks on child content
543
- // always target the child. This avoids false positives from native browser
542
+ // A press on the ::backdrop targets the <dialog> itself, so native browser
544
543
  // popups (date pickers, selects, color pickers) that render outside the
545
- // dialog's bounding rect.
546
- const handleClick = event => {
547
- if (event.target === event.currentTarget && allowBackdropClick) {
548
- onOpenChange(false);
549
- }
550
- };
544
+ // dialog's rect never count. The press must start and end there.
545
+ const handleBackdropPress = useCallback(() => {
546
+ onOpenChange(false);
547
+ }, [onOpenChange]);
548
+ const backdropPress = useBackdropPress(handleBackdropPress, allowBackdropClick);
551
549
 
552
550
  // The native `cancel` event is the browser's own close-watcher firing: an
553
551
  // Android back gesture, or a close request the stack never saw a press for.
@@ -638,8 +636,11 @@ export function Dialog({
638
636
  })(), isFullscreen && styles.fullscreen, isFullscreen && isOpen && styles.fullscreenOpen, opensSettled && styles.openSettled, xstyle), className, style),
639
637
  ...layerClickBoundary({
640
638
  ...safeProps,
641
- onClick: chainEventHandlers(handleClick, onClickProp)
639
+ onClick: chainEventHandlers(backdropPress.onClick, onClickProp)
642
640
  }),
641
+ onPointerDownCapture: chainEventHandlers(backdropPress.onPointerDownCapture, safeProps.onPointerDownCapture),
642
+ onPointerUpCapture: chainEventHandlers(backdropPress.onPointerUpCapture, safeProps.onPointerUpCapture),
643
+ onPointerCancelCapture: chainEventHandlers(backdropPress.onPointerCancelCapture, safeProps.onPointerCancelCapture),
643
644
  onCancel: handleCancel,
644
645
  "aria-modal": isHydrating ? undefined : 'true'
645
646
  // The default aria-labelledby (from a DialogHeader title) is set
@@ -1 +1 @@
1
- {"version":3,"file":"Drawer.d.ts","sourceRoot":"","sources":["../../src/Drawer/Drawer.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AAEH,OAAO,EAAgC,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAIpE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAQ5C,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,WAAW,CAAC;AAkB7C,OAAO,KAAK,EAAC,WAAW,EAAe,MAAM,WAAW,CAAC;AA6MzD,MAAM,WAAW,WAAY,SAAQ,SAAS,CAAC,iBAAiB,CAAC;IAC/D,iDAAiD;IACjD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAEnC;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;;;;;OAMG;IACH,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAExC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IAEvB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAExB;;;;;OAKG;IACH,YAAY,CAAC,EAAE;QACb;;;;;;WAMG;QACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;KAC/B,CAAC;IAEF;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;IAExB;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,WAAW,CAAC;IAEtB;;;OAGG;IACH,QAAQ,EAAE,SAAS,CAAC;IAEpB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,MAAM,CAAC,EACrB,MAAM,EAAE,UAAU,EAClB,YAAY,EACZ,IAAY,EACZ,KAAW,EACX,YAAY,EACZ,KAAK,EACL,QAAe,EACf,OAAgB,EAChB,OAAO,EACP,QAAQ,EACR,MAAM,EACN,SAAS,EACT,KAAK,EACL,OAAO,EAAE,WAAW,EACpB,SAAS,EAAE,aAAa,EACxB,GAAG,EACH,GAAG,KAAK,EACT,EAAE,WAAW,+BAyMb;yBA3Ne,MAAM"}
1
+ {"version":3,"file":"Drawer.d.ts","sourceRoot":"","sources":["../../src/Drawer/Drawer.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AAEH,OAAO,EAAgC,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAIpE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAQ5C,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,WAAW,CAAC;AAoB7C,OAAO,KAAK,EAAC,WAAW,EAAe,MAAM,WAAW,CAAC;AA+MzD,MAAM,WAAW,WAAY,SAAQ,SAAS,CAAC,iBAAiB,CAAC;IAC/D,iDAAiD;IACjD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAEnC;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;;;;;OAMG;IACH,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAExC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IAEvB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAExB;;;;;OAKG;IACH,YAAY,CAAC,EAAE;QACb;;;;;;WAMG;QACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;KAC/B,CAAC;IAEF;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;IAExB;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,WAAW,CAAC;IAEtB;;;OAGG;IACH,QAAQ,EAAE,SAAS,CAAC;IAEpB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,MAAM,CAAC,EACrB,MAAM,EAAE,UAAU,EAClB,YAAY,EACZ,IAAY,EACZ,KAAW,EACX,YAAY,EACZ,KAAK,EACL,QAAe,EACf,OAAgB,EAChB,OAAO,EACP,QAAQ,EACR,MAAM,EACN,SAAS,EACT,KAAK,EACL,OAAO,EAAE,WAAW,EACpB,SAAS,EAAE,aAAa,EACxB,GAAG,EACH,GAAG,KAAK,EACT,EAAE,WAAW,+BAsNb;yBAxOe,MAAM"}
@@ -70,13 +70,15 @@ import { layerTextReset } from "../Layer/layerTextReset.stylex.js";
70
70
  import { layerStructureReset } from "../Layer/layerStructureReset.stylex.js";
71
71
  import { LayerContentBoundary } from "../Layer/layerScopedContext.js";
72
72
  import { useModalOutlet } from "../Layer/modalOutlet.js";
73
+ import { useBackdropPress } from "../Layer/useBackdropPress.js";
73
74
  import { layerClickBoundary } from "../Layer/layerClickBoundary.js";
74
75
  import { ModalAnnounceProvider } from "../hooks/ModalAnnounceProvider.js";
75
76
  import { composeEventHandlers, mergeProps, themeProps } from "../utils/index.js";
77
+ import { chainEventHandlers } from "../utils/chainEventHandlers.js";
76
78
  import { container, overlayPaddingReset } from "../Layout/index.js";
77
79
  import { containerPaddingBlockEndVarStyles, containerPaddingBlockStartVarStyles, containerPaddingInlineVarStyles, paddingStyles, spacingStepToToken } from "../Layout/padding.stylex.js";
78
80
  import { useDrawerDialogPresence } from "./useDrawerDialogPresence.js";
79
-
81
+ import { jsx as _jsx } from "react/jsx-runtime";
80
82
  // =============================================================================
81
83
  // Styles
82
84
  // =============================================================================
@@ -84,7 +86,6 @@ import { useDrawerDialogPresence } from "./useDrawerDialogPresence.js";
84
86
  // Below this viewport width the drawer preserves a fixed reveal of the page
85
87
  // behind instead of growing proportionally with the viewport. 640px is the
86
88
  // repo's mobile breakpoint.
87
- import { jsx as _jsx } from "react/jsx-runtime";
88
89
  const MOBILE_BREAKPOINT = 640;
89
90
 
90
91
  // Material's established mobile drawer pattern leaves a 56dp reveal. Using
@@ -347,13 +348,12 @@ export function Drawer({
347
348
  }
348
349
  }, [allowEscape, handleDismiss, shouldDismissOnCloseRequest]);
349
350
 
350
- // Clicks on the ::backdrop target the <dialog> element itself; clicks on
351
- // drawer content always target a child (the content area fills the panel).
352
- const handleClick = useCallback(event => {
353
- if (event.target === event.currentTarget && hasScrim && allowScrimClick) {
354
- onOpenChange(false);
355
- }
356
- }, [allowScrimClick, hasScrim, onOpenChange]);
351
+ // A press on the ::backdrop targets the <dialog> itself (the content area
352
+ // fills the panel). The press must start and end there.
353
+ const handleScrimPress = useCallback(() => {
354
+ onOpenChange(false);
355
+ }, [onOpenChange]);
356
+ const scrimPress = useBackdropPress(handleScrimPress, hasScrim && allowScrimClick);
357
357
  const widthValue = typeof width === 'number' ? `${width}px` : width;
358
358
  const isFullWidthOnMobile = experimental?.isFullWidthOnMobile ?? false;
359
359
  const mobileWidth = isFullWidthOnMobile ? MOBILE_WIDTH_FULL : `min(${widthValue}, calc(100dvw - ${MOBILE_PAGE_REVEAL}px))`;
@@ -391,8 +391,13 @@ export function Drawer({
391
391
  } : undefined),
392
392
  ...layerClickBoundary({
393
393
  ...safeProps,
394
- onClick: composeEventHandlers(onClickProp, handleClick)
394
+ onClick: composeEventHandlers(onClickProp, scrimPress.onClick)
395
395
  }),
396
+ // The hook records where the press started first. The caller's capture
397
+ // handler then runs, also when a child prevented default.
398
+ onPointerDownCapture: chainEventHandlers(scrimPress.onPointerDownCapture, safeProps.onPointerDownCapture),
399
+ onPointerUpCapture: chainEventHandlers(scrimPress.onPointerUpCapture, safeProps.onPointerUpCapture),
400
+ onPointerCancelCapture: chainEventHandlers(scrimPress.onPointerCancelCapture, safeProps.onPointerCancelCapture),
396
401
  onKeyDown: onKeyDownProp,
397
402
  onCancel: handleCancel,
398
403
  children: /*#__PURE__*/_jsx(ModalAnnounceProvider, {
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @file useBackdropPress.ts
3
+ * @input A modal layer's backdrop dismissal and whether it is allowed now
4
+ * @output Capture-phase pointer handlers and a click handler for the layer's
5
+ * <dialog> root
6
+ * @position Shared backdrop-press rule for Dialog and Drawer
7
+ */
8
+ import { type PointerEvent, type MouseEvent } from 'react';
9
+ export interface BackdropPressHandlers {
10
+ onPointerDownCapture: (event: PointerEvent<HTMLElement>) => void;
11
+ onPointerUpCapture: (event: PointerEvent<HTMLElement>) => void;
12
+ onPointerCancelCapture: (event: PointerEvent<HTMLElement>) => void;
13
+ onClick: (event: MouseEvent<HTMLElement>) => void;
14
+ }
15
+ /**
16
+ * A press on a modal layer's backdrop targets the <dialog> root itself. A
17
+ * click there is also what the browser sends when a press starts on one side
18
+ * of the layer's edge and ends on the other (it goes to the nearest common
19
+ * ancestor), so a backdrop press dismisses only when its pointer-down and its
20
+ * pointer-up both landed on the backdrop. A cancelled pointer ends the press.
21
+ *
22
+ * The pointer handlers run in the capture phase. A nested layer, such as a
23
+ * popover open in the dialog, stops its presses at its own root, so a
24
+ * bubble-phase handler here does not see them.
25
+ */
26
+ export declare function useBackdropPress(onBackdropPress: () => void, isEnabled: boolean): BackdropPressHandlers;
27
+ //# sourceMappingURL=useBackdropPress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBackdropPress.d.ts","sourceRoot":"","sources":["../../src/Layer/useBackdropPress.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AAEH,OAAO,EAAsB,KAAK,YAAY,EAAE,KAAK,UAAU,EAAC,MAAM,OAAO,CAAC;AAE9E,MAAM,WAAW,qBAAqB;IACpC,oBAAoB,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IACjE,kBAAkB,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IAC/D,sBAAsB,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IACnE,OAAO,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;CACnD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,eAAe,EAAE,MAAM,IAAI,EAC3B,SAAS,EAAE,OAAO,GACjB,qBAAqB,CA6CvB"}
@@ -0,0 +1,53 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file useBackdropPress.ts
7
+ * @input A modal layer's backdrop dismissal and whether it is allowed now
8
+ * @output Capture-phase pointer handlers and a click handler for the layer's
9
+ * <dialog> root
10
+ * @position Shared backdrop-press rule for Dialog and Drawer
11
+ */
12
+ import { useCallback, useRef } from 'react';
13
+ /**
14
+ * A press on a modal layer's backdrop targets the <dialog> root itself. A
15
+ * click there is also what the browser sends when a press starts on one side
16
+ * of the layer's edge and ends on the other (it goes to the nearest common
17
+ * ancestor), so a backdrop press dismisses only when its pointer-down and its
18
+ * pointer-up both landed on the backdrop. A cancelled pointer ends the press.
19
+ *
20
+ * The pointer handlers run in the capture phase. A nested layer, such as a
21
+ * popover open in the dialog, stops its presses at its own root, so a
22
+ * bubble-phase handler here does not see them.
23
+ */
24
+ export function useBackdropPress(onBackdropPress, isEnabled) {
25
+ const startedOnBackdropRef = useRef(false);
26
+ const endedOnBackdropRef = useRef(false);
27
+ const onPointerDownCapture = useCallback(event => {
28
+ startedOnBackdropRef.current = event.target === event.currentTarget;
29
+ endedOnBackdropRef.current = false;
30
+ }, []);
31
+ const onPointerUpCapture = useCallback(event => {
32
+ endedOnBackdropRef.current = startedOnBackdropRef.current && event.target === event.currentTarget;
33
+ startedOnBackdropRef.current = false;
34
+ }, []);
35
+ const onPointerCancelCapture = useCallback(() => {
36
+ startedOnBackdropRef.current = false;
37
+ endedOnBackdropRef.current = false;
38
+ }, []);
39
+ const onClick = useCallback(event => {
40
+ const pressWasOnBackdrop = endedOnBackdropRef.current;
41
+ startedOnBackdropRef.current = false;
42
+ endedOnBackdropRef.current = false;
43
+ if (isEnabled && pressWasOnBackdrop && event.target === event.currentTarget) {
44
+ onBackdropPress();
45
+ }
46
+ }, [isEnabled, onBackdropPress]);
47
+ return {
48
+ onPointerDownCapture,
49
+ onPointerUpCapture,
50
+ onPointerCancelCapture,
51
+ onClick
52
+ };
53
+ }
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * @file SegmentedControl.tsx
3
- * @input Uses React, StyleX, SegmentedControlContext, useListFocus, useKeyboardHint
3
+ * @input Uses React, StyleX, SegmentedControlContext, useListFocus,
4
+ * useKeyboardHint, useTouchPresentation, useSegmentedSelectionMotion
4
5
  * @output Exports SegmentedControl component and SegmentedControlProps type
5
6
  * @position Container wrapper; provides context to SegmentedControlItem children
6
7
  *
@@ -8,6 +9,7 @@
8
9
  * - /packages/core/src/SegmentedControl/SegmentedControl.doc.mjs
9
10
  * - /packages/core/src/SegmentedControl/index.ts
10
11
  * - /packages/core/src/SegmentedControl/SegmentedControl.test.tsx
12
+ * - /packages/core/src/SegmentedControl/useSegmentedSelectionMotion.ts
11
13
  * - /packages/cli/assets/templates/blocks/components/SegmentedControl/ (showcase blocks)
12
14
  */
13
15
  import React, { type ReactNode } from 'react';
@@ -1 +1 @@
1
- {"version":3,"file":"SegmentedControl.d.ts","sourceRoot":"","sources":["../../src/SegmentedControl/SegmentedControl.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,EAAuB,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAQlE,OAAO,KAAK,EACV,oBAAoB,EACpB,sBAAsB,EACvB,MAAM,2BAA2B,CAAC;AAInC,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAI5C,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CACjD,SAAS,CAAC,cAAc,CAAC,EACzB,UAAU,CACX;IACC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAC5B;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,sBAAsB,CAAC;IAChC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;CACrB;AA0DD;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,GAAG,EACH,KAAK,EACL,QAAQ,EACR,KAAK,EACL,IAAI,EAAE,QAAQ,EACd,MAAc,EACd,UAAkB,EAClB,eAAe,EACf,QAAQ,EACR,MAAM,EACN,SAAS,EACT,KAAK,EACL,SAAS,EAAE,aAAa,EACxB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,UAAU,EAClB,kBAAkB,EAAE,mBAAmB,EACvC,GAAG,IAAI,EACR,EAAE,qBAAqB,qBAoJvB;yBAtKe,gBAAgB"}
1
+ {"version":3,"file":"SegmentedControl.d.ts","sourceRoot":"","sources":["../../src/SegmentedControl/SegmentedControl.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,EAA+B,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAQ1E,OAAO,KAAK,EACV,oBAAoB,EACpB,sBAAsB,EACvB,MAAM,2BAA2B,CAAC;AAInC,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAO5C,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CACjD,SAAS,CAAC,cAAc,CAAC,EACzB,UAAU,CACX;IACC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAC5B;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,sBAAsB,CAAC;IAChC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;CACrB;AAqMD;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,GAAG,EACH,KAAK,EACL,QAAQ,EACR,KAAK,EACL,IAAI,EAAE,QAAQ,EACd,MAAc,EACd,UAAkB,EAClB,eAAe,EACf,QAAQ,EACR,MAAM,EACN,SAAS,EACT,KAAK,EACL,SAAS,EAAE,aAAa,EACxB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,UAAU,EAClB,kBAAkB,EAAE,mBAAmB,EACvC,GAAG,IAAI,EACR,EAAE,qBAAqB,qBAkLvB;yBApMe,gBAAgB"}
@@ -4,7 +4,8 @@
4
4
 
5
5
  /**
6
6
  * @file SegmentedControl.tsx
7
- * @input Uses React, StyleX, SegmentedControlContext, useListFocus, useKeyboardHint
7
+ * @input Uses React, StyleX, SegmentedControlContext, useListFocus,
8
+ * useKeyboardHint, useTouchPresentation, useSegmentedSelectionMotion
8
9
  * @output Exports SegmentedControl component and SegmentedControlProps type
9
10
  * @position Container wrapper; provides context to SegmentedControlItem children
10
11
  *
@@ -12,9 +13,10 @@
12
13
  * - /packages/core/src/SegmentedControl/SegmentedControl.doc.mjs
13
14
  * - /packages/core/src/SegmentedControl/index.ts
14
15
  * - /packages/core/src/SegmentedControl/SegmentedControl.test.tsx
16
+ * - /packages/core/src/SegmentedControl/useSegmentedSelectionMotion.ts
15
17
  * - /packages/cli/assets/templates/blocks/components/SegmentedControl/ (showcase blocks)
16
18
  */
17
- import React, { useMemo, useCallback } from 'react';
19
+ import React, { useMemo, useCallback, useRef } from 'react';
18
20
  import * as stylex from '@stylexjs/stylex';
19
21
  import "../theme/tokens.stylex.js";
20
22
  import { colorVars, spacingVars, radiusVars } from "../theme/tokens.stylex.js";
@@ -28,6 +30,8 @@ import { joinAriaIDs } from "../utils/inputAria.js";
28
30
  import { useSize } from "../SizeContext/SizeContext.js";
29
31
  import { themeProps } from "../utils/themeProps.js";
30
32
  import { useMergedRefs } from "../hooks/useMergedRefs.js";
33
+ import { useTouchPresentation } from "../utils/touchPresentation.js";
34
+ import { useSegmentedSelectionMotion } from "./useSegmentedSelectionMotion.js";
31
35
  import { jsxs as _jsxs } from "react/jsx-runtime";
32
36
  // =============================================================================
33
37
  // Styles
@@ -35,6 +39,8 @@ import { jsxs as _jsxs } from "react/jsx-runtime";
35
39
 
36
40
  const styles = {
37
41
  container: {
42
+ kVAEAm: "x1n2onr6",
43
+ kHBbk8: "xc8icb0",
38
44
  k1xSpc: "x3nfvp2",
39
45
  kGNEyG: "x6s0dn4",
40
46
  kzqmXN: "xeq5yr9",
@@ -54,6 +60,87 @@ const styles = {
54
60
  disabled: {
55
61
  kSiTet: "xbyyjgo",
56
62
  $$css: true
63
+ },
64
+ lens: {
65
+ kgeoSG: "x1cpjm7i",
66
+ kEoFBp: "x1hmns74",
67
+ kxmVGU: "xy5mcqj",
68
+ k75S9Q: "x1h6x5ug",
69
+ k96MEf: "x319kzk",
70
+ kOlAkY: "xj84cd",
71
+ kgzTxl: "xlxjeq7",
72
+ kQmYQc: "xtql2tq",
73
+ kEdqc7: "x1xm9zrc",
74
+ km8f2m: "xkk1bqk",
75
+ ktHQil: "x1e91tbo",
76
+ kSdLuy: "xwrs98t",
77
+ ku0W4C: "x13rwrtk",
78
+ kLkRvE: "x1nk0e2p",
79
+ k3DiCg: "x19ph129",
80
+ kuPSpR: "xkyjh71",
81
+ k836YN: "xai7mxl",
82
+ kTP8oX: "xzr6j9d",
83
+ khXQ3S: "x1mz0nwo",
84
+ k2irxo: "xtojb3y",
85
+ ktNmms: "x19rx9s5",
86
+ kGBI9E: "x1gz1ewn",
87
+ ks091d: "xirquxt",
88
+ k9BEaU: "x1rj83xg",
89
+ kLSKZI: "x1gp8cmm",
90
+ kg8CZY: "x267z7u",
91
+ kgb78d: "xu7nluz",
92
+ kTJRYx: "xaki043",
93
+ kUmSqE: "x131tjkd",
94
+ k2L7bE: "x10hoewy",
95
+ krR4Wo: "x1rimih4",
96
+ kjBTDo: "xjmzqq9",
97
+ ktumvE: "xymdich",
98
+ kwknyK: "x1l1ofoh",
99
+ kSMQRv: "x1whvb6r",
100
+ k1cwP5: "xg5vybf",
101
+ k6d8aP: "x1j47n5p",
102
+ klC6Y0: "x11t1reg",
103
+ kFbLEI: "x1icymkt",
104
+ k5JduY: "x1s928wv",
105
+ kwXMNM: "x1j6awrg",
106
+ kDVEDD: "xi4xitw",
107
+ kH8cDV: "x1ggeerv",
108
+ k3foIR: "x3g43gi",
109
+ kkgrvl: "x1l8h5kv",
110
+ k4zj60: "x1fv2pxm",
111
+ kWgvks: "xx38sju",
112
+ k51VwD: "x8ihlwz",
113
+ kloYau: "x2q1x1w",
114
+ kMEsQN: "x1631zco",
115
+ kWkaDd: "x1y8idqa",
116
+ kU8QeM: "xwnwc82",
117
+ ks3ayO: "x1wb6yj0",
118
+ kNctxI: "x10e6nnj",
119
+ kgUb28: "x1d269y",
120
+ kakxe6: "x1pxrrzi",
121
+ kIWj2l: "x19crzk3",
122
+ kJM1pu: "x17as9s2",
123
+ kM2ZXO: "x54l34w",
124
+ kbMawA: "x1tmynux",
125
+ kgFEc0: "x13ob3d2",
126
+ kDH5la: "xbn9yse",
127
+ kBNLoZ: "x1icybqf",
128
+ kskBYc: "xk12vn8",
129
+ k9Mpue: "xsbnptc",
130
+ k6OB6z: "x1n4cxts",
131
+ kHSQhd: "x11at6vl",
132
+ kFO5lE: "x1t5rpqk",
133
+ kRcv9L: "x1ff385h",
134
+ kHi7i2: "x1tili5q",
135
+ kh0EW9: "xe87dmk",
136
+ kjCSS1: "x3sz45y",
137
+ kfzc6R: "x18pjm2d",
138
+ kc1AQg: "xa4st9h",
139
+ kvRJ0G: "x1tqw9ds",
140
+ kcGwG1: "x1pgdhoo",
141
+ kR8tOf: "xc4c6yy",
142
+ kxzRJW: "xt64kwx",
143
+ $$css: true
57
144
  }
58
145
  };
59
146
  const sizeStyles = {
@@ -107,6 +194,18 @@ export function SegmentedControl({
107
194
  ...rest
108
195
  }) {
109
196
  const size = useSize(sizeProp, 'md');
197
+ // A touch device (a coarse primary pointer) gets UIKit's motion: the
198
+ // selection slides on a spring. Under a mouse it slides on the theme's
199
+ // duration and easing.
200
+ const isTouchPresentation = useTouchPresentation().isTouch;
201
+ const trackRef = useRef(null);
202
+ // Every pointer's press is tracked. A drag from the selected segment
203
+ // carries the lens, and the lift commits the segment nearest the pointer.
204
+ // A lift outside the control commits nothing.
205
+ const {
206
+ selection,
207
+ attachTrack
208
+ } = useSegmentedSelectionMotion(trackRef, value, isTouchPresentation, !isDisabled);
110
209
 
111
210
  // Disabled-reason tooltip. Applies to the whole-group disabled state. The
112
211
  // tooltip listeners attach to the radiogroup container, which keeps pointer
@@ -145,13 +244,14 @@ export function SegmentedControl({
145
244
  isEnabled: !isDisabled
146
245
  });
147
246
  const handleContainerKeyDown = useCallback(e => {
247
+ selection.keyPressed();
148
248
  onKeyDownProp?.(e);
149
249
  if (e.defaultPrevented) {
150
250
  return;
151
251
  }
152
252
  hint.onKeyDown(e);
153
253
  handleKeyDown(e);
154
- }, [onKeyDownProp, hint, handleKeyDown]);
254
+ }, [selection, onKeyDownProp, hint, handleKeyDown]);
155
255
 
156
256
  // Selection-follows-focus (APG radiogroup): useListFocus only *moves* focus,
157
257
  // so whenever it lands focus on a new radio (arrow/Home/End, or a click) we
@@ -179,6 +279,12 @@ export function SegmentedControl({
179
279
  if (!e.currentTarget.contains(e.relatedTarget)) {
180
280
  return;
181
281
  }
282
+ // A mouse or pen press moves focus at pointerdown. A finger moves it
283
+ // after the lift, before the tap's click. The press commits through
284
+ // that click, so this focus move selects nothing.
285
+ if (selection.isPressing()) {
286
+ return;
287
+ }
182
288
  const focused = e.target?.closest('[role="radio"][data-value]');
183
289
  if (!focused || focused.getAttribute('aria-disabled') === 'true') {
184
290
  return;
@@ -187,19 +293,20 @@ export function SegmentedControl({
187
293
  if (nextValue != null && nextValue !== value) {
188
294
  onChange(nextValue);
189
295
  }
190
- }, [onFocusProp, hint, handleFocus, isDisabled, onChange, value]);
296
+ }, [onFocusProp, hint, handleFocus, isDisabled, onChange, selection, value]);
191
297
  const contextValue = useMemo(() => ({
192
298
  value,
193
299
  onChange,
194
300
  size,
195
301
  layout,
196
302
  isDisabled,
197
- disabledReasonID: showsDisabledMessage ? disabledMessageTooltip.describedBy : undefined
198
- }), [value, onChange, size, layout, isDisabled, showsDisabledMessage, disabledMessageTooltip.describedBy]);
303
+ disabledReasonID: showsDisabledMessage ? disabledMessageTooltip.describedBy : undefined,
304
+ isTouchPresentation
305
+ }), [value, onChange, size, layout, isDisabled, showsDisabledMessage, disabledMessageTooltip.describedBy, isTouchPresentation]);
199
306
  return /*#__PURE__*/_jsxs(SegmentedControlContext, {
200
307
  value: contextValue,
201
308
  children: [/*#__PURE__*/_jsxs("div", {
202
- ref: useMergedRefs(ref, listRef, disabledMessageTooltip.ref),
309
+ ref: useMergedRefs(ref, listRef, trackRef, attachTrack, disabledMessageTooltip.ref),
203
310
  ...rest,
204
311
  role: "radiogroup",
205
312
  "aria-label": label,
@@ -212,7 +319,7 @@ export function SegmentedControl({
212
319
  onBlur: composeEventHandlers(onBlurProp, hint.onBlur),
213
320
  ...mergeProps(themeProps('segmented-control', {
214
321
  size
215
- }), stylex.props(styles.container, sizeStyles[size], layout === 'fill' && styles.fill, isDisabled && styles.disabled, xstyle), className, style),
322
+ }), stylex.props(styles.container, sizeStyles[size], layout === 'fill' && styles.fill, (isTouchPresentation || !isDisabled) && styles.lens, isDisabled && styles.disabled, xstyle), className, style),
216
323
  children: [children, hint.hintElement]
217
324
  }), showsDisabledMessage && disabledMessageTooltip.renderTooltip(disabledMessage)]
218
325
  });
@@ -12,6 +12,12 @@ export interface SegmentedControlContextValue {
12
12
  * described by the reason (spec:AST-085 FR5).
13
13
  */
14
14
  disabledReasonID?: string;
15
+ /**
16
+ * True in the touch presentation (under a finger, `useTouchPresentation`):
17
+ * the control animates selection changes itself (the surface slides), so
18
+ * segments drop their CSS transitions.
19
+ */
20
+ isTouchPresentation?: boolean;
15
21
  }
16
22
  export declare const SegmentedControlContext: import("react").Context<SegmentedControlContextValue | null>;
17
23
  export declare function useSegmentedControlContext(): SegmentedControlContextValue;
@@ -1 +1 @@
1
- {"version":3,"file":"SegmentedControlContext.d.ts","sourceRoot":"","sources":["../../src/SegmentedControl/SegmentedControlContext.ts"],"names":[],"mappings":"AAgBA,MAAM,MAAM,oBAAoB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACtD,MAAM,MAAM,sBAAsB,GAAG,KAAK,GAAG,MAAM,CAAC;AAEpD,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,IAAI,EAAE,oBAAoB,CAAC;IAC3B,MAAM,EAAE,sBAAsB,CAAC;IAC/B,UAAU,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,eAAO,MAAM,uBAAuB,8DACsB,CAAC;AAG3D,wBAAgB,0BAA0B,IAAI,4BAA4B,CASzE"}
1
+ {"version":3,"file":"SegmentedControlContext.d.ts","sourceRoot":"","sources":["../../src/SegmentedControl/SegmentedControlContext.ts"],"names":[],"mappings":"AAgBA,MAAM,MAAM,oBAAoB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACtD,MAAM,MAAM,sBAAsB,GAAG,KAAK,GAAG,MAAM,CAAC;AAEpD,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,IAAI,EAAE,oBAAoB,CAAC;IAC3B,MAAM,EAAE,sBAAsB,CAAC;IAC/B,UAAU,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,eAAO,MAAM,uBAAuB,8DACsB,CAAC;AAG3D,wBAAgB,0BAA0B,IAAI,4BAA4B,CASzE"}
@@ -5,6 +5,7 @@
5
5
  * @position Child item; renders as a radio button within the segmented control
6
6
  *
7
7
  * SYNC: When modified, update:
8
+ * - /packages/core/src/SegmentedControl/useSegmentedSelectionMotion.ts (the title marker)
8
9
  * - /packages/core/src/SegmentedControl/SegmentedControl.doc.mjs
9
10
  * - /packages/core/src/SegmentedControl/index.ts
10
11
  * - /packages/core/src/SegmentedControl/SegmentedControl.test.tsx
@@ -1 +1 @@
1
- {"version":3,"file":"SegmentedControlItem.d.ts","sourceRoot":"","sources":["../../src/SegmentedControl/SegmentedControlItem.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,EAAC,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAiB5C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAO5C,MAAM,WAAW,yBAA0B,SAAQ,SAAS,CAAC,iBAAiB,CAAC;IAC7E,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACnC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAgHD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,GAAG,EACH,KAAK,EACL,KAAK,EACL,aAAqB,EACrB,IAAI,EACJ,UAAkB,EAClB,OAAO,EAAE,WAAW,EACpB,MAAM,EACN,kBAAkB,EAAE,mBAAmB,EACvC,GAAG,IAAI,EACR,EAAE,yBAAyB,qBA2E3B;yBAtFe,oBAAoB"}
1
+ {"version":3,"file":"SegmentedControlItem.d.ts","sourceRoot":"","sources":["../../src/SegmentedControl/SegmentedControlItem.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,EAAC,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAkB5C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAO5C,MAAM,WAAW,yBAA0B,SAAQ,SAAS,CAAC,iBAAiB,CAAC;IAC7E,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACnC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAsHD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,GAAG,EACH,KAAK,EACL,KAAK,EACL,aAAqB,EACrB,IAAI,EACJ,UAAkB,EAClB,OAAO,EAAE,WAAW,EACpB,MAAM,EACN,kBAAkB,EAAE,mBAAmB,EACvC,GAAG,IAAI,EACR,EAAE,yBAAyB,qBA8E3B;yBAzFe,oBAAoB"}
@@ -9,6 +9,7 @@
9
9
  * @position Child item; renders as a radio button within the segmented control
10
10
  *
11
11
  * SYNC: When modified, update:
12
+ * - /packages/core/src/SegmentedControl/useSegmentedSelectionMotion.ts (the title marker)
12
13
  * - /packages/core/src/SegmentedControl/SegmentedControl.doc.mjs
13
14
  * - /packages/core/src/SegmentedControl/index.ts
14
15
  * - /packages/core/src/SegmentedControl/SegmentedControl.test.tsx
@@ -17,6 +18,7 @@
17
18
  import React from 'react';
18
19
  import { joinAriaIDs } from "../utils/inputAria.js";
19
20
  import { segmentDisabledProps } from "../privateAttributes/segmentDisabled.stylex.js";
21
+ import { segmentTitleProps } from "../privateAttributes/segmentTitle.stylex.js";
20
22
  import * as stylex from '@stylexjs/stylex';
21
23
  import "../theme/tokens.stylex.js";
22
24
  import { colorVars, spacingVars, sizeVars, durationVars, easeVars, fontWeightVars, shadowVars, typeScaleVars } from "../theme/tokens.stylex.js";
@@ -85,6 +87,10 @@ const styles = {
85
87
  kjj79g: "xl56j7k",
86
88
  kmuXW: "x2lah0s",
87
89
  $$css: true
90
+ },
91
+ touchPresentation: {
92
+ k1ekBW: "x13b0p5u",
93
+ $$css: true
88
94
  }
89
95
  };
90
96
  const CONCENTRIC_RADIUS = 'max(0px, calc(var(--_segmented-control-radius) - var(--_segmented-control-padding)))';
@@ -198,8 +204,9 @@ export function SegmentedControlItem({
198
204
  }), focusOutlineProps.focusVisible(styles.base, sizeStyles[size], isFill && styles.fill, isLabelHidden && styles.iconOnly, isSelected && styles.selected,
199
205
  // The shared hover and pressed overlay, on the segments a press can
200
206
  // change: the selected segment keeps its raised surface as it is.
201
- !isSelected && !isItemDisabled && interactionOverlayStyles.backgroundColor, isItemDisabled && styles.disabled, xstyle)),
207
+ !isSelected && !isItemDisabled && interactionOverlayStyles.backgroundColor, isItemDisabled && styles.disabled, ctx.isTouchPresentation === true && styles.touchPresentation, xstyle)),
202
208
  children: [iconElement, !isLabelHidden && /*#__PURE__*/_jsx("span", {
209
+ ...segmentTitleProps,
203
210
  ...{
204
211
  className: "xb3r6kr xlyipyv xeuugli"
205
212
  },