@astryxdesign/core 0.7.7-canary.fc68123 → 0.7.7

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 (240) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/dist/AlertDialog/AlertDialog.d.ts +1 -4
  3. package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
  4. package/dist/AlertDialog/AlertDialog.js +6 -36
  5. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  6. package/dist/BottomSheet/BottomSheetPanel.js +4 -23
  7. package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
  8. package/dist/BottomSheet/useSheetGestures.js +1 -3
  9. package/dist/Button/Button.d.ts.map +1 -1
  10. package/dist/Button/Button.js +1 -17
  11. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  12. package/dist/CheckboxInput/CheckboxInput.js +2 -13
  13. package/dist/ComplexSelector/ComplexSelector.d.ts +1 -4
  14. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  15. package/dist/ComplexSelector/ComplexSelector.js +4 -14
  16. package/dist/ContextMenu/ContextMenu.js +2 -2
  17. package/dist/Dialog/Dialog.d.ts.map +1 -1
  18. package/dist/Dialog/Dialog.js +31 -7
  19. package/dist/Field/Field.js +2 -2
  20. package/dist/HoverCard/HoverCard.d.ts +3 -4
  21. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  22. package/dist/HoverCard/HoverCard.js +2 -27
  23. package/dist/HoverCard/useHoverCard.d.ts +4 -5
  24. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  25. package/dist/HoverCard/useHoverCard.js +31 -17
  26. package/dist/Icon/Icon.d.ts +1 -5
  27. package/dist/Icon/Icon.d.ts.map +1 -1
  28. package/dist/Icon/Icon.js +2 -5
  29. package/dist/Layer/usePressPin.d.ts +5 -17
  30. package/dist/Layer/usePressPin.d.ts.map +1 -1
  31. package/dist/Layer/usePressPin.js +12 -62
  32. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  33. package/dist/Lightbox/Lightbox.js +0 -18
  34. package/dist/PowerSearch/PowerSearch.d.ts +3 -36
  35. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  36. package/dist/PowerSearch/PowerSearch.js +127 -433
  37. package/dist/PowerSearch/PowerSearchEditPopover.d.ts +1 -32
  38. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  39. package/dist/PowerSearch/PowerSearchEditPopover.js +6 -17
  40. package/dist/PowerSearch/PowerSearchValueEditor.d.ts +3 -38
  41. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  42. package/dist/PowerSearch/PowerSearchValueEditor.js +61 -170
  43. package/dist/PowerSearch/formatFilterValue.d.ts +0 -7
  44. package/dist/PowerSearch/formatFilterValue.d.ts.map +1 -1
  45. package/dist/PowerSearch/formatFilterValue.js +1 -11
  46. package/dist/RadioList/RadioList.d.ts.map +1 -1
  47. package/dist/RadioList/RadioList.js +2 -3
  48. package/dist/RadioList/RadioListItem.d.ts +1 -1
  49. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  50. package/dist/RadioList/RadioListItem.js +3 -5
  51. package/dist/Resizable/ResizeHandle.d.ts +1 -2
  52. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  53. package/dist/Resizable/ResizeHandle.js +77 -99
  54. package/dist/SegmentedControl/useSegmentedSelectionMotion.d.ts.map +1 -1
  55. package/dist/SegmentedControl/useSegmentedSelectionMotion.js +3 -3
  56. package/dist/Slider/sliderTouchController.js +2 -2
  57. package/dist/Spinner/Spinner.d.ts +1 -1
  58. package/dist/Spinner/Spinner.d.ts.map +1 -1
  59. package/dist/Spinner/Spinner.js +58 -60
  60. package/dist/Switch/useSwitchTouch.js +4 -4
  61. package/dist/Table/BaseTable.d.ts.map +1 -1
  62. package/dist/Table/BaseTable.js +8 -29
  63. package/dist/TextInput/TextInput.d.ts +2 -2
  64. package/dist/TextInput/TextInput.d.ts.map +1 -1
  65. package/dist/TextInput/TextInput.js +7 -10
  66. package/dist/Toast/Toast.d.ts +1 -11
  67. package/dist/Toast/Toast.d.ts.map +1 -1
  68. package/dist/Toast/Toast.js +15 -25
  69. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  70. package/dist/Toast/ToastViewport.js +34 -94
  71. package/dist/Toast/types.d.ts +3 -9
  72. package/dist/Toast/types.d.ts.map +1 -1
  73. package/dist/Tooltip/useTooltip.d.ts +1 -19
  74. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  75. package/dist/Tooltip/useTooltip.js +53 -34
  76. package/dist/Typeahead/TypeaheadItem.d.ts +1 -2
  77. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  78. package/dist/astryx.css +6 -35
  79. package/dist/gestures/PressTracker.d.ts.map +1 -1
  80. package/dist/gestures/PressTracker.js +3 -4
  81. package/dist/hooks/actionGroupTracker.d.ts +1 -4
  82. package/dist/hooks/actionGroupTracker.d.ts.map +1 -1
  83. package/dist/hooks/actionGroupTracker.js +3 -13
  84. package/dist/hooks/index.d.ts +0 -1
  85. package/dist/hooks/index.d.ts.map +1 -1
  86. package/dist/hooks/index.js +0 -1
  87. package/dist/hooks/useAttachment.d.ts +1 -1
  88. package/dist/hooks/useAttachment.js +1 -1
  89. package/dist/hooks/useInputStatusIcon.d.ts +1 -1
  90. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -1
  91. package/dist/hooks/useInputStatusIcon.js +2 -2
  92. package/dist/hooks/useLongPress.d.ts +2 -6
  93. package/dist/hooks/useLongPress.d.ts.map +1 -1
  94. package/dist/hooks/useLongPress.js +15 -95
  95. package/dist/i18n/generated-english/PowerSearch/PowerSearchValueEditor.generated.d.ts.map +1 -1
  96. package/dist/i18n/generated-english/PowerSearch/PowerSearchValueEditor.generated.js +0 -6
  97. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  98. package/dist/i18n/generated-locales/en.generated.js +0 -30
  99. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  100. package/dist/i18n/generated-locales/pseudo.generated.js +0 -30
  101. package/dist/index.d.ts +0 -1
  102. package/dist/index.d.ts.map +1 -1
  103. package/dist/index.js +0 -4
  104. package/dist/motion/animatedLayout.d.ts.map +1 -1
  105. package/dist/motion/animatedLayout.js +6 -5
  106. package/dist/motion/animator.d.ts +14 -14
  107. package/dist/motion/animator.d.ts.map +1 -1
  108. package/dist/motion/animator.js +18 -41
  109. package/dist/utils/dateParser.d.ts.map +1 -1
  110. package/dist/utils/dateParser.js +0 -12
  111. package/dist/utils/focusReturn.d.ts +0 -15
  112. package/dist/utils/focusReturn.d.ts.map +1 -1
  113. package/dist/utils/focusReturn.js +0 -28
  114. package/dist/utils/interactionOverlay.stylex.d.ts +2 -3
  115. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  116. package/dist/utils/interactionOverlay.stylex.js +2 -3
  117. package/dist/utils/pressFeedback.d.ts.map +1 -1
  118. package/dist/utils/pressFeedback.js +6 -19
  119. package/locales/en.json +0 -120
  120. package/locales/pseudo.json +0 -90
  121. package/package.json +5 -10
  122. package/src/AlertDialog/AlertDialog.doc.mjs +2 -2
  123. package/src/AlertDialog/AlertDialog.tsx +5 -37
  124. package/src/BottomSheet/BottomSheetPanel.tsx +3 -37
  125. package/src/BottomSheet/useSheetGestures.ts +1 -3
  126. package/src/Button/Button.tsx +4 -26
  127. package/src/CheckboxInput/CheckboxInput.doc.mjs +0 -18
  128. package/src/CheckboxInput/CheckboxInput.tsx +9 -36
  129. package/src/ComplexSelector/ComplexSelector.tsx +5 -20
  130. package/src/ContextMenu/ContextMenu.tsx +2 -2
  131. package/src/Dialog/Dialog.tsx +31 -10
  132. package/src/Field/Field.tsx +2 -2
  133. package/src/HoverCard/HoverCard.doc.mjs +2 -4
  134. package/src/HoverCard/HoverCard.tsx +4 -35
  135. package/src/HoverCard/useHoverCard.doc.mjs +36 -178
  136. package/src/HoverCard/useHoverCard.tsx +52 -46
  137. package/src/Icon/Icon.doc.mjs +8 -49
  138. package/src/Icon/Icon.tsx +7 -15
  139. package/src/Layer/usePressPin.ts +15 -81
  140. package/src/Lightbox/Lightbox.tsx +0 -18
  141. package/src/PowerSearch/PowerSearch.doc.mjs +5 -54
  142. package/src/PowerSearch/PowerSearch.tsx +155 -609
  143. package/src/PowerSearch/PowerSearchEditPopover.tsx +6 -28
  144. package/src/PowerSearch/PowerSearchValueEditor.tsx +38 -203
  145. package/src/PowerSearch/formatFilterValue.ts +1 -18
  146. package/src/RadioList/RadioList.tsx +2 -3
  147. package/src/RadioList/RadioListItem.tsx +3 -5
  148. package/src/Resizable/Resizable.doc.mjs +4 -5
  149. package/src/Resizable/ResizeHandle.tsx +109 -167
  150. package/src/SegmentedControl/useSegmentedSelectionMotion.ts +3 -6
  151. package/src/Slider/sliderTouchController.ts +2 -2
  152. package/src/Spinner/Spinner.tsx +63 -77
  153. package/src/Switch/useSwitchTouch.ts +4 -4
  154. package/src/Table/BaseTable.tsx +12 -46
  155. package/src/TextInput/TextInput.tsx +10 -11
  156. package/src/Toast/Toast.doc.mjs +3 -4
  157. package/src/Toast/Toast.tsx +32 -52
  158. package/src/Toast/ToastViewport.tsx +35 -121
  159. package/src/Toast/types.ts +3 -9
  160. package/src/Tooltip/useTooltip.doc.mjs +0 -24
  161. package/src/Tooltip/useTooltip.tsx +56 -61
  162. package/src/Typeahead/TypeaheadItem.doc.mjs +3 -6
  163. package/src/Typeahead/TypeaheadItem.tsx +1 -2
  164. package/src/gestures/PressTracker.ts +3 -4
  165. package/src/gestures/README.md +1 -7
  166. package/src/hooks/actionGroupTracker.ts +5 -20
  167. package/src/hooks/index.ts +0 -2
  168. package/src/hooks/useAttachment.ts +1 -1
  169. package/src/hooks/useInputStatusIcon.tsx +2 -3
  170. package/src/hooks/useLongPress.doc.mjs +2 -2
  171. package/src/hooks/useLongPress.ts +15 -136
  172. package/src/i18n/generated-english/PowerSearch/PowerSearchValueEditor.generated.ts +0 -6
  173. package/src/i18n/generated-locales/en.generated.ts +0 -41
  174. package/src/i18n/generated-locales/pseudo.generated.ts +0 -42
  175. package/src/index.ts +0 -13
  176. package/src/motion/README.md +5 -9
  177. package/src/motion/animatedLayout.ts +4 -5
  178. package/src/motion/animator.ts +29 -61
  179. package/src/utils/dateParser.ts +0 -16
  180. package/src/utils/focusReturn.ts +0 -30
  181. package/src/utils/interactionOverlay.stylex.ts +2 -3
  182. package/src/utils/pressFeedback.ts +6 -19
  183. package/dist/PowerSearch/PowerSearchBottomSheet.d.ts +0 -100
  184. package/dist/PowerSearch/PowerSearchBottomSheet.d.ts.map +0 -1
  185. package/dist/PowerSearch/PowerSearchBottomSheet.js +0 -1913
  186. package/dist/PowerSearch/formatTokenValue.d.ts +0 -28
  187. package/dist/PowerSearch/formatTokenValue.d.ts.map +0 -1
  188. package/dist/PowerSearch/formatTokenValue.js +0 -160
  189. package/dist/PowerSearch/powerSearchSheetUtils.d.ts +0 -65
  190. package/dist/PowerSearch/powerSearchSheetUtils.d.ts.map +0 -1
  191. package/dist/PowerSearch/powerSearchSheetUtils.js +0 -168
  192. package/dist/animator/createAnimator.d.ts +0 -98
  193. package/dist/animator/createAnimator.d.ts.map +0 -1
  194. package/dist/animator/createAnimator.js +0 -259
  195. package/dist/hooks/pressGroupContext.d.ts +0 -6
  196. package/dist/hooks/pressGroupContext.d.ts.map +0 -1
  197. package/dist/hooks/pressGroupContext.js +0 -20
  198. package/dist/hooks/useActionGroupTracking.d.ts +0 -53
  199. package/dist/hooks/useActionGroupTracking.d.ts.map +0 -1
  200. package/dist/hooks/useActionGroupTracking.js +0 -174
  201. package/dist/hooks/useAnimator.d.ts +0 -41
  202. package/dist/hooks/useAnimator.d.ts.map +0 -1
  203. package/dist/hooks/useAnimator.js +0 -70
  204. package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.d.ts +0 -8
  205. package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.d.ts.map +0 -1
  206. package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.js +0 -42
  207. package/dist/i18n/generated-english/PowerSearch/formatTokenValue.generated.d.ts +0 -8
  208. package/dist/i18n/generated-english/PowerSearch/formatTokenValue.generated.d.ts.map +0 -1
  209. package/dist/i18n/generated-english/PowerSearch/formatTokenValue.generated.js +0 -14
  210. package/dist/internal/indicatorPress.stylex.d.ts +0 -20
  211. package/dist/internal/indicatorPress.stylex.d.ts.map +0 -1
  212. package/dist/internal/indicatorPress.stylex.js +0 -26
  213. package/dist/motion/progress.d.ts +0 -88
  214. package/dist/motion/progress.d.ts.map +0 -1
  215. package/dist/motion/progress.js +0 -128
  216. package/dist/motion/uikit/alert.d.ts +0 -41
  217. package/dist/motion/uikit/alert.d.ts.map +0 -1
  218. package/dist/motion/uikit/alert.js +0 -43
  219. package/dist/privateAttributes/pressGroupItem.stylex.d.ts +0 -15
  220. package/dist/privateAttributes/pressGroupItem.stylex.d.ts.map +0 -1
  221. package/dist/privateAttributes/pressGroupItem.stylex.js +0 -23
  222. package/dist/utils/triggerFocus.d.ts +0 -36
  223. package/dist/utils/triggerFocus.d.ts.map +0 -1
  224. package/dist/utils/triggerFocus.js +0 -65
  225. package/src/PowerSearch/PowerSearchBottomSheet.tsx +0 -2515
  226. package/src/PowerSearch/formatTokenValue.ts +0 -215
  227. package/src/PowerSearch/powerSearchSheetUtils.ts +0 -224
  228. package/src/animator/createAnimator.doc.mjs +0 -153
  229. package/src/animator/createAnimator.ts +0 -324
  230. package/src/hooks/pressGroupContext.ts +0 -21
  231. package/src/hooks/useActionGroupTracking.ts +0 -224
  232. package/src/hooks/useAnimator.doc.mjs +0 -109
  233. package/src/hooks/useAnimator.ts +0 -67
  234. package/src/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.ts +0 -43
  235. package/src/i18n/generated-english/PowerSearch/formatTokenValue.generated.ts +0 -15
  236. package/src/internal/indicatorPress.stylex.ts +0 -24
  237. package/src/motion/progress.ts +0 -155
  238. package/src/motion/uikit/alert.ts +0 -43
  239. package/src/privateAttributes/pressGroupItem.stylex.ts +0 -23
  240. package/src/utils/triggerFocus.ts +0 -80
@@ -26,14 +26,12 @@ import {RadioListContext} from './RadioList';
26
26
  import {RadioListReasonContext} from './RadioListReasonContext';
27
27
  import {joinAriaIDs} from '../utils/inputAria';
28
28
  import {colorVars, radiusVars} from '../theme/tokens.stylex';
29
- import {isRenderable} from '../utils/isRenderable';
30
- import {mergeProps} from '../utils/mergeProps';
31
- import {rtlStyles} from '../utils/rtlStyles';
29
+ import {mergeProps, isRenderable, rtlStyles} from '../utils';
32
30
  import {indicatorScope} from '../Indicator/indicator.markers.stylex';
33
31
  import {useIndicatorFocusRing} from '../hooks/useIndicatorFocusRing';
34
32
  import {usePressFeedback} from '../hooks/usePressFeedback';
35
- import {useIndicator} from '../Indicator/useIndicator';
36
- import {Item, type ItemAlign} from '../Item/Item';
33
+ import {useIndicator} from '../Indicator';
34
+ import {Item, type ItemAlign} from '../Item';
37
35
  import {themeProps} from '../utils/themeProps';
38
36
 
39
37
  // The touch press's paint, declared by the shared overlay styles on the
@@ -12,13 +12,13 @@ const anatomy = [
12
12
  name: 'Grip pill',
13
13
  required: false,
14
14
  description:
15
- 'Default visible grip indicator; dragging it resizes, wherever it sits. Custom handle content can replace it.',
15
+ 'Default visible grip indicator; custom handle content can replace it.',
16
16
  },
17
17
  {
18
18
  name: 'Grab zone',
19
19
  required: true,
20
20
  description:
21
- 'Invisible enlarged pointer region centred on the divider. An inline zone takes at most half of a pane shorter than it; an overlay handle takes at most half of its pane.',
21
+ 'Invisible enlarged pointer region aligned with the grip or divider.',
22
22
  },
23
23
  ];
24
24
 
@@ -224,8 +224,7 @@ const region = useResizable({
224
224
  description:
225
225
  'Which side of the divider the pill sits on. ' +
226
226
  'auto = content side (derived from isReversed), flips when collapsed. ' +
227
- 'start = left/top, end = right/bottom, center = centered on divider. ' +
228
- 'It moves only the pill, which a drag can still take wherever it sits; the grab zone stays centred on the divider.',
227
+ 'start = left/top, end = right/bottom, center = centered on divider.',
229
228
  default: "'auto'",
230
229
  },
231
230
  {
@@ -318,7 +317,7 @@ export const docsDense = {
318
317
  isAlwaysVisible:
319
318
  'show pill grip at rest instead of only on hover. Use when discoverability important',
320
319
  pillPlacement:
321
- 'which side of divider pill sits on. auto = content side (derived from isReversed), flips when collapsed; start = left/top, end = right/bottom, center = centered on divider. Moves only the pill (still draggable); grab zone stays on the divider',
320
+ 'which side of divider pill sits on. auto = content side (derived from isReversed), flips when collapsed; start = left/top, end = right/bottom, center = centered on divider',
322
321
  label: 'accessible label for separator',
323
322
  resizable: 'resize props from useResizable: connects handle to panel',
324
323
  },
@@ -8,12 +8,29 @@
8
8
  * @output Styled drag handle with WAI-ARIA separator role and keyboard support
9
9
  * @position Between resizable panels; consumed directly by builders
10
10
  *
11
- * A drag takes pointer capture on the element it starts on, so moves over an
12
- * embedded frame still arrive.
11
+ * The handle element is 1px wide (the divider line itself), with an
12
+ * absolutely-positioned wider hit area for pointer interaction.
13
+ * Pill grip indicator can sit on either side of the divider (or centered)
14
+ * via pillPlacement. Default 'auto' places the pill on the panel side and
15
+ * flips when the panel collapses to 0px so it stays accessible.
16
+ *
17
+ * Pill placement uses a single stylex dynamic style that accepts a direction
18
+ * multiplier (-1 or 1). The pill element has its own themeProps
19
+ * ('resize-handle-pill') so themes can target size/shape directly.
20
+ *
21
+ * While the panel is collapsed, aria-valuenow is clamped to aria-valuemin
22
+ * (a value below the minimum is invalid per WCAG 4.1.2) and a localized
23
+ * "Collapsed" aria-valuetext announces the real state.
24
+ *
25
+ * The handle's input (the pointer drag, the keys, the double-click) is
26
+ * resizeGesture.ts. ResizeHandle forwards its React events to it, over the
27
+ * region its `resizable` prop describes. A drag takes pointer capture on the grab zone.
28
+ * The gesture's move, up and cancel events then go there, as in Slider, even
29
+ * over an embedded frame.
13
30
  */
14
31
 
15
32
  import {useEffect, useRef, useState} from 'react';
16
- import type {HTMLAttributes, ReactNode} from 'react';
33
+ import type {ReactNode} from 'react';
17
34
  import * as stylex from '@stylexjs/stylex';
18
35
  import type {BaseProps} from '../BaseProps';
19
36
  import {
@@ -64,6 +81,30 @@ function resolveEffectiveSide(
64
81
  return panelSide;
65
82
  }
66
83
 
84
+ /**
85
+ * Hit-area inline bias, expressed as a fraction of the pill's per-side offset.
86
+ *
87
+ * The visible pill is placed by `pillOffsetX`/`pillOffsetY`: anchored at the
88
+ * divider's inline-start edge and pushed one grip-width-plus-gap toward the
89
+ * panel side via a PHYSICAL translate (dir −1 = start, +1 = end). Because that
90
+ * offset is physical, the pill sits on the same physical side of the divider in
91
+ * both LTR and RTL. The grab zone must sit OVER that pill, so it has to use the
92
+ * exact same anchor + physical-offset construction as the pill (just wider) —
93
+ * anchoring the hit area at the divider centre (`insetInlineStart: 50%`) and
94
+ * biasing with a percentage translate mixes a logical anchor (which flips under
95
+ * RTL) with a physical translate (which doesn't), leaving the grab zone stranded
96
+ * to one side under RTL. Returning the pill's `dir` here lets the hit area reuse
97
+ * `pillOffsetX`/`pillOffsetY` so the two elements are positioned identically.
98
+ */
99
+ function hitAreaBiasDir(
100
+ effectiveSide: 'start' | 'end' | 'center',
101
+ ): number | null {
102
+ if (effectiveSide === 'center') {
103
+ return null;
104
+ }
105
+ return effectiveSide === 'start' ? -1 : 1;
106
+ }
107
+
67
108
  const styles = stylex.create({
68
109
  handle: {
69
110
  position: 'relative',
@@ -96,12 +137,6 @@ const styles = stylex.create({
96
137
  insetInlineEnd: 0,
97
138
  height: 'var(--resize-handle-hit-area, 16px)',
98
139
  },
99
- overlayHorizontalOnShortPane: {
100
- width: 'min(var(--resize-handle-hit-area, 16px), 50%)',
101
- },
102
- overlayVerticalOnShortPane: {
103
- height: 'min(var(--resize-handle-hit-area, 16px), 50%)',
104
- },
105
140
  horizontal: {
106
141
  width: 1,
107
142
  height: '100%',
@@ -161,27 +196,19 @@ const styles = stylex.create({
161
196
  ':is(:disabled,[aria-disabled="true"])': 'default',
162
197
  },
163
198
  },
164
- // The grab zone sits centred on the Handle whatever `pillPlacement` says,
165
- // which only places the pill. Inline centering comes from
166
- // rtlStyles.centerInline at the call site (correct in LTR and RTL); this
167
- // block owns only the block axis.
199
+ // Centered grab zone (pillPlacement 'center' / no bias): sit the hit area on
200
+ // the divider itself. Inline centering comes from rtlStyles.centerInline at
201
+ // the call site (correct in LTR and RTL); this block owns only the block axis.
168
202
  hitAreaCenteredY: {
169
203
  insetBlockStart: '50%',
170
204
  transform: 'translateY(-50%)',
171
205
  },
172
- hitAreaWithinHandleX: {
173
- width: `min(${spacingVars['--spacing-4']}, 100%)`,
174
- },
175
- hitAreaWithinHandleY: {
176
- height: `min(${spacingVars['--spacing-4']}, 100%)`,
177
- },
178
206
 
179
207
  // Pill base — themes target .astryx-resize-handle-pill for size/shape.
180
208
  pill: {
181
209
  position: 'absolute',
182
210
  zIndex: 2,
183
- touchAction: 'none',
184
- userSelect: 'none',
211
+ pointerEvents: 'none',
185
212
  borderRadius: radiusVars['--radius-full'],
186
213
  backgroundColor: colorVars['--color-border'],
187
214
  transitionProperty: 'opacity, background-color, transform',
@@ -189,18 +216,6 @@ const styles = stylex.create({
189
216
  transitionTimingFunction: easeVars['--ease-standard'],
190
217
  top: '50%',
191
218
  },
192
- pillGrabHorizontal: {
193
- cursor: {
194
- default: 'col-resize',
195
- ':is(:disabled,[aria-disabled="true"])': 'default',
196
- },
197
- },
198
- pillGrabVertical: {
199
- cursor: {
200
- default: 'row-resize',
201
- ':is(:disabled,[aria-disabled="true"])': 'default',
202
- },
203
- },
204
219
  pillHorizontal: {
205
220
  width: 3,
206
221
  height: spacingVars['--spacing-8'],
@@ -209,93 +224,59 @@ const styles = stylex.create({
209
224
  width: spacingVars['--spacing-8'],
210
225
  height: 3,
211
226
  },
212
- // A hidden pill paints nothing, so it takes no pointer either.
213
- pillHidden: {opacity: 0, pointerEvents: 'none'},
227
+ pillHidden: {opacity: 0},
214
228
  pillVisible: {opacity: 1},
215
229
  pillHover: {
216
230
  opacity: 1,
217
- pointerEvents: 'auto',
218
231
  backgroundColor: colorVars['--color-border'],
219
232
  },
220
233
  pillActive: {
221
234
  opacity: 1,
222
- pointerEvents: 'auto',
223
235
  backgroundColor: colorVars['--color-border-emphasized'],
224
236
  },
225
- // A pill shown on hover can sit outside the Grab zone. This invisible
226
- // extension spans the gap toward the Handle, so the pointer keeps the hover
227
- // on its way to the pill. It reaches no further than the pill, and it takes
228
- // the pointer only while the pill does. The pill's offset is a physical
229
- // translate, so the side toward the Handle is physical too.
230
- pillBridgeRight: {
231
- '::before': {
232
- content: '""',
233
- position: 'absolute',
234
- insetBlock: 0,
235
- insetInlineStart: {
236
- default: 0,
237
- ':is([dir="rtl"] *)': `calc(-1 * ${spacingVars['--spacing-1']} - 1px)`,
238
- },
239
- insetInlineEnd: {
240
- default: `calc(-1 * ${spacingVars['--spacing-1']} - 1px)`,
241
- ':is([dir="rtl"] *)': 0,
242
- },
243
- },
244
- },
245
- pillBridgeLeft: {
246
- '::before': {
247
- content: '""',
248
- position: 'absolute',
249
- insetBlock: 0,
250
- insetInlineStart: {
251
- default: `calc(-1 * ${spacingVars['--spacing-1']} - 1px)`,
252
- ':is([dir="rtl"] *)': 0,
253
- },
254
- insetInlineEnd: {
255
- default: 0,
256
- ':is([dir="rtl"] *)': `calc(-1 * ${spacingVars['--spacing-1']} - 1px)`,
257
- },
258
- },
259
- },
260
- pillBridgeDown: {
261
- '::before': {
262
- content: '""',
263
- position: 'absolute',
264
- insetInline: 0,
265
- insetBlockStart: 0,
266
- insetBlockEnd: `calc(-1 * ${spacingVars['--spacing-1']} - 1px)`,
267
- },
268
- },
269
- pillBridgeUp: {
270
- '::before': {
271
- content: '""',
272
- position: 'absolute',
273
- insetInline: 0,
274
- insetBlockStart: `calc(-1 * ${spacingVars['--spacing-1']} - 1px)`,
275
- insetBlockEnd: 0,
276
- },
277
- },
278
237
  });
279
238
 
280
239
  // Dynamic styles — avoids inline style overrides.
281
240
  // Each axis gets its own function since StyleX requires static structure.
282
241
  const dynamicStyles = stylex.create({
283
- // An inline Handle's zone is centred on its line, so half of it reaches into
284
- // the pane.
285
- hitAreaLengthX: (pane: number) => ({
286
- width: `min(${spacingVars['--spacing-4']}, ${pane}px)`,
242
+ // Hit-area inline offset — mirrors the pill's `pillOffsetX`/`pillOffsetY`
243
+ // construction so the grab zone sits centred on the visible pill in BOTH
244
+ // directions. Both anchor at the divider's inline-start edge
245
+ // (`insetInlineStart: 0`) and travel toward the panel side with the SAME
246
+ // physical translate `dir * (gripWidth + gap)`, so they share the pill's
247
+ // near edge. The hit area is wider, so it also shifts by half the width
248
+ // difference `(16px − 3px) / 2 = 6.5px` to align the two CENTRES. That
249
+ // centring shift is along the inline axis (a box grows from its inline-start
250
+ // edge toward inline-end), so it must flip physical sign under RTL — unlike
251
+ // the `dir * (...)` travel, which is physical and identical in both
252
+ // directions because the pill's own offset is physical. Mixing the previous
253
+ // divider-relative `50%` anchor with a percentage translate stranded the grab
254
+ // zone to one side under RTL; this construction keeps it on the pill.
255
+ //
256
+ // Only the offset axis is translated. Unlike the pill — a small box anchored
257
+ // at the divider's midpoint, so it needs a −50% self-shift to centre — the
258
+ // grab zone is STRETCHED along the handle by hitAreaHorizontal/Vertical's
259
+ // 0/0 insets, so it is already in place on that axis. A percentage there
260
+ // would displace it by half the handle's own length, growing with the panel.
261
+ hitAreaOffsetX: (dir: number) => ({
262
+ insetInlineStart: 0,
263
+ transform: {
264
+ default: `translateX(calc(${dir} * (3px + ${spacingVars['--spacing-1']}) - 6.5px))`,
265
+ ':is([dir="rtl"] *)': `translateX(calc(${dir} * (3px + ${spacingVars['--spacing-1']}) + 6.5px))`,
266
+ },
287
267
  }),
288
- hitAreaLengthY: (pane: number) => ({
289
- height: `min(${spacingVars['--spacing-4']}, ${pane}px)`,
268
+ hitAreaOffsetY: (dir: number) => ({
269
+ insetBlockStart: 0,
270
+ transform: `translateY(calc(${dir} * (3px + ${spacingVars['--spacing-1']}) - 6.5px))`,
290
271
  }),
291
- pillOffsetX: (dir: -1 | 1) => ({
272
+ pillOffsetX: (dir: number) => ({
292
273
  insetInlineStart: 0,
293
274
  transform: `translate(calc(${dir} * (100% + ${spacingVars['--spacing-1']})), -50%)`,
294
275
  }),
295
276
  // Vertical offset: no rotation — use explicit landscape dimensions.
296
277
  // Rotation + offset creates confusing coordinate math since translate
297
278
  // operates in pre-rotation local space.
298
- pillOffsetY: (dir: -1 | 1) => ({
279
+ pillOffsetY: (dir: number) => ({
299
280
  top: 0,
300
281
  transform: `translate(-50%, calc(${dir} * (100% + ${spacingVars['--spacing-1']})))`,
301
282
  }),
@@ -352,8 +333,7 @@ export interface ResizeHandleProps extends Omit<
352
333
  isAlwaysVisible?: boolean;
353
334
 
354
335
  /**
355
- * Which side of the divider line the pill sits on. It moves only the pill:
356
- * the grab zone stays centred on the divider.
336
+ * Which side of the divider line the pill sits on.
357
337
  * - `'auto'` — panel side by default, flips when panel is collapsed to 0px
358
338
  * - `'start'` — left (horizontal) or top (vertical)
359
339
  * - `'end'` — right (horizontal) or bottom (vertical)
@@ -488,12 +468,9 @@ export function ResizeHandle({
488
468
  isReversed,
489
469
  isCollapsed,
490
470
  );
491
- // An inline zone reaches at most half of the pane the Handle resizes. An
492
- // overlay Handle takes at most half of it. A collapsed pane, or one at 0px,
493
- // has nothing to protect and keeps the whole zone.
494
- const paneSize = resizable?._size;
495
- const paneLength =
496
- isCollapsed || paneSize == null || paneSize <= 0 ? undefined : paneSize;
471
+ // Physical offset direction shared by the pill and the grab zone (null =
472
+ // centered / no bias). Keeps the two positioned identically in LTR and RTL.
473
+ const hitBiasDir = hitAreaBiasDir(effectiveSide);
497
474
  const isInteracting = isHovered || isFocused;
498
475
 
499
476
  // A handle whose axis disagrees with its region drags along one axis while
@@ -556,35 +533,6 @@ export function ResizeHandle({
556
533
  ? t('@astryx.resizable.collapsed')
557
534
  : undefined;
558
535
 
559
- // The Grip pill takes the pointer as the Grab zone does, so a pill drawn
560
- // outside the zone still resizes.
561
- const grabHandlers: Pick<
562
- HTMLAttributes<HTMLDivElement>,
563
- | 'onPointerDown'
564
- | 'onPointerMove'
565
- | 'onPointerUp'
566
- | 'onPointerCancel'
567
- | 'onLostPointerCapture'
568
- | 'onPointerEnter'
569
- | 'onPointerLeave'
570
- > = {
571
- onPointerDown: e => {
572
- if (!isDisabled) {
573
- gesture?.pointerDown(e);
574
- }
575
- },
576
- onPointerMove: e => gesture?.pointerMove(e),
577
- onPointerUp: e => gesture?.pointerUp(e),
578
- onPointerCancel: e => gesture?.pointerCancel(e),
579
- onLostPointerCapture: e => gesture?.pointerCancel(e),
580
- onPointerEnter: () => setIsHovered(true),
581
- onPointerLeave: () => {
582
- if (!isDragging) {
583
- setIsHovered(false);
584
- }
585
- },
586
- };
587
-
588
536
  return (
589
537
  <div
590
538
  ref={useMergedRefs(ref, handleRef)}
@@ -612,11 +560,6 @@ export function ResizeHandle({
612
560
  isOverlay && styles.overlay,
613
561
  isOverlay &&
614
562
  (isHorizontal ? styles.overlayHorizontal : styles.overlayVertical),
615
- isOverlay &&
616
- paneLength != null &&
617
- (isHorizontal
618
- ? styles.overlayHorizontalOnShortPane
619
- : styles.overlayVerticalOnShortPane),
620
563
  !isOverlay && (isHorizontal ? styles.horizontal : styles.vertical),
621
564
  !isOverlay &&
622
565
  !hasDivider &&
@@ -651,21 +594,34 @@ export function ResizeHandle({
651
594
  {...stylex.props(
652
595
  styles.hitArea,
653
596
  isHorizontal ? styles.hitAreaHorizontal : styles.hitAreaVertical,
654
- isHorizontal
655
- ? rtlStyles.centerInline('0px')
656
- : styles.hitAreaCenteredY,
657
- isOverlay &&
658
- (isHorizontal
659
- ? styles.hitAreaWithinHandleX
660
- : styles.hitAreaWithinHandleY),
661
- !isOverlay &&
662
- paneLength != null &&
663
- (isHorizontal
664
- ? dynamicStyles.hitAreaLengthX(paneLength)
665
- : dynamicStyles.hitAreaLengthY(paneLength)),
597
+ // Bias the grab zone to sit over the visible pill. When the pill is
598
+ // centered (no bias) just center the hit area on the divider; when
599
+ // it's offset to a panel side, reuse the pill's physical-offset
600
+ // construction so the two stay aligned in LTR and RTL alike.
601
+ hitBiasDir == null
602
+ ? isHorizontal
603
+ ? rtlStyles.centerInline('0px')
604
+ : styles.hitAreaCenteredY
605
+ : isHorizontal
606
+ ? dynamicStyles.hitAreaOffsetX(hitBiasDir)
607
+ : dynamicStyles.hitAreaOffsetY(hitBiasDir),
666
608
  isDisabled && styles.disabled,
667
609
  )}
668
- {...grabHandlers}
610
+ onPointerDown={e => {
611
+ if (!isDisabled) {
612
+ gesture?.pointerDown(e);
613
+ }
614
+ }}
615
+ onPointerMove={e => gesture?.pointerMove(e)}
616
+ onPointerUp={e => gesture?.pointerUp(e)}
617
+ onPointerCancel={e => gesture?.pointerCancel(e)}
618
+ onLostPointerCapture={e => gesture?.pointerCancel(e)}
619
+ onPointerEnter={() => setIsHovered(true)}
620
+ onPointerLeave={() => {
621
+ if (!isDragging) {
622
+ setIsHovered(false);
623
+ }
624
+ }}
669
625
  />
670
626
  {/* Pill grip indicator — themed via .astryx-resize-handle-pill */}
671
627
  {children ?? (
@@ -684,25 +640,11 @@ export function ResizeHandle({
684
640
  : dynamicStyles.pillOffsetY(
685
641
  effectiveSide === 'start' ? -1 : 1,
686
642
  ),
687
- isHorizontal
688
- ? styles.pillGrabHorizontal
689
- : styles.pillGrabVertical,
690
643
  isAlwaysVisible ? styles.pillVisible : styles.pillHidden,
691
- !isAlwaysVisible &&
692
- effectiveSide !== 'center' &&
693
- (isHorizontal
694
- ? effectiveSide === 'start'
695
- ? styles.pillBridgeRight
696
- : styles.pillBridgeLeft
697
- : effectiveSide === 'start'
698
- ? styles.pillBridgeDown
699
- : styles.pillBridgeUp),
700
644
  isInteracting && !isDragging && styles.pillHover,
701
645
  isDragging && styles.pillActive,
702
- isDisabled && styles.disabled,
703
646
  ),
704
647
  )}
705
- {...grabHandlers}
706
648
  />
707
649
  )}
708
650
  </div>
@@ -25,7 +25,7 @@ import {
25
25
  type RefObject,
26
26
  } from 'react';
27
27
  import {
28
- createMotionAnimator,
28
+ createAnimator,
29
29
  type AnimateOptions,
30
30
  type Animator,
31
31
  type MotionCurve,
@@ -539,7 +539,7 @@ export class SegmentedSelectionMotion {
539
539
  private readonly after: Animator;
540
540
 
541
541
  constructor(
542
- private readonly animator: Animator = createMotionAnimator(null, {
542
+ private readonly animator: Animator = createAnimator(null, {
543
543
  pseudoElement: '::before',
544
544
  }),
545
545
  options: SegmentedSelectionMotionOptions = {},
@@ -552,10 +552,7 @@ export class SegmentedSelectionMotion {
552
552
  this.now = options.now ?? defaultNow;
553
553
  this.after =
554
554
  options.afterAnimator ??
555
- createMotionAnimator(null, {
556
- pseudoElement: '::after',
557
- initial: {opacity: 0},
558
- });
555
+ createAnimator(null, {pseudoElement: '::after', initial: {opacity: 0}});
559
556
  }
560
557
 
561
558
  /** Attaches the radiogroup whose pseudo-elements are the lens (null detaches). */
@@ -29,7 +29,7 @@ import {TapGestureRecognizer} from '../gestures/TapGestureRecognizer';
29
29
  import type {PointerKind} from '../gestures/types';
30
30
  import {isRtlElement} from '../hooks/isRtlElement';
31
31
  import {
32
- createMotionAnimator,
32
+ createAnimator,
33
33
  type Animator,
34
34
  type MotionCurve,
35
35
  type MotionHandle,
@@ -1186,7 +1186,7 @@ export class SliderTouchController {
1186
1186
  private animatorFor(element: HTMLElement): Animator {
1187
1187
  let animator = this.animators.get(element);
1188
1188
  if (animator == null) {
1189
- animator = createMotionAnimator(element);
1189
+ animator = createAnimator(element);
1190
1190
  this.animators.set(element, animator);
1191
1191
  }
1192
1192
  return animator;