@astryxdesign/core 0.7.8 → 0.7.9-canary.048476f

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 (213) hide show
  1. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  2. package/dist/BottomSheet/BottomSheet.js +5 -0
  3. package/dist/Calendar/styles.d.ts +1 -1
  4. package/dist/ClickableCard/ClickableCard.d.ts +1 -1
  5. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  6. package/dist/ClickableCard/ClickableCard.js +5 -1
  7. package/dist/DateInput/PickerWheel.d.ts.map +1 -1
  8. package/dist/DateInput/PickerWheel.js +7 -1
  9. package/dist/DateRangeInput/DateRangeInput.js +1 -1
  10. package/dist/Dialog/Dialog.d.ts +2 -1
  11. package/dist/Dialog/Dialog.d.ts.map +1 -1
  12. package/dist/Dialog/Dialog.js +12 -1
  13. package/dist/Drawer/useDrawerDialogPresence.d.ts +2 -1
  14. package/dist/Drawer/useDrawerDialogPresence.d.ts.map +1 -1
  15. package/dist/Drawer/useDrawerDialogPresence.js +11 -2
  16. package/dist/DropdownMenu/DropdownMenu.d.ts +9 -1
  17. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  18. package/dist/DropdownMenu/DropdownMenu.js +9 -4
  19. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  20. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +3 -2
  21. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  22. package/dist/DropdownMenu/DropdownMenuItem.js +3 -2
  23. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  24. package/dist/DropdownMenu/DropdownMenuRadioItem.js +3 -2
  25. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  26. package/dist/DropdownMenu/DropdownMenuSubMenu.js +3 -2
  27. package/dist/Field/inputDisabledFocus.d.ts +4 -4
  28. package/dist/Field/inputStyles.stylex.d.ts +1 -1
  29. package/dist/FileInput/FileInput.d.ts.map +1 -1
  30. package/dist/FileInput/FileInput.js +16 -2
  31. package/dist/Icon/Icon.d.ts +1 -1
  32. package/dist/InfoTip/InfoTip.d.ts +1 -0
  33. package/dist/InfoTip/InfoTip.d.ts.map +1 -1
  34. package/dist/InfoTip/InfoTip.js +1 -0
  35. package/dist/Item/Item.d.ts +2 -1
  36. package/dist/Item/Item.d.ts.map +1 -1
  37. package/dist/Item/Item.js +29 -6
  38. package/dist/Layer/holdEntranceInput.d.ts +24 -0
  39. package/dist/Layer/holdEntranceInput.d.ts.map +1 -0
  40. package/dist/Layer/holdEntranceInput.js +65 -0
  41. package/dist/Layer/inputHold.d.ts +49 -0
  42. package/dist/Layer/inputHold.d.ts.map +1 -0
  43. package/dist/Layer/inputHold.js +636 -0
  44. package/dist/Layer/preserveAnimations.d.ts +1 -5
  45. package/dist/Layer/preserveAnimations.d.ts.map +1 -1
  46. package/dist/Layer/preserveAnimations.js +12 -214
  47. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  48. package/dist/Lightbox/Lightbox.js +4 -0
  49. package/dist/MetadataList/MetadataList.js +1 -1
  50. package/dist/MobileNav/MobileNav.d.ts +11 -1
  51. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  52. package/dist/MobileNav/MobileNav.js +34 -1
  53. package/dist/MoreMenu/MoreMenu.d.ts +5 -3
  54. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  55. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  56. package/dist/RadioList/RadioListItem.js +4 -1
  57. package/dist/SelectableCard/SelectableCard.d.ts +1 -1
  58. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  59. package/dist/SelectableCard/SelectableCard.js +5 -1
  60. package/dist/Spinner/Spinner.d.ts.map +1 -1
  61. package/dist/Spinner/Spinner.js +12 -2
  62. package/dist/Table/plugins/sortable/useTableSortable.js +1 -1
  63. package/dist/Token/Token.d.ts.map +1 -1
  64. package/dist/Token/Token.js +9 -0
  65. package/dist/Token/TokenLink.d.ts +1 -1
  66. package/dist/Token/TokenLink.d.ts.map +1 -1
  67. package/dist/Token/TokenLink.js +4 -1
  68. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  69. package/dist/Tooltip/useTooltip.js +162 -59
  70. package/dist/TreeList/TreeListItem.d.ts +2 -1
  71. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  72. package/dist/TreeList/TreeListItem.js +27 -3
  73. package/dist/astryx.css +30 -7
  74. package/dist/gestures/GestureHost.d.ts +2 -2
  75. package/dist/gestures/GestureHost.d.ts.map +1 -1
  76. package/dist/gestures/GestureHost.js +21 -6
  77. package/dist/gestures/nativeScroll.d.ts +3 -6
  78. package/dist/gestures/nativeScroll.d.ts.map +1 -1
  79. package/dist/gestures/nativeScroll.js +7 -52
  80. package/dist/gestures/wheelSegmenter.d.ts +68 -1
  81. package/dist/gestures/wheelSegmenter.d.ts.map +1 -1
  82. package/dist/gestures/wheelSegmenter.js +199 -10
  83. package/dist/hooks/menuPressGesture.d.ts +133 -21
  84. package/dist/hooks/menuPressGesture.d.ts.map +1 -1
  85. package/dist/hooks/menuPressGesture.js +155 -30
  86. package/dist/hooks/menuPressValues.d.ts +16 -0
  87. package/dist/hooks/menuPressValues.d.ts.map +1 -0
  88. package/dist/hooks/menuPressValues.js +18 -0
  89. package/dist/hooks/useClickableContainer.d.ts +27 -5
  90. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  91. package/dist/hooks/useClickableContainer.js +41 -5
  92. package/dist/hooks/useFocusTrap.d.ts +10 -0
  93. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  94. package/dist/hooks/useFocusTrap.js +225 -132
  95. package/dist/hooks/useMenuPress.d.ts +34 -1
  96. package/dist/hooks/useMenuPress.d.ts.map +1 -1
  97. package/dist/hooks/useMenuPress.js +93 -5
  98. package/dist/hooks/usePressFeedback.d.ts +6 -6
  99. package/dist/hooks/usePressFeedback.js +6 -6
  100. package/dist/motion/carryAnimations.d.ts +58 -0
  101. package/dist/motion/carryAnimations.d.ts.map +1 -0
  102. package/dist/motion/carryAnimations.js +305 -0
  103. package/dist/motion/transientCopy.d.ts +4 -2
  104. package/dist/motion/transientCopy.d.ts.map +1 -1
  105. package/dist/motion/transientCopy.js +34 -5
  106. package/dist/motion/uikit/gestures.d.ts +41 -0
  107. package/dist/motion/uikit/gestures.d.ts.map +1 -1
  108. package/dist/motion/uikit/gestures.js +25 -0
  109. package/dist/motion/uikit/index.d.ts +1 -1
  110. package/dist/motion/uikit/index.d.ts.map +1 -1
  111. package/dist/motion/uikit/index.js +1 -1
  112. package/dist/privateAttributes/menuPressLit.stylex.d.ts +52 -0
  113. package/dist/privateAttributes/menuPressLit.stylex.d.ts.map +1 -0
  114. package/dist/privateAttributes/menuPressLit.stylex.js +99 -0
  115. package/dist/privateAttributes/press.stylex.d.ts +139 -0
  116. package/dist/privateAttributes/press.stylex.d.ts.map +1 -0
  117. package/dist/privateAttributes/press.stylex.js +98 -0
  118. package/dist/theme/tokens.stylex.d.ts +2 -2
  119. package/dist/theme/tokens.stylex.js +1 -1
  120. package/dist/utils/flatTree.d.ts +6 -3
  121. package/dist/utils/flatTree.d.ts.map +1 -1
  122. package/dist/utils/flatTree.js +13 -3
  123. package/dist/utils/hitArea.d.ts.map +1 -1
  124. package/dist/utils/hitArea.js +82 -5
  125. package/dist/utils/inputStream.d.ts +14 -1
  126. package/dist/utils/inputStream.d.ts.map +1 -1
  127. package/dist/utils/inputStream.js +40 -9
  128. package/dist/utils/interactionOverlay.stylex.d.ts +2 -1
  129. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  130. package/dist/utils/interactionOverlay.stylex.js +7 -2
  131. package/dist/utils/pressFeedback.d.ts +7 -0
  132. package/dist/utils/pressFeedback.d.ts.map +1 -1
  133. package/dist/utils/pressFeedback.js +32 -7
  134. package/dist/utils/pressGesture.d.ts +6 -0
  135. package/dist/utils/pressGesture.d.ts.map +1 -1
  136. package/dist/utils/pressGesture.js +17 -0
  137. package/dist/utils/rowPressLayer.stylex.d.ts +14 -0
  138. package/dist/utils/rowPressLayer.stylex.d.ts.map +1 -0
  139. package/dist/utils/rowPressLayer.stylex.js +25 -0
  140. package/dist/utils/scrollView.d.ts +25 -0
  141. package/dist/utils/scrollView.d.ts.map +1 -0
  142. package/dist/utils/scrollView.js +108 -0
  143. package/dist/utils/selectionLock.d.ts.map +1 -1
  144. package/dist/utils/selectionLock.js +1 -10
  145. package/dist/utils/triggerFocus.d.ts +5 -0
  146. package/dist/utils/triggerFocus.d.ts.map +1 -1
  147. package/dist/utils/triggerFocus.js +9 -2
  148. package/package.json +5 -5
  149. package/src/BottomSheet/BottomSheet.tsx +5 -0
  150. package/src/ClickableCard/ClickableCard.tsx +8 -1
  151. package/src/DateInput/PickerWheel.tsx +7 -2
  152. package/src/DateRangeInput/DateRangeInput.tsx +1 -1
  153. package/src/Dialog/Dialog.doc.mjs +1 -1
  154. package/src/Dialog/Dialog.tsx +12 -1
  155. package/src/Drawer/Drawer.doc.mjs +1 -1
  156. package/src/Drawer/useDrawerDialogPresence.ts +9 -2
  157. package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -2
  158. package/src/DropdownMenu/DropdownMenu.tsx +29 -5
  159. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +5 -1
  160. package/src/DropdownMenu/DropdownMenuItem.tsx +8 -4
  161. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +5 -1
  162. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +5 -1
  163. package/src/FileInput/FileInput.tsx +16 -2
  164. package/src/InfoTip/InfoTip.doc.mjs +26 -2
  165. package/src/InfoTip/InfoTip.tsx +1 -0
  166. package/src/Item/Item.tsx +51 -9
  167. package/src/Layer/holdEntranceInput.ts +84 -0
  168. package/src/Layer/inputHold.ts +734 -0
  169. package/src/Layer/preserveAnimations.ts +12 -275
  170. package/src/Lightbox/Lightbox.tsx +4 -0
  171. package/src/MetadataList/MetadataList.tsx +1 -1
  172. package/src/MobileNav/MobileNav.doc.mjs +3 -3
  173. package/src/MobileNav/MobileNav.tsx +34 -1
  174. package/src/MoreMenu/MoreMenu.doc.mjs +2 -4
  175. package/src/MoreMenu/MoreMenu.tsx +5 -3
  176. package/src/RadioList/RadioListItem.tsx +4 -5
  177. package/src/SelectableCard/SelectableCard.tsx +8 -1
  178. package/src/Spinner/Spinner.tsx +13 -3
  179. package/src/Table/plugins/sortable/useTableSortable.tsx +1 -1
  180. package/src/Token/Token.tsx +18 -1
  181. package/src/Token/TokenLink.tsx +7 -1
  182. package/src/Tooltip/useTooltip.tsx +175 -79
  183. package/src/TreeList/TreeListItem.tsx +36 -2
  184. package/src/gestures/GestureHost.ts +21 -5
  185. package/src/gestures/README.md +12 -4
  186. package/src/gestures/nativeScroll.ts +12 -68
  187. package/src/gestures/wheelSegmenter.ts +243 -10
  188. package/src/hooks/menuPressGesture.ts +244 -38
  189. package/src/hooks/menuPressValues.ts +18 -0
  190. package/src/hooks/useClickableContainer.doc.mjs +8 -2
  191. package/src/hooks/useClickableContainer.ts +72 -6
  192. package/src/hooks/useFocusTrap.ts +268 -151
  193. package/src/hooks/useMenuPress.ts +118 -11
  194. package/src/hooks/usePressFeedback.doc.mjs +3 -3
  195. package/src/hooks/usePressFeedback.ts +6 -6
  196. package/src/motion/MEASUREMENTS.md +11 -0
  197. package/src/motion/carryAnimations.ts +389 -0
  198. package/src/motion/transientCopy.ts +45 -5
  199. package/src/motion/uikit/gestures.ts +22 -0
  200. package/src/motion/uikit/index.ts +1 -0
  201. package/src/privateAttributes/menuPressLit.stylex.ts +104 -0
  202. package/src/privateAttributes/press.stylex.ts +244 -0
  203. package/src/theme/tokens.stylex.ts +1 -1
  204. package/src/utils/flatTree.ts +15 -3
  205. package/src/utils/hitArea.ts +107 -5
  206. package/src/utils/inputStream.ts +47 -10
  207. package/src/utils/interactionOverlay.stylex.ts +7 -2
  208. package/src/utils/pressFeedback.ts +31 -6
  209. package/src/utils/pressGesture.ts +24 -1
  210. package/src/utils/rowPressLayer.stylex.ts +27 -0
  211. package/src/utils/scrollView.ts +135 -0
  212. package/src/utils/selectionLock.ts +5 -10
  213. package/src/utils/triggerFocus.ts +17 -4
@@ -3,10 +3,10 @@
3
3
  /**
4
4
  * @file pressFeedback.ts
5
5
  * @input The press state machine (pressGesture.ts), the menu-press and
6
- * press-group markers and the document's pointer, keyboard, scroll and
6
+ * press-group markers, the scroll-view test (scrollView.ts) and the document's pointer, keyboard, scroll and
7
7
  * visibility events
8
8
  * @output PRESSABLE_ATTRIBUTE, PRESSED_ATTRIBUTE, pressableProps,
9
- * pressFadeMs and installPressFeedback
9
+ * pressFadeMs, installPressFeedback and cancelPress
10
10
  * @position The DOM half of the touch press model. usePressFeedback installs
11
11
  * it, and the CSS arms live in interactionOverlay.stylex.ts.
12
12
  *
@@ -23,9 +23,13 @@
23
23
  // Where the primary pointer is fine, the controller ignores the mouse and the
24
24
  // keyboard, and CSS `:active` keeps their press. Under a coarse primary
25
25
  // pointer the CSS drops `:active`, so the controller paints every press. A
26
- // finger's press paints after the delay. A mouse, trackpad or Space press
27
- // paints at once. The controller reads the same query as the stylesheet, so
28
- // they agree.
26
+ // mouse, trackpad or Space press paints at once. The controller reads the
27
+ // same query as the stylesheet, so they agree.
28
+ //
29
+ // A finger waits only inside a scroller. Outside one, a surface highlights in
30
+ // the frame the finger lands, as a UIControl does. Inside one, it waits the
31
+ // content-touch delay, because the touch can become a scroll. Rows follow the
32
+ // same rule.
29
33
  //
30
34
  // A surface's hold does nothing, so it keeps the browser's long-press text
31
35
  // selection (AST-094). Only an element whose hold does something suppresses
@@ -44,6 +48,7 @@ import {
44
48
  } from './pressGesture';
45
49
  import {isCoarsePointer, watchCoarsePointer} from './coarsePointer';
46
50
  import {subscribeInput} from './inputStream';
51
+ import {isInsideScrollView} from './scrollView';
47
52
  import {TOUCH_QUERY} from './touchPresentation';
48
53
  import {holdsPressThroughDrag} from '../privateAttributes/pressHoldsDrag.stylex';
49
54
  import {isTrackedByGroup} from '../privateAttributes/pressGroupItem.stylex';
@@ -247,7 +252,11 @@ function paintElement(element: HTMLElement, paint: PressPaint): void {
247
252
  element.removeAttribute(PRESSED_ATTRIBUTE);
248
253
  return;
249
254
  }
250
- element.setAttribute(PRESSED_ATTRIBUTE, paint);
255
+ // A step that keeps the paint writes nothing, so the element sees one
256
+ // change per paint.
257
+ if (element.getAttribute(PRESSED_ATTRIBUTE) !== paint) {
258
+ element.setAttribute(PRESSED_ATTRIBUTE, paint);
259
+ }
251
260
  }
252
261
 
253
262
  function clearTimer(slot: Slot): void {
@@ -381,6 +390,21 @@ function finish(slot: Slot): void {
381
390
  }
382
391
  }
383
392
 
393
+ /**
394
+ * Ends the press the controller follows on `element`, and its paint, at once.
395
+ * A clickable container calls it when a press starts on an interactive
396
+ * element inside it: that press does not activate the container, so the
397
+ * container shows no press.
398
+ */
399
+ export function cancelPress(element: Element): void {
400
+ if (finger.live?.element === element) {
401
+ finish(finger);
402
+ }
403
+ if (button.live?.element === element) {
404
+ finish(button);
405
+ }
406
+ }
407
+
384
408
  /** A new press on an element owns its paint: the other slot's gesture there ends. */
385
409
  function takeOver(other: Slot, element: HTMLElement): void {
386
410
  if (other.live?.element === element) {
@@ -521,6 +545,7 @@ function onPointerDown(event: PointerEvent): void {
521
545
  kind: 'down',
522
546
  x: event.clientX,
523
547
  y: event.clientY,
548
+ delayed: isInsideScrollView(element),
524
549
  }),
525
550
  );
526
551
  }
@@ -21,6 +21,10 @@
21
21
  // primary pointer, so a mouse, trackpad or Space press on such a device comes
22
22
  // here as `buttonDown`: painted at once and faded on release.
23
23
  //
24
+ // Only a finger inside a scroller waits. The controller tells the machine
25
+ // which touch waits (`down`'s `delayed`), and a touch that does not wait goes
26
+ // straight to `pressed`. After that the machine is the same.
27
+ //
24
28
  // The constants were measured against native apps. The machine knows nothing
25
29
  // of the DOM, so {@link pressGestureStep}'s cases are the state diagram.
26
30
 
@@ -105,7 +109,17 @@ export interface PressGesture {
105
109
  }
106
110
 
107
111
  export type PressEvent =
108
- | {kind: 'down'; x: number; y: number}
112
+ | {
113
+ kind: 'down';
114
+ x: number;
115
+ y: number;
116
+ /**
117
+ * Whether this touch waits {@link PRESS_ONSET_MS} before it paints:
118
+ * a surface inside a scroller. Defaults to
119
+ * `true`.
120
+ */
121
+ delayed?: boolean;
122
+ }
109
123
  /**
110
124
  * A mouse button or the Space key went down on the surface. The
111
125
  * controller sends this only where the stylesheet has dropped `:active`
@@ -167,6 +181,15 @@ export function pressGestureStep(
167
181
  event: PressEvent,
168
182
  ): PressStep {
169
183
  if (event.kind === 'down') {
184
+ // A surface with no scroller above it: nothing can turn this touch into a
185
+ // scroll, so it paints in the frame it lands, as a UIControl highlights.
186
+ if (event.delayed === false) {
187
+ return {
188
+ gesture: {originX: event.x, originY: event.y, phase: 'pressed'},
189
+ paint: 'on',
190
+ timer: null,
191
+ };
192
+ }
170
193
  // A new touch always starts armed and painting nothing, whatever the last
171
194
  // gesture ended as. This is the only way out of `dead`.
172
195
  return {
@@ -0,0 +1,27 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file rowPressLayer.stylex.ts
5
+ * @input StyleX
6
+ * @output The list row press layer's clocks (`cellPressConsts`)
7
+ * @position Internal to core: no package entry exports it. Only the row
8
+ * layer (privateAttributes/press.stylex.ts) reads it, so a component
9
+ * without a press row ships none of it; interactionOverlay.exports.test.ts
10
+ * keeps it off the published paths.
11
+ */
12
+
13
+ import * as stylex from '@stylexjs/stylex';
14
+
15
+ /**
16
+ * A list row's release (`rowPressLayerStyles.cell`), mirrored for the
17
+ * stylesheet. StyleX evaluates a style at build time and cannot read the
18
+ * constants table, so these are the table's press release values
19
+ * (`timing.press`, motion/uikit/timing.ts). interactionOverlay.cell.test.ts
20
+ * fails if they drift.
21
+ */
22
+ export const cellPressConsts = stylex.defineConsts({
23
+ /** `timing.press.highlightOutMs`, in ms. */
24
+ cellReleaseMs: 200,
25
+ /** `timing.press.highlightOutCurve`, as CSS. */
26
+ cellReleaseEase: 'cubic-bezier(0, 0, 0.2, 1)',
27
+ });
@@ -0,0 +1,135 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file scrollView.ts
5
+ * @input An element and its computed styles
6
+ * @output Exports parseTouchAction, rootOverflow, SCROLLABLE_OVERFLOW and
7
+ * isInsideScrollView: whether a touch on the element lands inside a box the
8
+ * browser can scroll
9
+ * @position The one definition of "inside a scroll view", shared by the
10
+ * press feedback controller (utils/pressFeedback.ts) and the gesture
11
+ * core's native-scroll probe (gestures/nativeScroll.ts), so Core's main
12
+ * entry can ask it without the gesture core
13
+ */
14
+
15
+ import {parentAcrossShadow} from './flatTree';
16
+
17
+ /** Overflow values that let a box scroll. */
18
+ export const SCROLLABLE_OVERFLOW: ReadonlySet<string> = new Set([
19
+ 'auto',
20
+ 'scroll',
21
+ 'overlay',
22
+ ]);
23
+
24
+ /** How far a box must overflow, in px, before it counts as scrolling. */
25
+ const OVERFLOW_TOLERANCE_PX = 1;
26
+
27
+ export interface AllowedPans {
28
+ readonly x: boolean;
29
+ readonly y: boolean;
30
+ }
31
+
32
+ /** Which pans a `touch-action` value lets the browser perform. */
33
+ export function parseTouchAction(value: string): AllowedPans {
34
+ const tokens = value.trim().split(/\s+/);
35
+ if (tokens.includes('auto') || tokens.includes('manipulation')) {
36
+ return {x: true, y: true};
37
+ }
38
+ if (tokens.includes('none')) {
39
+ return {x: false, y: false};
40
+ }
41
+ return {
42
+ x: tokens.some(
43
+ token =>
44
+ token === 'pan-x' || token === 'pan-left' || token === 'pan-right',
45
+ ),
46
+ y: tokens.some(
47
+ token => token === 'pan-y' || token === 'pan-up' || token === 'pan-down',
48
+ ),
49
+ };
50
+ }
51
+
52
+ /**
53
+ * The viewport's overflow on an axis. It scrolls unless the root or the body
54
+ * clips it, as a scroll lock does with `overflow: hidden`.
55
+ */
56
+ export function rootOverflow(doc: Document, axis: 'x' | 'y'): string {
57
+ const view = doc.defaultView;
58
+ if (view == null) {
59
+ return 'hidden';
60
+ }
61
+ for (const element of [doc.documentElement, doc.body]) {
62
+ if (element == null) {
63
+ continue;
64
+ }
65
+ const style = view.getComputedStyle(element);
66
+ const value = axis === 'x' ? style.overflowX : style.overflowY;
67
+ if (value === 'hidden' || value === 'clip') {
68
+ return 'hidden';
69
+ }
70
+ }
71
+ return 'auto';
72
+ }
73
+
74
+ function overflows(element: Element, axis: 'x' | 'y'): boolean {
75
+ return axis === 'y'
76
+ ? element.scrollHeight - element.clientHeight > OVERFLOW_TOLERANCE_PX
77
+ : element.scrollWidth - element.clientWidth > OVERFLOW_TOLERANCE_PX;
78
+ }
79
+
80
+ /**
81
+ * Whether a touch on `element` lands inside a scroll view, UIKit's question
82
+ * for a touch: a box above it, the page included, overflows on an axis it
83
+ * scrolls, and `touch-action` on the way up leaves that axis to the browser.
84
+ * A `position: fixed` box ends the walk, because it does not move with any
85
+ * scroller above it. A wheel scrolls whatever `touch-action` says, so a
86
+ * wheel's question passes `readsTouchAction: false`.
87
+ */
88
+ export function isInsideScrollView(
89
+ element: Element,
90
+ {readsTouchAction = true}: {readsTouchAction?: boolean} = {},
91
+ ): boolean {
92
+ const doc = element.ownerDocument;
93
+ const view = doc.defaultView;
94
+ if (view == null) {
95
+ return false;
96
+ }
97
+ const root = doc.scrollingElement ?? doc.documentElement;
98
+ let panX = true;
99
+ let panY = true;
100
+ for (let node: Node | null = element; node != null;) {
101
+ if (node instanceof Element) {
102
+ const style = view.getComputedStyle(node);
103
+ if (readsTouchAction) {
104
+ const pans = parseTouchAction(style.touchAction || 'auto');
105
+ panX &&= pans.x;
106
+ panY &&= pans.y;
107
+ }
108
+ if (node === root || node === doc.documentElement) {
109
+ break;
110
+ }
111
+ if (
112
+ (panY &&
113
+ SCROLLABLE_OVERFLOW.has(style.overflowY) &&
114
+ overflows(node, 'y')) ||
115
+ (panX &&
116
+ SCROLLABLE_OVERFLOW.has(style.overflowX) &&
117
+ overflows(node, 'x'))
118
+ ) {
119
+ return true;
120
+ }
121
+ if (style.position === 'fixed') {
122
+ return false;
123
+ }
124
+ }
125
+ node = parentAcrossShadow(node);
126
+ }
127
+ return (
128
+ (panY &&
129
+ SCROLLABLE_OVERFLOW.has(rootOverflow(doc, 'y')) &&
130
+ overflows(root, 'y')) ||
131
+ (panX &&
132
+ SCROLLABLE_OVERFLOW.has(rootOverflow(doc, 'x')) &&
133
+ overflows(root, 'x'))
134
+ );
135
+ }
@@ -28,7 +28,11 @@
28
28
  //
29
29
  // A mouse never takes the lock, so its released behavior stays (AST-017).
30
30
 
31
- import {containsAlongFlatTree, parentAcrossShadow} from './flatTree';
31
+ import {
32
+ containsAlongFlatTree,
33
+ deepActiveElement,
34
+ parentAcrossShadow,
35
+ } from './flatTree';
32
36
  import {subscribeInput} from './inputStream';
33
37
  import {isIosWebKit} from './touchQueries';
34
38
 
@@ -616,15 +620,6 @@ function openShadowRootsOf(target: EventTarget | null): ShadowRoot[] {
616
620
  return roots;
617
621
  }
618
622
 
619
- /** The focused element, inside open shadow roots too. */
620
- function deepActiveElement(doc: Document | null): Element | null {
621
- let active = doc?.activeElement ?? null;
622
- while (active?.shadowRoot?.activeElement != null) {
623
- active = active.shadowRoot.activeElement;
624
- }
625
- return active;
626
- }
627
-
628
623
  /**
629
624
  * The selection-lock styles on `set`, refcounted per element; idempotent
630
625
  * release.
@@ -37,6 +37,22 @@ export function isFocusableTrigger(element: HTMLElement): boolean {
37
37
  export function isFocusForTrigger(
38
38
  event: Event,
39
39
  mode: TriggerFocusMode,
40
+ ): boolean {
41
+ return isFocusOnTrigger(
42
+ event.target,
43
+ event.currentTarget as HTMLElement | null,
44
+ mode,
45
+ );
46
+ }
47
+
48
+ /**
49
+ * `isFocusForTrigger` for focus already on `target`, inside `trigger`: the
50
+ * same rule, read when a trigger attaches with focus inside it.
51
+ */
52
+ export function isFocusOnTrigger(
53
+ target: EventTarget | null,
54
+ trigger: HTMLElement | null,
55
+ mode: TriggerFocusMode,
40
56
  ): boolean {
41
57
  if (mode === 'always') {
42
58
  return true;
@@ -44,10 +60,7 @@ export function isFocusForTrigger(
44
60
  if (mode === 'never') {
45
61
  return false;
46
62
  }
47
- const trigger = event.currentTarget as HTMLElement | null;
48
- return (
49
- trigger != null && (event.target === trigger || isFocusableTrigger(trigger))
50
- );
63
+ return trigger != null && (target === trigger || isFocusableTrigger(trigger));
51
64
  }
52
65
 
53
66
  /** Remembers whether a trigger's current focus counted when it arrived. */