@astryxdesign/core 0.7.6 → 0.7.7-canary.0eb44a2

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 (272) hide show
  1. package/dist/AppShell/AppShell.d.ts.map +1 -1
  2. package/dist/AppShell/AppShell.js +23 -3
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +1 -1
  5. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  6. package/dist/BottomSheet/BottomSheetPanel.js +16 -9
  7. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  8. package/dist/Breadcrumbs/BreadcrumbItem.js +2 -1
  9. package/dist/Button/Button.d.ts.map +1 -1
  10. package/dist/Button/Button.js +3 -2
  11. package/dist/Chat/ChatComposerInput.d.ts +9 -0
  12. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  13. package/dist/Chat/ChatComposerInput.js +2 -0
  14. package/dist/Chat/ChatMessage.js +2 -2
  15. package/dist/Chat/chatMessage.vars.stylex.d.ts +8 -8
  16. package/dist/Chat/chatMessage.vars.stylex.js +8 -8
  17. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  18. package/dist/CommandPalette/CommandPaletteInput.js +8 -1
  19. package/dist/DateInput/MonthScroller.d.ts.map +1 -1
  20. package/dist/DateInput/MonthScroller.js +72 -2
  21. package/dist/DateInput/Wheel.d.ts +17 -1
  22. package/dist/DateInput/Wheel.d.ts.map +1 -1
  23. package/dist/DateInput/Wheel.js +179 -19
  24. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  25. package/dist/DateRangeInput/DateRangeInput.js +63 -49
  26. package/dist/Dialog/Dialog.d.ts.map +1 -1
  27. package/dist/Dialog/Dialog.js +37 -1
  28. package/dist/Drawer/Drawer.d.ts.map +1 -1
  29. package/dist/Drawer/Drawer.js +6 -0
  30. package/dist/Drawer/useDrawerDialogPresence.d.ts.map +1 -1
  31. package/dist/Drawer/useDrawerDialogPresence.js +4 -0
  32. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  33. package/dist/DropdownMenu/DropdownMenu.js +20 -4
  34. package/dist/DropdownMenu/useMenuOverflow.d.ts +13 -3
  35. package/dist/DropdownMenu/useMenuOverflow.d.ts.map +1 -1
  36. package/dist/DropdownMenu/useMenuOverflow.js +31 -8
  37. package/dist/Field/PanelSearchInput.d.ts.map +1 -1
  38. package/dist/Field/PanelSearchInput.js +8 -1
  39. package/dist/Field/textEntryTraits.d.ts +6 -6
  40. package/dist/Field/textEntryTraits.js +6 -6
  41. package/dist/Layer/layerClickBoundary.d.ts +7 -1
  42. package/dist/Layer/layerClickBoundary.d.ts.map +1 -1
  43. package/dist/Layer/layerClickBoundary.js +2 -2
  44. package/dist/Layer/usePressPin.d.ts +95 -0
  45. package/dist/Layer/usePressPin.d.ts.map +1 -0
  46. package/dist/Layer/usePressPin.js +365 -0
  47. package/dist/Layer/useTouchTrigger.d.ts +23 -1
  48. package/dist/Layer/useTouchTrigger.d.ts.map +1 -1
  49. package/dist/Layer/useTouchTrigger.js +45 -7
  50. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  51. package/dist/Lightbox/Lightbox.js +2 -1
  52. package/dist/Link/Link.d.ts.map +1 -1
  53. package/dist/Link/Link.js +1 -0
  54. package/dist/Popover/usePopover.d.ts.map +1 -1
  55. package/dist/Popover/usePopover.js +56 -7
  56. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  57. package/dist/PowerSearch/PowerSearch.js +38 -30
  58. package/dist/ProgressBar/ProgressBar.d.ts +1 -1
  59. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  60. package/dist/ProgressBar/ProgressBar.js +52 -14
  61. package/dist/ProgressBar/ProgressBarTouch.d.ts +29 -0
  62. package/dist/ProgressBar/ProgressBarTouch.d.ts.map +1 -0
  63. package/dist/ProgressBar/ProgressBarTouch.js +91 -0
  64. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  65. package/dist/SegmentedControl/SegmentedControlItem.js +2 -1
  66. package/dist/Selector/Selector.d.ts.map +1 -1
  67. package/dist/Selector/Selector.js +25 -13
  68. package/dist/Slider/Slider.d.ts.map +1 -1
  69. package/dist/Slider/Slider.js +404 -131
  70. package/dist/Slider/sliderTouchController.d.ts +189 -0
  71. package/dist/Slider/sliderTouchController.d.ts.map +1 -0
  72. package/dist/Slider/sliderTouchController.js +1017 -0
  73. package/dist/Slider/useSliderTouch.d.ts +22 -0
  74. package/dist/Slider/useSliderTouch.d.ts.map +1 -0
  75. package/dist/Slider/useSliderTouch.js +56 -0
  76. package/dist/Switch/Switch.d.ts +1 -1
  77. package/dist/Switch/Switch.d.ts.map +1 -1
  78. package/dist/Switch/Switch.js +200 -46
  79. package/dist/Switch/switchFill.d.ts +31 -0
  80. package/dist/Switch/switchFill.d.ts.map +1 -0
  81. package/dist/Switch/switchFill.js +88 -0
  82. package/dist/Switch/switchTouch.d.ts +81 -0
  83. package/dist/Switch/switchTouch.d.ts.map +1 -0
  84. package/dist/Switch/switchTouch.js +133 -0
  85. package/dist/Switch/useSwitchTouch.d.ts +285 -0
  86. package/dist/Switch/useSwitchTouch.d.ts.map +1 -0
  87. package/dist/Switch/useSwitchTouch.js +998 -0
  88. package/dist/TabList/Tab.d.ts.map +1 -1
  89. package/dist/TabList/Tab.js +4 -3
  90. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  91. package/dist/Tokenizer/Tokenizer.js +12 -5
  92. package/dist/Tokenizer/TokenizerItemRemovableContext.d.ts +3 -0
  93. package/dist/Tokenizer/TokenizerItemRemovableContext.d.ts.map +1 -0
  94. package/dist/Tokenizer/TokenizerItemRemovableContext.js +17 -0
  95. package/dist/Tooltip/Tooltip.d.ts +30 -6
  96. package/dist/Tooltip/Tooltip.d.ts.map +1 -1
  97. package/dist/Tooltip/Tooltip.js +5 -0
  98. package/dist/Tooltip/index.d.ts +1 -1
  99. package/dist/Tooltip/index.d.ts.map +1 -1
  100. package/dist/Tooltip/useTooltip.d.ts +31 -5
  101. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  102. package/dist/Tooltip/useTooltip.js +59 -18
  103. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  104. package/dist/Typeahead/BaseTypeahead.js +19 -9
  105. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  106. package/dist/Typeahead/Typeahead.js +26 -15
  107. package/dist/astryx.css +91 -8
  108. package/dist/gestures/GestureArena.d.ts +21 -2
  109. package/dist/gestures/GestureArena.d.ts.map +1 -1
  110. package/dist/gestures/GestureArena.js +23 -3
  111. package/dist/gestures/GestureHost.d.ts +123 -3
  112. package/dist/gestures/GestureHost.d.ts.map +1 -1
  113. package/dist/gestures/GestureHost.js +797 -15
  114. package/dist/gestures/GestureRecognizer.d.ts +15 -1
  115. package/dist/gestures/GestureRecognizer.d.ts.map +1 -1
  116. package/dist/gestures/GestureRecognizer.js +12 -0
  117. package/dist/gestures/PanGestureRecognizer.d.ts +8 -0
  118. package/dist/gestures/PanGestureRecognizer.d.ts.map +1 -1
  119. package/dist/gestures/PinchGestureRecognizer.d.ts +5 -1
  120. package/dist/gestures/PinchGestureRecognizer.d.ts.map +1 -1
  121. package/dist/gestures/PinchGestureRecognizer.js +10 -0
  122. package/dist/gestures/PressTracker.d.ts +6 -0
  123. package/dist/gestures/PressTracker.d.ts.map +1 -1
  124. package/dist/gestures/PressTracker.js +3 -0
  125. package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts +3 -3
  126. package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts.map +1 -1
  127. package/dist/gestures/ScreenEdgePanGestureRecognizer.js +7 -5
  128. package/dist/gestures/nativeScroll.d.ts +16 -2
  129. package/dist/gestures/nativeScroll.d.ts.map +1 -1
  130. package/dist/gestures/nativeScroll.js +39 -8
  131. package/dist/gestures/recognizerInternals.d.ts +4 -2
  132. package/dist/gestures/recognizerInternals.d.ts.map +1 -1
  133. package/dist/gestures/types.d.ts +17 -0
  134. package/dist/gestures/types.d.ts.map +1 -1
  135. package/dist/gestures/types.js +7 -0
  136. package/dist/gestures/usePressTracker.d.ts.map +1 -1
  137. package/dist/gestures/usePressTracker.js +1 -0
  138. package/dist/gestures/wheelSegmenter.d.ts +56 -0
  139. package/dist/gestures/wheelSegmenter.d.ts.map +1 -0
  140. package/dist/gestures/wheelSegmenter.js +122 -0
  141. package/dist/hooks/useAttachment.d.ts +16 -8
  142. package/dist/hooks/useAttachment.d.ts.map +1 -1
  143. package/dist/hooks/useAttachment.js +35 -15
  144. package/dist/hooks/useFocusTrap.d.ts +15 -0
  145. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  146. package/dist/hooks/useFocusTrap.js +13 -2
  147. package/dist/hooks/usePager.d.ts +261 -0
  148. package/dist/hooks/usePager.d.ts.map +1 -0
  149. package/dist/hooks/usePager.js +1169 -0
  150. package/dist/motion/animator.d.ts.map +1 -1
  151. package/dist/motion/animator.js +140 -19
  152. package/dist/motion/paging.d.ts +69 -0
  153. package/dist/motion/paging.d.ts.map +1 -0
  154. package/dist/motion/paging.js +148 -0
  155. package/dist/motion/uikit/index.d.ts +8 -4
  156. package/dist/motion/uikit/index.d.ts.map +1 -1
  157. package/dist/motion/uikit/index.js +8 -4
  158. package/dist/motion/uikit/paging.d.ts +171 -0
  159. package/dist/motion/uikit/paging.d.ts.map +1 -0
  160. package/dist/motion/uikit/paging.js +192 -0
  161. package/dist/motion/uikit/progressBar.d.ts +79 -0
  162. package/dist/motion/uikit/progressBar.d.ts.map +1 -0
  163. package/dist/motion/uikit/progressBar.js +102 -0
  164. package/dist/motion/uikit/scroll.d.ts +0 -19
  165. package/dist/motion/uikit/scroll.d.ts.map +1 -1
  166. package/dist/motion/uikit/scroll.js +2 -21
  167. package/dist/motion/uikit/slider.d.ts +97 -0
  168. package/dist/motion/uikit/slider.d.ts.map +1 -0
  169. package/dist/motion/uikit/slider.js +111 -0
  170. package/dist/motion/uikit/springs.d.ts +59 -30
  171. package/dist/motion/uikit/springs.d.ts.map +1 -1
  172. package/dist/motion/uikit/springs.js +64 -34
  173. package/dist/motion/uikit/switch.d.ts +166 -0
  174. package/dist/motion/uikit/switch.d.ts.map +1 -0
  175. package/dist/motion/uikit/switch.js +186 -0
  176. package/dist/privateAttributes/pressHoldsDrag.stylex.d.ts +18 -0
  177. package/dist/privateAttributes/pressHoldsDrag.stylex.d.ts.map +1 -0
  178. package/dist/privateAttributes/pressHoldsDrag.stylex.js +28 -0
  179. package/dist/utils/pressFeedback.d.ts.map +1 -1
  180. package/dist/utils/pressFeedback.js +5 -0
  181. package/dist/utils/selectionLock.d.ts +43 -0
  182. package/dist/utils/selectionLock.d.ts.map +1 -1
  183. package/dist/utils/selectionLock.js +394 -6
  184. package/package.json +5 -5
  185. package/src/AppShell/AppShell.doc.mjs +10 -0
  186. package/src/AppShell/AppShell.tsx +48 -2
  187. package/src/Avatar/Avatar.tsx +3 -1
  188. package/src/BottomSheet/BottomSheetPanel.tsx +16 -9
  189. package/src/Breadcrumbs/BreadcrumbItem.tsx +4 -1
  190. package/src/Button/Button.tsx +3 -2
  191. package/src/Chat/Chat.doc.mjs +1 -0
  192. package/src/Chat/ChatComposerInput.doc.mjs +11 -0
  193. package/src/Chat/ChatComposerInput.tsx +11 -0
  194. package/src/Chat/ChatMessage.tsx +2 -2
  195. package/src/Chat/chatMessage.vars.stylex.ts +8 -8
  196. package/src/CommandPalette/CommandPaletteInput.tsx +6 -0
  197. package/src/DateInput/MonthScroller.tsx +63 -5
  198. package/src/DateInput/Wheel.tsx +174 -14
  199. package/src/DateRangeInput/DateRangeInput.doc.mjs +3 -2
  200. package/src/DateRangeInput/DateRangeInput.tsx +117 -71
  201. package/src/Dialog/Dialog.tsx +37 -1
  202. package/src/Drawer/Drawer.tsx +6 -0
  203. package/src/Drawer/useDrawerDialogPresence.ts +4 -0
  204. package/src/DropdownMenu/DropdownMenu.tsx +28 -6
  205. package/src/DropdownMenu/useMenuOverflow.ts +44 -8
  206. package/src/Field/PanelSearchInput.tsx +6 -0
  207. package/src/Field/textEntryTraits.ts +6 -6
  208. package/src/Layer/layerClickBoundary.ts +2 -2
  209. package/src/Layer/usePressPin.ts +516 -0
  210. package/src/Layer/useTouchTrigger.ts +56 -6
  211. package/src/Lightbox/Lightbox.tsx +2 -1
  212. package/src/Link/Link.tsx +3 -0
  213. package/src/Popover/usePopover.tsx +71 -9
  214. package/src/PowerSearch/PowerSearch.tsx +38 -31
  215. package/src/ProgressBar/ProgressBar.doc.mjs +7 -5
  216. package/src/ProgressBar/ProgressBar.tsx +147 -16
  217. package/src/ProgressBar/ProgressBarTouch.ts +109 -0
  218. package/src/SegmentedControl/SegmentedControlItem.tsx +2 -1
  219. package/src/Selector/Selector.tsx +29 -2
  220. package/src/Slider/Slider.doc.mjs +11 -7
  221. package/src/Slider/Slider.tsx +539 -132
  222. package/src/Slider/sliderTouchController.ts +1209 -0
  223. package/src/Slider/useSliderTouch.ts +78 -0
  224. package/src/Switch/Switch.doc.mjs +3 -3
  225. package/src/Switch/Switch.tsx +412 -86
  226. package/src/Switch/switchFill.ts +106 -0
  227. package/src/Switch/switchTouch.ts +194 -0
  228. package/src/Switch/useSwitchTouch.ts +1121 -0
  229. package/src/TabList/Tab.tsx +5 -3
  230. package/src/Tokenizer/Tokenizer.tsx +15 -5
  231. package/src/Tokenizer/TokenizerItemRemovableContext.ts +22 -0
  232. package/src/Tooltip/Tooltip.doc.mjs +21 -6
  233. package/src/Tooltip/Tooltip.tsx +42 -5
  234. package/src/Tooltip/index.ts +1 -0
  235. package/src/Tooltip/useTooltip.doc.mjs +4 -2
  236. package/src/Tooltip/useTooltip.tsx +84 -21
  237. package/src/Typeahead/BaseTypeahead.tsx +16 -8
  238. package/src/Typeahead/Typeahead.tsx +29 -15
  239. package/src/gestures/GestureArena.ts +45 -4
  240. package/src/gestures/GestureHost.ts +958 -15
  241. package/src/gestures/GestureRecognizer.ts +28 -0
  242. package/src/gestures/PanGestureRecognizer.ts +8 -0
  243. package/src/gestures/PinchGestureRecognizer.ts +16 -1
  244. package/src/gestures/PressTracker.ts +10 -0
  245. package/src/gestures/README.md +82 -3
  246. package/src/gestures/ScreenEdgePanGestureRecognizer.ts +7 -5
  247. package/src/gestures/nativeScroll.ts +49 -9
  248. package/src/gestures/recognizerInternals.ts +9 -2
  249. package/src/gestures/types.ts +18 -0
  250. package/src/gestures/usePressTracker.ts +2 -0
  251. package/src/gestures/wheelSegmenter.ts +143 -0
  252. package/src/hooks/useAttachment.ts +56 -14
  253. package/src/hooks/useFocusTrap.ts +26 -2
  254. package/src/hooks/usePager.ts +1446 -0
  255. package/src/motion/MEASUREMENTS.md +36 -27
  256. package/src/motion/README.md +7 -3
  257. package/src/motion/animator.ts +193 -25
  258. package/src/motion/paging.ts +161 -0
  259. package/src/motion/uikit/index.ts +7 -4
  260. package/src/motion/uikit/paging.ts +193 -0
  261. package/src/motion/uikit/progressBar.ts +102 -0
  262. package/src/motion/uikit/scroll.ts +2 -18
  263. package/src/motion/uikit/slider.ts +105 -0
  264. package/src/motion/uikit/springs.ts +53 -32
  265. package/src/motion/uikit/switch.ts +174 -0
  266. package/src/privateAttributes/pressHoldsDrag.stylex.ts +28 -0
  267. package/src/utils/pressFeedback.ts +5 -0
  268. package/src/utils/selectionLock.ts +501 -6
  269. package/dist/motion/selectPage.d.ts +0 -26
  270. package/dist/motion/selectPage.d.ts.map +0 -1
  271. package/dist/motion/selectPage.js +0 -55
  272. package/src/motion/selectPage.ts +0 -55
@@ -3,11 +3,12 @@
3
3
  /**
4
4
  * @file selectionLock.ts
5
5
  * @input The elements a held touch contact presses, and the touch's target
6
- * @output lockSelection: no text selection and no touch callout under the
7
- * held finger until the release runs; and locksSelectionFor: which contacts
8
- * take it
9
- * @position Brought by the recognizers whose hold does something. The gesture
10
- * host calls it and never imports it.
6
+ * @output lockSelection and locksSelectionFor give a held finger's lock.
7
+ * lockDragSelection gives a claimed mouse or pen drag's hold, which keeps
8
+ * the page's selection. startsOnSelectableText and selectionRangesOf are
9
+ * what a mouse or pen press reads.
10
+ * @position Recognizers whose hold does something bring lockSelection; the
11
+ * gesture host imports the drag hold and the press reads.
11
12
  *
12
13
  * SYNC: When modified, update GestureHost.ts and utils/pressFeedback.ts; the
13
14
  * iOS query matches utils/touchPresentation.ts.
@@ -27,7 +28,8 @@
27
28
  //
28
29
  // A mouse never takes the lock, so its released behavior stays (AST-017).
29
30
 
30
- import {containsAlongFlatTree} from './flatTree';
31
+ import {containsAlongFlatTree, parentAcrossShadow} from './flatTree';
32
+ import {subscribeInput} from './inputStream';
31
33
  import {isIosWebKit} from './touchQueries';
32
34
 
33
35
  /** What a held finger must not start on a control or a cell. */
@@ -101,6 +103,499 @@ export function lockSelection(
101
103
  }
102
104
  };
103
105
  }
106
+ return lockStyles(set);
107
+ }
108
+
109
+ /** How a drag's selection hold ends: at a lift, or at a cancel. */
110
+ export interface DragSelectionReleaseOptions {
111
+ /**
112
+ * The press was cancelled (a pointercancel, a blur, a detach), so no
113
+ * mouseup or click follows: the hold ends at once. Called again after a
114
+ * lift, it ends the hold's last task at once too.
115
+ */
116
+ isCancel?: boolean;
117
+ }
118
+
119
+ /**
120
+ * Refuse a new selection while a mouse or pen drag that a recognizer claimed
121
+ * runs, until the returned function runs. `kept` is the page's selection
122
+ * when the press began, and it stays. Anything that replaces it while the
123
+ * drag runs is put back to it. That includes the press's own collapse and a
124
+ * selection the drag started before its claim. So the drag takes no new
125
+ * selection and never clears an existing one. `elements` refuse a
126
+ * selection with inline styles, on every engine. The document refuses one
127
+ * too, because WebKit extends a drag's selection over text outside the
128
+ * elements without another `selectstart`. So a `selectstart` anywhere is
129
+ * refused, and the selection is put back. A `selectstart` does not leave its
130
+ * shadow tree. So the document refuses only one that starts outside every
131
+ * shadow tree, and a claiming element inside one refuses its own. The
132
+ * press's own `mouseup` and `click` come after its `pointerup` in the same
133
+ * task, and WebKit settles the drag's selection there. So the document's
134
+ * hold ends a task after a lift. A cancel ends it at once, because nothing
135
+ * follows a cancel. `onReleased` runs once the document's hold has ended.
136
+ * The page listeners exist only during a press. Idempotent.
137
+ */
138
+ export function lockDragSelection(
139
+ elements: Iterable<Element>,
140
+ view: Window | null | undefined,
141
+ kept: ReadonlyArray<AbstractRange>,
142
+ onReleased?: () => void,
143
+ ): (options?: DragSelectionReleaseOptions) => void {
144
+ const doc = view?.document ?? null;
145
+ const roots = shadowRootsOfRanges(kept);
146
+ const restore = () => {
147
+ const selection = view?.getSelection();
148
+ // The focused field keeps its own selection (a drag never starts in one).
149
+ if (selection == null || isTextEntry(deepActiveElement(doc))) {
150
+ return;
151
+ }
152
+ const held = heldRanges(selection, roots);
153
+ if (kept.length === 0) {
154
+ if (held.some(range => !range.collapsed)) {
155
+ selection.removeAllRanges();
156
+ }
157
+ return;
158
+ }
159
+ // A render can remove what the kept selection held: then there is
160
+ // nothing to put back.
161
+ if (sameRanges(held, kept) || !kept.every(isValidRange)) {
162
+ return;
163
+ }
164
+ selectRanges(selection, kept);
165
+ };
166
+ restore();
167
+ const set = new Set(elements);
168
+ const refuse = (event: Event) => {
169
+ if (!isTextEntry(event.target)) {
170
+ event.preventDefault();
171
+ }
172
+ };
173
+ for (const element of set) {
174
+ element.addEventListener('selectstart', refuse);
175
+ }
176
+ const stopDocument =
177
+ view == null
178
+ ? []
179
+ : [
180
+ subscribeInput(view, 'selectstart', refuse, {cancels: true}),
181
+ subscribeInput(view, 'selectionchange', restore),
182
+ ];
183
+ const unlockStyles = lockStyles(set);
184
+ let stage: 'held' | 'lifted' | 'done' = 'held';
185
+ let timer: number | null = null;
186
+ const releaseDocument = () => {
187
+ if (stage === 'done') {
188
+ return;
189
+ }
190
+ stage = 'done';
191
+ if (timer != null) {
192
+ view?.clearTimeout(timer);
193
+ timer = null;
194
+ }
195
+ try {
196
+ restore();
197
+ } finally {
198
+ for (const stop of stopDocument) {
199
+ stop();
200
+ }
201
+ onReleased?.();
202
+ }
203
+ };
204
+ return ({isCancel = false}: DragSelectionReleaseOptions = {}) => {
205
+ if (stage === 'held') {
206
+ stage = 'lifted';
207
+ for (const element of set) {
208
+ element.removeEventListener('selectstart', refuse);
209
+ }
210
+ unlockStyles();
211
+ // A lift's own mouseup and click follow in its task and must still
212
+ // meet the hold; nothing follows a cancel, which ends it at once.
213
+ if (view == null || isCancel) {
214
+ releaseDocument();
215
+ } else {
216
+ timer = view.setTimeout(releaseDocument, 0);
217
+ }
218
+ } else if (stage === 'lifted' && isCancel) {
219
+ releaseDocument();
220
+ }
221
+ };
222
+ }
223
+
224
+ /**
225
+ * The page's selection now, its ranges copied; none for a collapsed or empty
226
+ * one. A drag's hold keeps what this read when the press began.
227
+ */
228
+ export function selectionRangesOf(
229
+ view: Window | null | undefined,
230
+ ): ReadonlyArray<AbstractRange> {
231
+ const selection = view?.getSelection();
232
+ if (selection == null) {
233
+ return [];
234
+ }
235
+ const composed = composedRangesOf(selection, []);
236
+ if (composed != null) {
237
+ return composed.filter(range => !range.collapsed).map(liveWhereItCan);
238
+ }
239
+ if (selection.isCollapsed) {
240
+ return [];
241
+ }
242
+ const ranges: Range[] = [];
243
+ for (let index = 0; index < selection.rangeCount; index++) {
244
+ ranges.push(selection.getRangeAt(index).cloneRange());
245
+ }
246
+ return ranges;
247
+ }
248
+
249
+ /**
250
+ * Whether a press at (`x`, `y`) lands on text a drag would select. That is a
251
+ * text field, or a character of non-empty text whose element allows a
252
+ * selection. A native button's label, slotted or not, is not selectable text,
253
+ * because the engine starts no selection from a drag inside a button. A point
254
+ * beside text, which a caret query snaps to the nearest character, lands on
255
+ * none. Where the engine has no caret query, nothing is selectable text.
256
+ */
257
+ export function startsOnSelectableText(
258
+ doc: Document,
259
+ x: number,
260
+ y: number,
261
+ target: EventTarget | null,
262
+ ): boolean {
263
+ if (isTextEntry(target)) {
264
+ return true;
265
+ }
266
+ const caret = caretAt(doc, x, y, [
267
+ ...ownShadowRoot(target),
268
+ ...openShadowRootsOf(target),
269
+ ]);
270
+ if (caret == null) {
271
+ return false;
272
+ }
273
+ const {node, offset} = caret;
274
+ if (node.nodeType !== 3 || (node.textContent ?? '').trim() === '') {
275
+ return false;
276
+ }
277
+ // Text directly under a shadow root takes its host's style.
278
+ const element =
279
+ node.parentElement ??
280
+ (node.parentNode?.nodeType === 11
281
+ ? (node.parentNode as ShadowRoot).host
282
+ : null);
283
+ if (element == null || !allowsSelection(element) || isInButton(node)) {
284
+ return false;
285
+ }
286
+ const length = (node.textContent ?? '').length;
287
+ const range = doc.createRange();
288
+ if (typeof range.getClientRects !== 'function') {
289
+ return false;
290
+ }
291
+ for (const start of [offset - 1, offset]) {
292
+ if (start < 0 || start >= length) {
293
+ continue;
294
+ }
295
+ range.setStart(node, start);
296
+ range.setEnd(node, start + 1);
297
+ for (const rect of Array.from(range.getClientRects())) {
298
+ if (
299
+ x >= rect.left &&
300
+ x <= rect.right &&
301
+ y >= rect.top &&
302
+ y <= rect.bottom
303
+ ) {
304
+ return true;
305
+ }
306
+ }
307
+ }
308
+ return false;
309
+ }
310
+
311
+ /** Whether `node` is inside a native button, along the flat tree. */
312
+ function isInButton(node: Node): boolean {
313
+ for (let at: Node | null = node; at != null; at = parentAcrossShadow(at)) {
314
+ if (at.nodeType === 1 && (at as Element).localName === 'button') {
315
+ return true;
316
+ }
317
+ }
318
+ return false;
319
+ }
320
+
321
+ /** The caret position the engine puts at a point, by either query. */
322
+ function caretAt(
323
+ doc: Document,
324
+ x: number,
325
+ y: number,
326
+ shadowRoots: ReadonlyArray<ShadowRoot>,
327
+ ): {node: Node; offset: number} | null {
328
+ const queries = doc as Document & {
329
+ caretPositionFromPoint?: (
330
+ x: number,
331
+ y: number,
332
+ options?: {shadowRoots?: ShadowRoot[]},
333
+ ) => {offsetNode: Node; offset: number} | null;
334
+ caretRangeFromPoint?: (x: number, y: number) => Range | null;
335
+ };
336
+ if (typeof queries.caretPositionFromPoint === 'function') {
337
+ // Without the roots, a point over shadow text answers at its host.
338
+ const position =
339
+ shadowRoots.length > 0
340
+ ? queries.caretPositionFromPoint(x, y, {
341
+ shadowRoots: [...shadowRoots],
342
+ })
343
+ : queries.caretPositionFromPoint(x, y);
344
+ return position == null
345
+ ? null
346
+ : {node: position.offsetNode, offset: position.offset};
347
+ }
348
+ if (typeof queries.caretRangeFromPoint === 'function') {
349
+ const range = queries.caretRangeFromPoint(x, y);
350
+ return range == null
351
+ ? null
352
+ : {node: range.startContainer, offset: range.startOffset};
353
+ }
354
+ return null;
355
+ }
356
+
357
+ /** Whether an element's text can be selected (`user-select` is not none). */
358
+ function allowsSelection(element: Element): boolean {
359
+ const style = element.ownerDocument.defaultView?.getComputedStyle(element);
360
+ if (style == null) {
361
+ return false;
362
+ }
363
+ return (
364
+ style.getPropertyValue('user-select') !== 'none' &&
365
+ style.getPropertyValue('-webkit-user-select') !== 'none'
366
+ );
367
+ }
368
+
369
+ /**
370
+ * The ranges `selection` holds, with boundaries inside `shadowRoots` where the
371
+ * engine reads composed ranges.
372
+ */
373
+ function heldRanges(
374
+ selection: Selection,
375
+ shadowRoots: ReadonlyArray<ShadowRoot>,
376
+ ): ReadonlyArray<AbstractRange> {
377
+ const composed = composedRangesOf(selection, shadowRoots);
378
+ if (composed != null) {
379
+ return composed;
380
+ }
381
+ const ranges: Range[] = [];
382
+ for (let index = 0; index < selection.rangeCount; index++) {
383
+ ranges.push(selection.getRangeAt(index));
384
+ }
385
+ return ranges;
386
+ }
387
+
388
+ /**
389
+ * A live copy of `range` when both boundaries share a root, so it follows
390
+ * DOM changes. A range across roots stays static: no live range holds it.
391
+ */
392
+ function liveWhereItCan(range: StaticRange): AbstractRange {
393
+ const live = range.startContainer.ownerDocument?.createRange();
394
+ if (
395
+ live == null ||
396
+ range.startContainer.getRootNode() !== range.endContainer.getRootNode()
397
+ ) {
398
+ return range;
399
+ }
400
+ live.setStart(range.startContainer, range.startOffset);
401
+ live.setEnd(range.endContainer, range.endOffset);
402
+ return live;
403
+ }
404
+
405
+ /** Whether both of `range`'s boundaries still exist in the document. */
406
+ function isValidRange(range: AbstractRange): boolean {
407
+ return (
408
+ isValidBoundary(range.startContainer, range.startOffset) &&
409
+ isValidBoundary(range.endContainer, range.endOffset)
410
+ );
411
+ }
412
+
413
+ function isValidBoundary(node: Node, offset: number): boolean {
414
+ const length =
415
+ node.nodeType === 3 || node.nodeType === 4 || node.nodeType === 8
416
+ ? (node as CharacterData).length
417
+ : node.childNodes.length;
418
+ return node.isConnected && offset >= 0 && offset <= length;
419
+ }
420
+
421
+ /** Whether two range lists have the same boundaries, in order. */
422
+ function sameRanges(
423
+ held: ReadonlyArray<AbstractRange>,
424
+ kept: ReadonlyArray<AbstractRange>,
425
+ ): boolean {
426
+ return (
427
+ held.length === kept.length &&
428
+ kept.every((range, index) => {
429
+ const other = held[index];
430
+ return (
431
+ other.startContainer === range.startContainer &&
432
+ other.startOffset === range.startOffset &&
433
+ other.endContainer === range.endContainer &&
434
+ other.endOffset === range.endOffset
435
+ );
436
+ })
437
+ );
438
+ }
439
+
440
+ /**
441
+ * Put `ranges` back as the selection. One range goes through
442
+ * `setBaseAndExtent`, which also reaches inside a shadow root.
443
+ */
444
+ function selectRanges(
445
+ selection: Selection,
446
+ ranges: ReadonlyArray<AbstractRange>,
447
+ ): void {
448
+ const [only] = ranges;
449
+ if (ranges.length === 1 && typeof selection.setBaseAndExtent === 'function') {
450
+ selection.setBaseAndExtent(
451
+ only.startContainer,
452
+ only.startOffset,
453
+ only.endContainer,
454
+ only.endOffset,
455
+ );
456
+ return;
457
+ }
458
+ selection.removeAllRanges();
459
+ for (const range of ranges) {
460
+ const copy = range.startContainer.ownerDocument?.createRange();
461
+ if (copy == null) {
462
+ continue;
463
+ }
464
+ copy.setStart(range.startContainer, range.startOffset);
465
+ copy.setEnd(range.endContainer, range.endOffset);
466
+ selection.addRange(copy);
467
+ }
468
+ }
469
+
470
+ /**
471
+ * The selection's composed ranges, reaching into each open shadow root a
472
+ * boundary lands on; null where the engine has no `getComposedRanges`.
473
+ * Without the roots, a selection inside a shadow root reads as one around
474
+ * its host.
475
+ */
476
+ function composedRangesOf(
477
+ selection: Selection,
478
+ known: ReadonlyArray<ShadowRoot>,
479
+ ): StaticRange[] | null {
480
+ const query = (
481
+ selection as Selection & {
482
+ getComposedRanges?: (options?: {
483
+ shadowRoots?: ShadowRoot[];
484
+ }) => StaticRange[];
485
+ }
486
+ ).getComposedRanges;
487
+ if (typeof query !== 'function') {
488
+ return null;
489
+ }
490
+ // Engines take the roots as a dictionary, or, in older releases, as
491
+ // arguments.
492
+ const read = (roots: ShadowRoot[]): StaticRange[] | null => {
493
+ try {
494
+ return query.call(selection, {shadowRoots: roots});
495
+ } catch {
496
+ try {
497
+ return (query as (...roots: ShadowRoot[]) => StaticRange[]).call(
498
+ selection,
499
+ ...roots,
500
+ );
501
+ } catch {
502
+ return null;
503
+ }
504
+ }
505
+ };
506
+ const roots = [...known];
507
+ for (;;) {
508
+ const ranges = read([...roots]);
509
+ if (ranges == null) {
510
+ return null;
511
+ }
512
+ const found = new Set<ShadowRoot>();
513
+ for (const range of ranges) {
514
+ for (const [node, offset] of [
515
+ [range.startContainer, range.startOffset],
516
+ [range.endContainer, range.endOffset],
517
+ ] as const) {
518
+ for (const child of [
519
+ node.childNodes[offset],
520
+ node.childNodes[offset - 1],
521
+ ]) {
522
+ const root =
523
+ child?.nodeType === 1 ? (child as Element).shadowRoot : null;
524
+ if (root != null && !roots.includes(root)) {
525
+ found.add(root);
526
+ }
527
+ }
528
+ }
529
+ }
530
+ if (found.size === 0) {
531
+ return ranges;
532
+ }
533
+ roots.push(...found);
534
+ }
535
+ }
536
+
537
+ /** The open shadow roots on the root chains of `ranges`' boundaries. */
538
+ function shadowRootsOfRanges(
539
+ ranges: ReadonlyArray<AbstractRange>,
540
+ ): ReadonlyArray<ShadowRoot> {
541
+ const roots = new Set<ShadowRoot>();
542
+ for (const range of ranges) {
543
+ for (const root of [
544
+ ...openShadowRootsOf(range.startContainer),
545
+ ...openShadowRootsOf(range.endContainer),
546
+ ]) {
547
+ roots.add(root);
548
+ }
549
+ }
550
+ return [...roots];
551
+ }
552
+
553
+ /**
554
+ * The open shadow root `target` hosts, if any. A press on text directly under
555
+ * a shadow root targets its host.
556
+ */
557
+ function ownShadowRoot(target: EventTarget | null): ShadowRoot[] {
558
+ const root =
559
+ (target as Node | null)?.nodeType === 1
560
+ ? (target as Element).shadowRoot
561
+ : null;
562
+ return root == null ? [] : [root];
563
+ }
564
+
565
+ /** The open shadow roots on `target`'s root chain, innermost first. */
566
+ function openShadowRootsOf(target: EventTarget | null): ShadowRoot[] {
567
+ const roots: ShadowRoot[] = [];
568
+ let root =
569
+ target != null && typeof (target as Node).nodeType === 'number'
570
+ ? (target as Node).getRootNode()
571
+ : null;
572
+ while (root != null && root.nodeType === 11) {
573
+ const host = (root as ShadowRoot).host as Element | undefined;
574
+ if (host == null) {
575
+ break;
576
+ }
577
+ if ((root as ShadowRoot).mode === 'open') {
578
+ roots.push(root as ShadowRoot);
579
+ }
580
+ root = host.getRootNode();
581
+ }
582
+ return roots;
583
+ }
584
+
585
+ /** The focused element, inside open shadow roots too. */
586
+ function deepActiveElement(doc: Document | null): Element | null {
587
+ let active = doc?.activeElement ?? null;
588
+ while (active?.shadowRoot?.activeElement != null) {
589
+ active = active.shadowRoot.activeElement;
590
+ }
591
+ return active;
592
+ }
593
+
594
+ /**
595
+ * The selection-lock styles on `set`, refcounted per element; idempotent
596
+ * release.
597
+ */
598
+ function lockStyles(set: ReadonlySet<Element>): () => void {
104
599
  const locked: Element[] = [];
105
600
  for (const element of set) {
106
601
  const style = styleOf(element);
@@ -1,26 +0,0 @@
1
- /**
2
- * @file selectPage.ts
3
- * @input A release (position, velocity), the page size and count, the
4
- * current page
5
- * @output Exports `selectPage`: the page a paging release settles on
6
- * @position UIScrollView paging (`isPagingEnabled`) on the release policy
7
- * (resolveSnapPoint.ts). Pure: no DOM, no React
8
- *
9
- * SYNC: When modified, update selectPage.test.ts.
10
- */
11
- import type { SnapSelection } from './projection.js';
12
- /**
13
- * UIScrollView paging (`isPagingEnabled`): the page a release settles on, at
14
- * most one page from `currentPage`, chosen by projecting the release with
15
- * the paging projection rate (uikit/scroll.ts; a GUESS until measured).
16
- * Pages are `pageSize` apart starting at 0.
17
- */
18
- export declare function selectPage({ position, velocity, pageSize, pageCount, currentPage, rate, }: {
19
- position: number;
20
- velocity: number;
21
- pageSize: number;
22
- pageCount: number;
23
- currentPage: number;
24
- rate?: number;
25
- }): SnapSelection;
26
- //# sourceMappingURL=selectPage.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"selectPage.d.ts","sourceRoot":"","sources":["../../src/motion/selectPage.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,cAAc,CAAC;AAIhD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,WAAW,EACX,IAAmC,GACpC,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GAAG,aAAa,CAiBhB"}
@@ -1,55 +0,0 @@
1
- // Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- /**
4
- * @file selectPage.ts
5
- * @input A release (position, velocity), the page size and count, the
6
- * current page
7
- * @output Exports `selectPage`: the page a paging release settles on
8
- * @position UIScrollView paging (`isPagingEnabled`) on the release policy
9
- * (resolveSnapPoint.ts). Pure: no DOM, no React
10
- *
11
- * SYNC: When modified, update selectPage.test.ts.
12
- */
13
-
14
- import { resolveSnapPoint } from "./resolveSnapPoint.js";
15
- import { scroll } from "./uikit/scroll.js";
16
-
17
- /**
18
- * UIScrollView paging (`isPagingEnabled`): the page a release settles on, at
19
- * most one page from `currentPage`, chosen by projecting the release with
20
- * the paging projection rate (uikit/scroll.ts; a GUESS until measured).
21
- * Pages are `pageSize` apart starting at 0.
22
- */
23
- export function selectPage({
24
- position,
25
- velocity,
26
- pageSize,
27
- pageCount,
28
- currentPage,
29
- rate = scroll.paging.projectionRate
30
- }) {
31
- const points = Array.from({
32
- length: Math.max(1, pageCount)
33
- }, (_, i) => i * pageSize);
34
- // The release policy (resolveSnapPoint.ts), with the paging rate as the
35
- // seconds of velocity it projects, no flick and one page per release.
36
- const {
37
- index,
38
- projected
39
- } = resolveSnapPoint({
40
- points,
41
- position,
42
- velocity,
43
- flickThreshold: Infinity,
44
- projection: {
45
- seconds: rate / (1 - rate) / 1000
46
- },
47
- fromIndex: currentPage,
48
- maxSteps: 1
49
- });
50
- return {
51
- index,
52
- value: points[index],
53
- projected
54
- };
55
- }
@@ -1,55 +0,0 @@
1
- // Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- /**
4
- * @file selectPage.ts
5
- * @input A release (position, velocity), the page size and count, the
6
- * current page
7
- * @output Exports `selectPage`: the page a paging release settles on
8
- * @position UIScrollView paging (`isPagingEnabled`) on the release policy
9
- * (resolveSnapPoint.ts). Pure: no DOM, no React
10
- *
11
- * SYNC: When modified, update selectPage.test.ts.
12
- */
13
-
14
- import type {SnapSelection} from './projection';
15
- import {resolveSnapPoint} from './resolveSnapPoint';
16
- import {scroll} from './uikit/scroll';
17
-
18
- /**
19
- * UIScrollView paging (`isPagingEnabled`): the page a release settles on, at
20
- * most one page from `currentPage`, chosen by projecting the release with
21
- * the paging projection rate (uikit/scroll.ts; a GUESS until measured).
22
- * Pages are `pageSize` apart starting at 0.
23
- */
24
- export function selectPage({
25
- position,
26
- velocity,
27
- pageSize,
28
- pageCount,
29
- currentPage,
30
- rate = scroll.paging.projectionRate,
31
- }: {
32
- position: number;
33
- velocity: number;
34
- pageSize: number;
35
- pageCount: number;
36
- currentPage: number;
37
- rate?: number;
38
- }): SnapSelection {
39
- const points = Array.from(
40
- {length: Math.max(1, pageCount)},
41
- (_, i) => i * pageSize,
42
- );
43
- // The release policy (resolveSnapPoint.ts), with the paging rate as the
44
- // seconds of velocity it projects, no flick and one page per release.
45
- const {index, projected} = resolveSnapPoint({
46
- points,
47
- position,
48
- velocity,
49
- flickThreshold: Infinity,
50
- projection: {seconds: rate / (1 - rate) / 1000},
51
- fromIndex: currentPage,
52
- maxSteps: 1,
53
- });
54
- return {index, value: points[index], projected};
55
- }