@astryxdesign/core 0.7.8 → 0.7.9-canary.3b44b43

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 (203) hide show
  1. package/dist/Calendar/styles.d.ts +1 -1
  2. package/dist/ClickableCard/ClickableCard.d.ts +1 -1
  3. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  4. package/dist/ClickableCard/ClickableCard.js +5 -1
  5. package/dist/DateInput/PickerWheel.d.ts.map +1 -1
  6. package/dist/DateInput/PickerWheel.js +7 -1
  7. package/dist/DateRangeInput/DateRangeInput.js +1 -1
  8. package/dist/Dialog/Dialog.d.ts +2 -1
  9. package/dist/Dialog/Dialog.d.ts.map +1 -1
  10. package/dist/Dialog/Dialog.js +8 -1
  11. package/dist/Drawer/useDrawerDialogPresence.d.ts +2 -1
  12. package/dist/Drawer/useDrawerDialogPresence.d.ts.map +1 -1
  13. package/dist/Drawer/useDrawerDialogPresence.js +11 -2
  14. package/dist/DropdownMenu/DropdownMenu.d.ts +9 -1
  15. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  16. package/dist/DropdownMenu/DropdownMenu.js +9 -4
  17. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  18. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +3 -2
  19. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  20. package/dist/DropdownMenu/DropdownMenuItem.js +3 -2
  21. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  22. package/dist/DropdownMenu/DropdownMenuRadioItem.js +3 -2
  23. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  24. package/dist/DropdownMenu/DropdownMenuSubMenu.js +3 -2
  25. package/dist/Field/inputDisabledFocus.d.ts +4 -4
  26. package/dist/Field/inputStyles.stylex.d.ts +1 -1
  27. package/dist/FileInput/FileInput.d.ts.map +1 -1
  28. package/dist/FileInput/FileInput.js +16 -2
  29. package/dist/Icon/Icon.d.ts +1 -1
  30. package/dist/InfoTip/InfoTip.d.ts +1 -0
  31. package/dist/InfoTip/InfoTip.d.ts.map +1 -1
  32. package/dist/InfoTip/InfoTip.js +1 -0
  33. package/dist/Item/Item.d.ts +2 -1
  34. package/dist/Item/Item.d.ts.map +1 -1
  35. package/dist/Item/Item.js +29 -6
  36. package/dist/Layer/holdEntranceInput.d.ts +24 -0
  37. package/dist/Layer/holdEntranceInput.d.ts.map +1 -0
  38. package/dist/Layer/holdEntranceInput.js +65 -0
  39. package/dist/Layer/inputHold.d.ts +49 -0
  40. package/dist/Layer/inputHold.d.ts.map +1 -0
  41. package/dist/Layer/inputHold.js +636 -0
  42. package/dist/Layer/preserveAnimations.d.ts +1 -5
  43. package/dist/Layer/preserveAnimations.d.ts.map +1 -1
  44. package/dist/Layer/preserveAnimations.js +12 -214
  45. package/dist/MetadataList/MetadataList.js +1 -1
  46. package/dist/MobileNav/MobileNav.d.ts +11 -1
  47. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  48. package/dist/MobileNav/MobileNav.js +34 -1
  49. package/dist/MoreMenu/MoreMenu.d.ts +5 -3
  50. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  51. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  52. package/dist/RadioList/RadioListItem.js +4 -1
  53. package/dist/SelectableCard/SelectableCard.d.ts +1 -1
  54. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  55. package/dist/SelectableCard/SelectableCard.js +5 -1
  56. package/dist/Spinner/Spinner.d.ts.map +1 -1
  57. package/dist/Spinner/Spinner.js +12 -2
  58. package/dist/Table/plugins/sortable/useTableSortable.js +1 -1
  59. package/dist/Token/Token.d.ts.map +1 -1
  60. package/dist/Token/Token.js +9 -0
  61. package/dist/Token/TokenLink.d.ts +1 -1
  62. package/dist/Token/TokenLink.d.ts.map +1 -1
  63. package/dist/Token/TokenLink.js +4 -1
  64. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  65. package/dist/Tooltip/useTooltip.js +162 -59
  66. package/dist/TreeList/TreeListItem.d.ts +2 -1
  67. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  68. package/dist/TreeList/TreeListItem.js +27 -3
  69. package/dist/astryx.css +30 -7
  70. package/dist/gestures/GestureHost.d.ts +2 -2
  71. package/dist/gestures/GestureHost.d.ts.map +1 -1
  72. package/dist/gestures/GestureHost.js +21 -6
  73. package/dist/gestures/nativeScroll.d.ts +3 -6
  74. package/dist/gestures/nativeScroll.d.ts.map +1 -1
  75. package/dist/gestures/nativeScroll.js +7 -52
  76. package/dist/gestures/wheelSegmenter.d.ts +68 -1
  77. package/dist/gestures/wheelSegmenter.d.ts.map +1 -1
  78. package/dist/gestures/wheelSegmenter.js +199 -10
  79. package/dist/hooks/menuPressGesture.d.ts +133 -21
  80. package/dist/hooks/menuPressGesture.d.ts.map +1 -1
  81. package/dist/hooks/menuPressGesture.js +155 -30
  82. package/dist/hooks/menuPressValues.d.ts +16 -0
  83. package/dist/hooks/menuPressValues.d.ts.map +1 -0
  84. package/dist/hooks/menuPressValues.js +18 -0
  85. package/dist/hooks/useClickableContainer.d.ts +27 -5
  86. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  87. package/dist/hooks/useClickableContainer.js +41 -5
  88. package/dist/hooks/useMenuPress.d.ts +34 -1
  89. package/dist/hooks/useMenuPress.d.ts.map +1 -1
  90. package/dist/hooks/useMenuPress.js +93 -5
  91. package/dist/hooks/usePressFeedback.d.ts +6 -6
  92. package/dist/hooks/usePressFeedback.js +6 -6
  93. package/dist/motion/carryAnimations.d.ts +58 -0
  94. package/dist/motion/carryAnimations.d.ts.map +1 -0
  95. package/dist/motion/carryAnimations.js +305 -0
  96. package/dist/motion/transientCopy.d.ts +4 -2
  97. package/dist/motion/transientCopy.d.ts.map +1 -1
  98. package/dist/motion/transientCopy.js +34 -5
  99. package/dist/motion/uikit/gestures.d.ts +41 -0
  100. package/dist/motion/uikit/gestures.d.ts.map +1 -1
  101. package/dist/motion/uikit/gestures.js +25 -0
  102. package/dist/motion/uikit/index.d.ts +1 -1
  103. package/dist/motion/uikit/index.d.ts.map +1 -1
  104. package/dist/motion/uikit/index.js +1 -1
  105. package/dist/privateAttributes/menuPressLit.stylex.d.ts +52 -0
  106. package/dist/privateAttributes/menuPressLit.stylex.d.ts.map +1 -0
  107. package/dist/privateAttributes/menuPressLit.stylex.js +99 -0
  108. package/dist/privateAttributes/press.stylex.d.ts +139 -0
  109. package/dist/privateAttributes/press.stylex.d.ts.map +1 -0
  110. package/dist/privateAttributes/press.stylex.js +98 -0
  111. package/dist/theme/tokens.stylex.d.ts +2 -2
  112. package/dist/theme/tokens.stylex.js +1 -1
  113. package/dist/utils/flatTree.d.ts +6 -3
  114. package/dist/utils/flatTree.d.ts.map +1 -1
  115. package/dist/utils/flatTree.js +13 -3
  116. package/dist/utils/hitArea.d.ts.map +1 -1
  117. package/dist/utils/hitArea.js +82 -5
  118. package/dist/utils/inputStream.d.ts +14 -1
  119. package/dist/utils/inputStream.d.ts.map +1 -1
  120. package/dist/utils/inputStream.js +40 -9
  121. package/dist/utils/interactionOverlay.stylex.d.ts +2 -1
  122. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  123. package/dist/utils/interactionOverlay.stylex.js +7 -2
  124. package/dist/utils/pressFeedback.d.ts +7 -0
  125. package/dist/utils/pressFeedback.d.ts.map +1 -1
  126. package/dist/utils/pressFeedback.js +32 -7
  127. package/dist/utils/pressGesture.d.ts +6 -0
  128. package/dist/utils/pressGesture.d.ts.map +1 -1
  129. package/dist/utils/pressGesture.js +17 -0
  130. package/dist/utils/rowPressLayer.stylex.d.ts +14 -0
  131. package/dist/utils/rowPressLayer.stylex.d.ts.map +1 -0
  132. package/dist/utils/rowPressLayer.stylex.js +25 -0
  133. package/dist/utils/scrollView.d.ts +25 -0
  134. package/dist/utils/scrollView.d.ts.map +1 -0
  135. package/dist/utils/scrollView.js +108 -0
  136. package/dist/utils/selectionLock.d.ts.map +1 -1
  137. package/dist/utils/selectionLock.js +1 -10
  138. package/dist/utils/triggerFocus.d.ts +5 -0
  139. package/dist/utils/triggerFocus.d.ts.map +1 -1
  140. package/dist/utils/triggerFocus.js +9 -2
  141. package/package.json +5 -5
  142. package/src/ClickableCard/ClickableCard.tsx +8 -1
  143. package/src/DateInput/PickerWheel.tsx +7 -2
  144. package/src/DateRangeInput/DateRangeInput.tsx +1 -1
  145. package/src/Dialog/Dialog.doc.mjs +1 -1
  146. package/src/Dialog/Dialog.tsx +8 -1
  147. package/src/Drawer/Drawer.doc.mjs +1 -1
  148. package/src/Drawer/useDrawerDialogPresence.ts +9 -2
  149. package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -2
  150. package/src/DropdownMenu/DropdownMenu.tsx +29 -5
  151. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +5 -1
  152. package/src/DropdownMenu/DropdownMenuItem.tsx +8 -4
  153. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +5 -1
  154. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +5 -1
  155. package/src/FileInput/FileInput.tsx +16 -2
  156. package/src/InfoTip/InfoTip.doc.mjs +26 -2
  157. package/src/InfoTip/InfoTip.tsx +1 -0
  158. package/src/Item/Item.tsx +51 -9
  159. package/src/Layer/holdEntranceInput.ts +84 -0
  160. package/src/Layer/inputHold.ts +734 -0
  161. package/src/Layer/preserveAnimations.ts +12 -275
  162. package/src/MetadataList/MetadataList.tsx +1 -1
  163. package/src/MobileNav/MobileNav.doc.mjs +3 -3
  164. package/src/MobileNav/MobileNav.tsx +34 -1
  165. package/src/MoreMenu/MoreMenu.doc.mjs +2 -4
  166. package/src/MoreMenu/MoreMenu.tsx +5 -3
  167. package/src/RadioList/RadioListItem.tsx +4 -5
  168. package/src/SelectableCard/SelectableCard.tsx +8 -1
  169. package/src/Spinner/Spinner.tsx +13 -3
  170. package/src/Table/plugins/sortable/useTableSortable.tsx +1 -1
  171. package/src/Token/Token.tsx +18 -1
  172. package/src/Token/TokenLink.tsx +7 -1
  173. package/src/Tooltip/useTooltip.tsx +175 -79
  174. package/src/TreeList/TreeListItem.tsx +36 -2
  175. package/src/gestures/GestureHost.ts +21 -5
  176. package/src/gestures/README.md +12 -4
  177. package/src/gestures/nativeScroll.ts +12 -68
  178. package/src/gestures/wheelSegmenter.ts +243 -10
  179. package/src/hooks/menuPressGesture.ts +244 -38
  180. package/src/hooks/menuPressValues.ts +18 -0
  181. package/src/hooks/useClickableContainer.doc.mjs +8 -2
  182. package/src/hooks/useClickableContainer.ts +72 -6
  183. package/src/hooks/useMenuPress.ts +118 -11
  184. package/src/hooks/usePressFeedback.doc.mjs +3 -3
  185. package/src/hooks/usePressFeedback.ts +6 -6
  186. package/src/motion/MEASUREMENTS.md +11 -0
  187. package/src/motion/carryAnimations.ts +389 -0
  188. package/src/motion/transientCopy.ts +45 -5
  189. package/src/motion/uikit/gestures.ts +22 -0
  190. package/src/motion/uikit/index.ts +1 -0
  191. package/src/privateAttributes/menuPressLit.stylex.ts +104 -0
  192. package/src/privateAttributes/press.stylex.ts +244 -0
  193. package/src/theme/tokens.stylex.ts +1 -1
  194. package/src/utils/flatTree.ts +15 -3
  195. package/src/utils/hitArea.ts +107 -5
  196. package/src/utils/inputStream.ts +47 -10
  197. package/src/utils/interactionOverlay.stylex.ts +7 -2
  198. package/src/utils/pressFeedback.ts +31 -6
  199. package/src/utils/pressGesture.ts +24 -1
  200. package/src/utils/rowPressLayer.stylex.ts +27 -0
  201. package/src/utils/scrollView.ts +135 -0
  202. package/src/utils/selectionLock.ts +5 -10
  203. package/src/utils/triggerFocus.ts +17 -4
@@ -0,0 +1,636 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file inputHold.ts
7
+ * @input A layer root, what ends its hold (a promise or an animator handle),
8
+ * and the hold's scope
9
+ * @output holdInput, holdsTouch, isInHeldLayer and isInputShield: input held
10
+ * away from a layer while it presents or dismisses
11
+ * @position The Layer module's one input hold, for components whose layer
12
+ * presents under a finger
13
+ *
14
+ * SYNC: When modified, update inputHold.test.tsx,
15
+ * __tests__/inputHold.interaction.browser.spec.ts and the holdInput callers.
16
+ */
17
+ import { containsAlongFlatTree, deepActiveElement, parentAcrossShadow } from "../utils/flatTree.js";
18
+ import { getLastPointerType, subscribeInput } from "../utils/inputStream.js";
19
+
20
+ /**
21
+ * What a hold does. `'viewport'` and `'layer'` add one shield over the whole
22
+ * viewport. `'layer'` also makes the layer's content inert and moves focus
23
+ * out of it. `'surface'` shields only the layer's own box, and the page
24
+ * around it stays live.
25
+ */
26
+
27
+ /** What ends a hold: a promise, or an animator handle (its `finished`). */
28
+
29
+ /** A document while any of its layers is held. */
30
+
31
+ /** A document's press hold, while its window listeners are on. */
32
+
33
+ const held = new WeakMap();
34
+ const heldDocuments = new WeakMap();
35
+ const pressHolds = new WeakMap();
36
+ const liveShields = new WeakSet();
37
+
38
+ /** Whether the shared input stream last delivered a mouse's pointer event. */
39
+ function isMouseLast(doc) {
40
+ const view = doc?.defaultView;
41
+ return view != null && getLastPointerType(view) === 'mouse';
42
+ }
43
+
44
+ /**
45
+ * Whether `node` is the shield of a live hold. A hit test that must see past
46
+ * held input takes the first element of `elementsFromPoint` that is not one.
47
+ */
48
+ export function isInputShield(node) {
49
+ return node != null && liveShields.has(node);
50
+ }
51
+
52
+ /**
53
+ * Whether `target` is inside a held layer. It walks the flat tree as the
54
+ * gesture host does: a slotted node goes to its slot, and a shadow root to
55
+ * its host.
56
+ */
57
+ export function isInHeldLayer(target) {
58
+ for (let node = target; node != null; node = parentAcrossShadow(node)) {
59
+ if (node instanceof Element && held.has(node)) {
60
+ return true;
61
+ }
62
+ }
63
+ return false;
64
+ }
65
+
66
+ /**
67
+ * Whether the gesture host holds `target` away for the current pointer. A
68
+ * dismissing layer holds every pointer. A presenting layer holds every
69
+ * pointer but a mouse, which keeps today's behavior. The host asks this for
70
+ * every touch. So this module imports nothing from gestures/, and a
71
+ * root-entry component can hold its layer without gesture code. With nothing
72
+ * held, this answers false.
73
+ */
74
+ export function holdsTouch(target) {
75
+ const doc = target instanceof Document ? target : target.ownerDocument;
76
+ // The gesture host asks for every touch: with nothing held, answer at once.
77
+ if (doc == null || !heldDocuments.has(doc)) {
78
+ return false;
79
+ }
80
+ // What lands on a shield is held, a mouse's press included.
81
+ if (isInputShield(target)) {
82
+ return true;
83
+ }
84
+ let isHeld = false;
85
+ for (let node = target; node != null; node = parentAcrossShadow(node)) {
86
+ const state = node instanceof Element ? held.get(node) : undefined;
87
+ if (state != null) {
88
+ if (state.layer > 0) {
89
+ return true;
90
+ }
91
+ isHeld = true;
92
+ }
93
+ }
94
+ return isHeld && !isMouseLast(doc);
95
+ }
96
+
97
+ /**
98
+ * Starts holding `layer`. A document's first held layer turns its press hold
99
+ * on, with one stream subscription per event type, however many layers are
100
+ * held. The hold subscribes to nothing while nothing is held.
101
+ */
102
+ function startHolding(layer) {
103
+ const doc = layer.ownerDocument;
104
+ let state = heldDocuments.get(doc);
105
+ if (state == null) {
106
+ state = {
107
+ layers: new Set()
108
+ };
109
+ heldDocuments.set(doc, state);
110
+ }
111
+ state.layers.add(layer);
112
+ pressHoldFor(doc)?.hold();
113
+ }
114
+
115
+ /** Stops holding `layer`; after the document's last one, ends its holding. */
116
+ function stopHolding(layer) {
117
+ heldDocuments.get(layer.ownerDocument)?.layers.delete(layer);
118
+ liveLayers(layer.ownerDocument);
119
+ }
120
+
121
+ /**
122
+ * The document's held layers still in it, oldest first. A layer that has
123
+ * left the document holds nothing more. When none is left, `holdsTouch`
124
+ * answers false and the press hold takes no new press. Its subscriptions end
125
+ * once the presses it holds have ended.
126
+ */
127
+ function liveLayers(doc) {
128
+ const state = heldDocuments.get(doc);
129
+ if (state == null) {
130
+ return [];
131
+ }
132
+ for (const layer of state.layers) {
133
+ if (!layer.isConnected) {
134
+ state.layers.delete(layer);
135
+ }
136
+ }
137
+ if (state.layers.size > 0) {
138
+ return [...state.layers];
139
+ }
140
+ heldDocuments.delete(doc);
141
+ pressHolds.get(doc)?.release();
142
+ return [];
143
+ }
144
+ const SWALLOWED = ['pointerdown', 'pointerup', 'touchstart', 'touchend', 'mousedown', 'mouseup', 'click', 'contextmenu'];
145
+ const swallow = event => {
146
+ // A touch's start stays uncancelled: cancelling it would cancel the page's
147
+ // pinch-zoom too. The shield's `touch-action: pinch-zoom` keeps a finger
148
+ // from panning. A tap's mouse events and click land on the shield, which
149
+ // swallows them.
150
+ if (event.cancelable && event.type !== 'touchstart') {
151
+ event.preventDefault();
152
+ }
153
+ event.stopPropagation();
154
+ };
155
+
156
+ /**
157
+ * A transparent cover inside `layer`: of the viewport from the top layer
158
+ * (`isFull`), or of the layer's own box alone. Because the cover is inside
159
+ * the layer, a tap on it lands in the layer, and a light-dismissing layer
160
+ * stays open.
161
+ */
162
+ function addShield(layer, isFull) {
163
+ const shield = layer.ownerDocument.createElement('div');
164
+ if (isFull) {
165
+ shield.setAttribute('popover', 'manual');
166
+ }
167
+ shield.setAttribute('aria-hidden', 'true');
168
+ Object.assign(shield.style, {
169
+ position: isFull ? 'fixed' : 'absolute',
170
+ inset: '0',
171
+ width: isFull ? '100vw' : '100%',
172
+ height: isFull ? '100vh' : '100%',
173
+ // Above the layer's own content.
174
+ zIndex: isFull ? 'auto' : '2147483647',
175
+ maxWidth: 'none',
176
+ maxHeight: 'none',
177
+ margin: '0',
178
+ padding: '0',
179
+ border: '0',
180
+ background: 'transparent',
181
+ overflow: 'hidden',
182
+ touchAction: 'pinch-zoom',
183
+ // These two inherited properties decide hit-testing. A layer root can opt
184
+ // out of hits, as a full-viewport layer does when a backdrop child takes
185
+ // them. Inherited from such a root, they would let a held tap fall through
186
+ // to the page.
187
+ pointerEvents: 'auto',
188
+ visibility: 'visible'
189
+ });
190
+ for (const type of SWALLOWED) {
191
+ shield.addEventListener(type, swallow, {
192
+ capture: true,
193
+ passive: false
194
+ });
195
+ }
196
+ layer.append(shield);
197
+ liveShields.add(shield);
198
+ if (isFull && typeof shield.showPopover === 'function') {
199
+ try {
200
+ shield.showPopover();
201
+ } catch {
202
+ // Not connected, or popovers unsupported: the shield still covers
203
+ // what it can as a fixed element.
204
+ }
205
+ }
206
+ return shield;
207
+ }
208
+
209
+ /**
210
+ * Holds input away from `layer` until `until` settles. Returns the release
211
+ * (idempotent), for a hold that ends early. Holds on one layer stack, and
212
+ * the layer comes back when the last one ends. Each event is judged by its
213
+ * own pointer, never by the primary pointer. So a mouse on a touch screen
214
+ * keeps its clicks during a presentation.
215
+ */
216
+ export function holdInput(layer, {
217
+ until,
218
+ scope
219
+ }) {
220
+ let state = held.get(layer);
221
+ if (state == null) {
222
+ state = {
223
+ viewport: 0,
224
+ layer: 0,
225
+ surface: 0,
226
+ shield: null,
227
+ isShieldFull: false,
228
+ inertContent: null
229
+ };
230
+ held.set(layer, state);
231
+ }
232
+ startHolding(layer);
233
+ const isFull = scope !== 'surface';
234
+ if (state.shield != null && isFull && !state.isShieldFull) {
235
+ // A surface hold's shield gives way to one over the viewport.
236
+ liveShields.delete(state.shield);
237
+ state.shield.remove();
238
+ state.shield = null;
239
+ }
240
+ if (state.shield == null) {
241
+ state.isShieldFull = isFull || state.viewport + state.layer > 0;
242
+ state.shield = addShield(layer, state.isShieldFull);
243
+ // A presentation lets a mouse through from the start when the stream's
244
+ // last pointer was a mouse. A press where the mouse rests then acts.
245
+ if (scope !== 'layer' && isMouseLast(layer.ownerDocument)) {
246
+ state.shield.style.pointerEvents = 'none';
247
+ }
248
+ }
249
+ if (scope === 'viewport') {
250
+ state.viewport += 1;
251
+ } else if (scope === 'surface') {
252
+ state.surface += 1;
253
+ } else {
254
+ state.layer += 1;
255
+ // A dismissal's shield takes every hit, a mouse's included.
256
+ state.shield.style.pointerEvents = 'auto';
257
+ if (state.layer === 1) {
258
+ state.inertContent = makeContentInert(layer, state.shield);
259
+ }
260
+ }
261
+ let isReleased = false;
262
+ const release = () => {
263
+ if (isReleased) {
264
+ return;
265
+ }
266
+ isReleased = true;
267
+ const current = held.get(layer);
268
+ if (current == null) {
269
+ return;
270
+ }
271
+ if (scope === 'viewport') {
272
+ current.viewport -= 1;
273
+ } else if (scope === 'surface') {
274
+ current.surface -= 1;
275
+ } else {
276
+ current.layer -= 1;
277
+ if (current.layer === 0 && current.inertContent != null) {
278
+ restoreInert(current.inertContent);
279
+ current.inertContent = null;
280
+ }
281
+ }
282
+ if (current.viewport === 0 && current.layer === 0 && current.surface === 0) {
283
+ if (current.shield != null) {
284
+ liveShields.delete(current.shield);
285
+ current.shield.remove();
286
+ }
287
+ held.delete(layer);
288
+ stopHolding(layer);
289
+ }
290
+ };
291
+ const end = 'finished' in until ? until.finished : until;
292
+ void Promise.resolve(end).then(release, release);
293
+ return release;
294
+ }
295
+ const HELD_POINTERS = new Set(['touch', 'pen']);
296
+
297
+ /**
298
+ * How long after a held press ends its click and compatibility mouse events
299
+ * still count as its own (WebKit sends them after `touchend`).
300
+ */
301
+ const CLICK_WINDOW_MS = 800;
302
+
303
+ /** How long a held press is followed past the hold's end, at most. */
304
+ const STRAY_PRESS_MS = 10_000;
305
+ const POINTER_EVENTS = ['pointerdown', 'pointermove', 'pointerup', 'pointercancel'];
306
+ const TOUCH_EVENTS = ['touchstart', 'touchmove', 'touchend', 'touchcancel'];
307
+ const PRESS_EVENTS = ['mousedown', 'mouseup', 'click', 'auxclick', 'dblclick', 'contextmenu'];
308
+
309
+ /** The document's press hold, turned on if it is off. */
310
+ function pressHoldFor(doc) {
311
+ let hold = pressHolds.get(doc) ?? null;
312
+ if (hold == null) {
313
+ hold = createPressHold(doc);
314
+ if (hold != null) {
315
+ pressHolds.set(doc, hold);
316
+ }
317
+ }
318
+ return hold;
319
+ }
320
+
321
+ /**
322
+ * Holds each finger or pen press that lands in `doc`'s window while any of
323
+ * its layers is held. Each press is held to its own end, even past the
324
+ * hold's end. A moving mouse passes through a presentation's shield. The
325
+ * hold hears input through the shared input stream, before its other
326
+ * subscribers, and stops each held event there. So no document or element
327
+ * listener hears it, and neither does press feedback or the gesture host.
328
+ * There is one per document. Its subscriptions end once nothing is held and
329
+ * the held presses and their clicks have ended.
330
+ */
331
+ function createPressHold(doc) {
332
+ const view = doc.defaultView;
333
+ if (view == null) {
334
+ return null;
335
+ }
336
+ /** Whether a new press is held: until the release. */
337
+ let isHolding = true;
338
+ let isListening = true;
339
+ /** The held presses still down, by pointer id and by touch identifier. */
340
+ const pointers = new Set();
341
+ const touches = new Set();
342
+ /** Whose press the next click is: a held one's, or any other's. */
343
+ let lastPress = null;
344
+ /** Until when a click with no pointer type is a held press's. */
345
+ let clickUntil = -Infinity;
346
+ let timer;
347
+ const now = () => view.performance.now();
348
+ const isPressDown = () => pointers.size > 0 || touches.size > 0;
349
+ const swallow = event => {
350
+ if (event.cancelable) {
351
+ event.preventDefault();
352
+ }
353
+ event.stopImmediatePropagation();
354
+ };
355
+
356
+ /** After the release: stops once the last held press and its click end. */
357
+ const settle = () => {
358
+ if (isHolding || !isListening) {
359
+ return;
360
+ }
361
+ if (timer !== undefined) {
362
+ clearTimeout(timer);
363
+ }
364
+ if (isPressDown()) {
365
+ timer = setTimeout(stop, STRAY_PRESS_MS);
366
+ return;
367
+ }
368
+ const wait = clickUntil - now();
369
+ if (wait > 0) {
370
+ timer = setTimeout(stop, wait);
371
+ } else {
372
+ stop();
373
+ }
374
+ };
375
+
376
+ /** Whether a layer is still held (one that left the document is not). */
377
+ const holdsNewPress = () => {
378
+ if (isHolding) {
379
+ liveLayers(doc);
380
+ }
381
+ return isHolding;
382
+ };
383
+
384
+ /**
385
+ * Whether a new press that lands on `target` is held. While a layer is held
386
+ * over the viewport, a press anywhere is held. While a layer is held only
387
+ * to its box (a `'surface'` hold), only a press on the layer is held. The
388
+ * page around it stays live.
389
+ */
390
+ const holdsPressAt = target => {
391
+ if (!holdsNewPress()) {
392
+ return false;
393
+ }
394
+ for (const layer of liveLayers(doc)) {
395
+ const state = held.get(layer);
396
+ if (state == null) {
397
+ continue;
398
+ }
399
+ if (state.viewport > 0 || state.layer > 0) {
400
+ return true;
401
+ }
402
+ if (state.surface > 0 && target instanceof Node && containsAlongFlatTree(layer, target)) {
403
+ return true;
404
+ }
405
+ }
406
+ return false;
407
+ };
408
+ const pressEnded = () => {
409
+ clickUntil = now() + CLICK_WINDOW_MS;
410
+ settle();
411
+ };
412
+
413
+ /** A shield takes hits for a finger or a pen, not for a moving mouse. */
414
+ const followPointer = pointerType => {
415
+ for (const layer of liveLayers(doc)) {
416
+ const state = held.get(layer);
417
+ if (state?.shield == null) {
418
+ continue;
419
+ }
420
+ const value = pointerType === 'mouse' && state.layer === 0 ? 'none' : 'auto';
421
+ if (state.shield.style.pointerEvents !== value) {
422
+ state.shield.style.pointerEvents = value;
423
+ }
424
+ }
425
+ };
426
+
427
+ /**
428
+ * A held press that landed past the shield, which had let a mouse through,
429
+ * is captured to the shield. Its release then stays the hold's, and a
430
+ * light-dismissing layer stays open. A synthetic press has no active
431
+ * pointer to capture, so `setPointerCapture` throws NotFoundError. Its
432
+ * events are swallowed all the same.
433
+ */
434
+ const captureToShield = event => {
435
+ if (!event.isTrusted) {
436
+ return;
437
+ }
438
+ const top = liveLayers(doc).at(-1);
439
+ const shield = top == null ? null : held.get(top)?.shield;
440
+ if (shield == null || isInputShield(event.target)) {
441
+ return;
442
+ }
443
+ shield.setPointerCapture(event.pointerId);
444
+ };
445
+ const onPointer = event => {
446
+ const {
447
+ pointerId,
448
+ pointerType,
449
+ type
450
+ } = event;
451
+ // A finger or a pen raises the shields. Only a mouse that moves with no
452
+ // button down lowers them. So a mouse press that began on a shield stays
453
+ // the shield's to its end. Otherwise its click would go to the two
454
+ // targets' common ancestor, a dialog that reads it as a backdrop click.
455
+ const isFollowed = HELD_POINTERS.has(pointerType) ? type === 'pointerdown' || type === 'pointermove' : pointerType === 'mouse' && type === 'pointermove' && event.buttons === 0;
456
+ if (isFollowed && holdsNewPress()) {
457
+ followPointer(pointerType);
458
+ }
459
+ if (type === 'pointerdown') {
460
+ if (!HELD_POINTERS.has(pointerType) || !holdsPressAt(event.target)) {
461
+ // A mouse's press, or one after the hold: its click is its own, and a
462
+ // held press with its id ended unheard.
463
+ pointers.delete(pointerId);
464
+ lastPress = 'free';
465
+ clickUntil = -Infinity;
466
+ settle();
467
+ return false;
468
+ }
469
+ pointers.add(pointerId);
470
+ lastPress = 'held';
471
+ captureToShield(event);
472
+ } else if (!pointers.has(pointerId)) {
473
+ return false;
474
+ }
475
+ // Canceling the press keeps the browser from sending its compatibility
476
+ // mouse events, and with them a focus move.
477
+ swallow(event);
478
+ if (type === 'pointerup' || type === 'pointercancel') {
479
+ pointers.delete(pointerId);
480
+ pressEnded();
481
+ }
482
+ return true;
483
+ };
484
+ const onTouch = event => {
485
+ const {
486
+ changedTouches,
487
+ touches: down,
488
+ type
489
+ } = event;
490
+ const changed = Array.from(changedTouches);
491
+ if (type === 'touchstart') {
492
+ // A held touch that the screen no longer has ended unheard. Its target,
493
+ // the shield, left the document at the release, so its end reached no
494
+ // listener here.
495
+ const live = new Set(Array.from(down, touch => touch.identifier));
496
+ for (const identifier of touches) {
497
+ if (!live.has(identifier)) {
498
+ touches.delete(identifier);
499
+ }
500
+ }
501
+ const heldTouches = changed.filter(touch => holdsPressAt(touch.target));
502
+ if (heldTouches.length === 0) {
503
+ // A touch after the hold, or one beside a layer held only to its
504
+ // box, is a new one, whatever its identifier.
505
+ for (const touch of changed) {
506
+ touches.delete(touch.identifier);
507
+ }
508
+ lastPress = 'free';
509
+ clickUntil = -Infinity;
510
+ settle();
511
+ return false;
512
+ }
513
+ for (const touch of heldTouches) {
514
+ touches.add(touch.identifier);
515
+ }
516
+ lastPress = 'held';
517
+ // Never canceled: canceling a touch's start would cancel the page's
518
+ // pinch-zoom with it.
519
+ event.stopImmediatePropagation();
520
+ return true;
521
+ }
522
+ if (!changed.some(touch => touches.has(touch.identifier))) {
523
+ return false;
524
+ }
525
+ if (type === 'touchmove') {
526
+ event.stopImmediatePropagation();
527
+ // One finger pans nothing; two still zoom the page.
528
+ if (down.length === 1 && event.cancelable) {
529
+ event.preventDefault();
530
+ }
531
+ return true;
532
+ }
533
+ // A canceled end sends no click and no compatibility mouse events.
534
+ swallow(event);
535
+ for (const touch of changed) {
536
+ touches.delete(touch.identifier);
537
+ }
538
+ pressEnded();
539
+ return true;
540
+ };
541
+ const onPress = event => {
542
+ // A click a key or assistive technology sends counts no press.
543
+ if (event.detail === 0) {
544
+ return false;
545
+ }
546
+ // A click that names its pointer is judged by that pointer. A
547
+ // compatibility mouse event, or a click sent as a plain mouse event,
548
+ // belongs to the last press.
549
+ const {
550
+ pointerType
551
+ } = event;
552
+ const isHeldPress = lastPress === 'held' && (pointerType == null || pointerType === '' || HELD_POINTERS.has(pointerType));
553
+ if (isHeldPress && (isPressDown() || now() <= clickUntil)) {
554
+ swallow(event);
555
+ return true;
556
+ }
557
+ return false;
558
+ };
559
+ function stop() {
560
+ if (!isListening) {
561
+ return;
562
+ }
563
+ isListening = false;
564
+ if (pressHolds.get(doc) === pressHold) {
565
+ pressHolds.delete(doc);
566
+ }
567
+ if (timer !== undefined) {
568
+ clearTimeout(timer);
569
+ }
570
+ for (const unsubscribe of unsubscribers.splice(0)) {
571
+ unsubscribe();
572
+ }
573
+ }
574
+
575
+ // A touch's start and cancel are never canceled, so their subscriptions
576
+ // keep the stream passive for them. The hold hears each event first, so a
577
+ // held one reaches no other subscriber: press feedback and the gesture host
578
+ // never hear it.
579
+ const unsubscribers = [...POINTER_EVENTS.map(type => subscribeInput(view, type, onPointer, {
580
+ cancels: true,
581
+ first: true
582
+ })), ...TOUCH_EVENTS.map(type => subscribeInput(view, type, onTouch, {
583
+ cancels: type === 'touchmove' || type === 'touchend',
584
+ first: true
585
+ })), ...PRESS_EVENTS.map(type => subscribeInput(view, type, onPress, {
586
+ cancels: true,
587
+ first: true
588
+ }))];
589
+ const pressHold = {
590
+ hold() {
591
+ isHolding = true;
592
+ if (timer !== undefined) {
593
+ clearTimeout(timer);
594
+ timer = undefined;
595
+ }
596
+ },
597
+ release() {
598
+ if (!isHolding) {
599
+ return;
600
+ }
601
+ isHolding = false;
602
+ settle();
603
+ }
604
+ };
605
+ return pressHold;
606
+ }
607
+
608
+ /**
609
+ * Makes the layer's content inert, except the shield, and moves focus out of
610
+ * it. An element that turns inert while focused keeps its focus and its key
611
+ * activation. Enter and Space still click such a button until the engine's
612
+ * focus fixup runs, if it runs. Blurring it leaves focus on the document, as
613
+ * that fixup does. The layer element itself stays live. An inert subtree is
614
+ * no hit target, so the shield inside the layer still catches every hit.
615
+ */
616
+ function makeContentInert(layer, shield) {
617
+ const content = new Map();
618
+ for (const child of layer.children) {
619
+ if (child !== shield) {
620
+ content.set(child, child.hasAttribute('inert'));
621
+ child.setAttribute('inert', '');
622
+ }
623
+ }
624
+ const focused = deepActiveElement(layer.ownerDocument);
625
+ if (focused instanceof HTMLElement && [...content.keys()].some(child => containsAlongFlatTree(child, focused))) {
626
+ focused.blur();
627
+ }
628
+ return content;
629
+ }
630
+ function restoreInert(content) {
631
+ for (const [element, wasInert] of content) {
632
+ if (!wasInert) {
633
+ element.removeAttribute('inert');
634
+ }
635
+ }
636
+ }
@@ -1,11 +1,7 @@
1
1
  /**
2
2
  * Run `reset`, which takes `root` out of rendering and back, and put every
3
3
  * CSS animation and transition of `root` and its subtree back in the state it
4
- * was in before. Recreated animations are matched to the ones they replace by
5
- * element, pseudo-element and name, in order, and then by name alone (an
6
- * effect script moved to another element or pseudo-element); a recreated
7
- * animation with no predecessor (an entry animation that had already played
8
- * and been dropped) is finished.
4
+ * was in before.
9
5
  */
10
6
  export declare function preserveAnimations(root: Element, reset: () => void): void;
11
7
  //# sourceMappingURL=preserveAnimations.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"preserveAnimations.d.ts","sourceRoot":"","sources":["../../src/Layer/preserveAnimations.ts"],"names":[],"mappings":"AAoPA;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,IAAI,GAAG,IAAI,CAkDzE"}
1
+ {"version":3,"file":"preserveAnimations.d.ts","sourceRoot":"","sources":["../../src/Layer/preserveAnimations.ts"],"names":[],"mappings":"AA0BA;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,IAAI,GAAG,IAAI,CASzE"}