@jsenv/dom 0.17.18 → 0.17.20

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 (2) hide show
  1. package/dist/jsenv_dom.js +211 -44
  2. package/package.json +1 -1
package/dist/jsenv_dom.js CHANGED
@@ -261,11 +261,22 @@ const chainEvent = (customEvent, parentEvent) => {
261
261
  if (!parentEvent) {
262
262
  return customEvent;
263
263
  }
264
- if (!customEvent.detail) {
265
- console.warn(
266
- `Event "${customEvent.type}" has no detail object. Cannot chain to parent event "${parentEvent.type}".`,
267
- );
268
- return customEvent;
264
+ if (!customEvent.detail || typeof customEvent.detail !== "object") {
265
+ // A native event has nowhere to hang the chain: `Event` has no detail at
266
+ // all and `UIEvent` (so `InputEvent` too) exposes it as a readonly number.
267
+ // Give it an own detail object, shadowing the prototype getter, so a
268
+ // synthetic event dispatched on behalf of a gesture can still say what
269
+ // caused it.
270
+ if (nativeDetailHasMeaning(customEvent)) {
271
+ console.warn(
272
+ `Chaining "${customEvent.type}" to "${parentEvent.type}" replaces its native detail (${customEvent.detail}), which carries the click count on this event type. Chain a custom event instead, or read the click count before chaining.`,
273
+ );
274
+ }
275
+ Object.defineProperty(customEvent, "detail", {
276
+ value: {},
277
+ configurable: true,
278
+ enumerable: true,
279
+ });
269
280
  }
270
281
  // Always build eventChain from the first wrapping so callers can rely on it
271
282
  // being present whenever `parentEvent` is set.
@@ -309,6 +320,24 @@ const findEvent = (event, predicate) => {
309
320
  return undefined;
310
321
  };
311
322
 
323
+ // `detail` is a click count on the pointer events that define one, and 0
324
+ // everywhere else (`input`, `focus`, `wheel`…). Overwriting it there loses the
325
+ // only way to tell a real click from a keyboard/programmatic one (detail === 0),
326
+ // so those events must not be chained.
327
+ const EVENT_TYPES_WITH_MEANINGFUL_DETAIL = new Set([
328
+ "click",
329
+ "auxclick",
330
+ "dblclick",
331
+ "mousedown",
332
+ "mouseup",
333
+ ]);
334
+ const nativeDetailHasMeaning = (event) => {
335
+ if (EVENT_TYPES_WITH_MEANINGFUL_DETAIL.has(event.type)) {
336
+ return true;
337
+ }
338
+ return typeof event.detail === "number" && event.detail !== 0;
339
+ };
340
+
312
341
  const resolveEventPredicate = (predicate) => {
313
342
  if (typeof predicate === "string") {
314
343
  return (e) => e.type === predicate;
@@ -8244,7 +8273,12 @@ const createDragGestureController = (options = {}) => {
8244
8273
  // metadata about interaction sources
8245
8274
  grabEvent: event,
8246
8275
  dragEvent: null,
8247
- releaseEvent: null
8276
+ releaseEvent: null,
8277
+ // The gesture ended without the hand ever letting go: the browser took
8278
+ // the touch to scroll with, another gesture took the pointer. Whoever
8279
+ // reads it must not commit anything — a release that was not asked for
8280
+ // says nothing about where things belong.
8281
+ cancelled: false
8248
8282
  };
8249
8283
  definePropertyAsReadOnly(gestureInfo, "name");
8250
8284
  definePropertyAsReadOnly(gestureInfo, "direction");
@@ -8556,9 +8590,13 @@ const createDragGestureController = (options = {}) => {
8556
8590
  };
8557
8591
  const release = ({
8558
8592
  event = new CustomEvent("programmatic"),
8593
+ cancelled = event.type === "pointercancel",
8559
8594
  releaseX = gestureInfo.dragX,
8560
8595
  releaseY = gestureInfo.dragY
8561
8596
  } = {}) => {
8597
+ // Written before the last drag is reported, so the callbacks reading the
8598
+ // end of the gesture all see the same one.
8599
+ gestureInfo.cancelled = cancelled;
8562
8600
  drag(releaseX, releaseY, {
8563
8601
  event,
8564
8602
  isRelease: true
@@ -8613,10 +8651,13 @@ const createDragGestureController = (options = {}) => {
8613
8651
  event: dragEvent
8614
8652
  });
8615
8653
  };
8616
- const releaseViaPointer = mouseupEvent => {
8617
- const [mouseReleaseX, mouseReleaseY] = mouseEventCoords(mouseupEvent);
8654
+ const releaseViaPointer = (pointerEvent, {
8655
+ cancelled
8656
+ } = {}) => {
8657
+ const [mouseReleaseX, mouseReleaseY] = mouseEventCoords(pointerEvent);
8618
8658
  dragGesture.release({
8619
- event: mouseupEvent,
8659
+ event: pointerEvent,
8660
+ cancelled,
8620
8661
  releaseX: mouseReleaseX,
8621
8662
  releaseY: mouseReleaseY
8622
8663
  });
@@ -8713,7 +8754,8 @@ const createDragGestureController = (options = {}) => {
8713
8754
  const cleanup = initializer({
8714
8755
  onMove: dragViaPointer,
8715
8756
  onRelease: releaseViaPointer,
8716
- gestureInfo: dragGesture.gestureInfo
8757
+ gestureInfo: dragGesture.gestureInfo,
8758
+ dragGesture
8717
8759
  });
8718
8760
  dragGesture.addReleaseCallback(() => {
8719
8761
  cleanup();
@@ -8725,7 +8767,8 @@ const createDragGestureController = (options = {}) => {
8725
8767
  return initDragByPointer(grabEvent, options, ({
8726
8768
  onMove,
8727
8769
  onRelease,
8728
- gestureInfo
8770
+ gestureInfo,
8771
+ dragGesture
8729
8772
  }) => {
8730
8773
  // Captured on something that will still be there at the end of the
8731
8774
  // gesture: the browser releases the capture when its element leaves the
@@ -8733,7 +8776,21 @@ const createDragGestureController = (options = {}) => {
8733
8776
  // finger would lose the pointer at its first move. Callers whose target
8734
8777
  // is stable have nothing to say and keep it.
8735
8778
  const target = options?.pointerCaptureElement || grabEvent.target;
8736
- target.setPointerCapture(grabEvent.pointerId);
8779
+ // The capture is ONE per pointer for the whole document: taking it is
8780
+ // taking it away from whoever had it, who is then told the exact same
8781
+ // thing it is told when its own gesture ends. So it is taken when this
8782
+ // gesture is established and not a moment earlier — for most callers
8783
+ // that is the grab itself (the intent was settled before, by a handle
8784
+ // or a long press), and for a caller that is still deciding what the
8785
+ // press means, it is whenever it says so (see capturePointer).
8786
+ let captured = false;
8787
+ dragGesture.capturePointer = () => {
8788
+ captured = true;
8789
+ target.setPointerCapture(grabEvent.pointerId);
8790
+ };
8791
+ if (!options?.pointerCaptureDeferred) {
8792
+ dragGesture.capturePointer();
8793
+ }
8737
8794
  /*
8738
8795
  * A touchmove left alone is the browser deciding the touch belongs to
8739
8796
  * it: it takes it to scroll with, and a touch it has taken is a pointer
@@ -8767,7 +8824,7 @@ const createDragGestureController = (options = {}) => {
8767
8824
  grabTarget.addEventListener("touchmove", preventTouchScroll, {
8768
8825
  passive: false
8769
8826
  });
8770
- // Only OUR capture ending means this gesture is over:
8827
+ // Only OUR capture ending is this gesture's business:
8771
8828
  // lostpointercapture bubbles, so a descendant giving up its own capture
8772
8829
  // walks straight into this listener. That is not a rare shape — it is
8773
8830
  // exactly what happens when a gesture hands over to another one (a
@@ -8775,16 +8832,37 @@ const createDragGestureController = (options = {}) => {
8775
8832
  // the pressed element, while the real one is being held on a container
8776
8833
  // above it), and taken as our own it kills the new gesture one
8777
8834
  // millisecond after it started.
8835
+ //
8836
+ // And when it IS ours, it is a loss, never an end: the ends a gesture
8837
+ // has are the pointer going up and the pointer being cancelled, both
8838
+ // listened for below. A capture that goes while the pointer is still
8839
+ // down was taken — by another gesture, or by the element it was held
8840
+ // on leaving the document — and what was being carried must go back
8841
+ // rather than land wherever the hand happened to be.
8778
8842
  const onCaptureLost = pointerEvent => {
8779
- if (pointerEvent.target !== target) {
8843
+ if (!captured || pointerEvent.target !== target) {
8780
8844
  return;
8781
8845
  }
8782
- onRelease(pointerEvent);
8846
+ onRelease(pointerEvent, {
8847
+ cancelled: true
8848
+ });
8783
8849
  };
8784
8850
  target.addEventListener("lostpointercapture", onCaptureLost);
8785
8851
  target.addEventListener("pointercancel", onRelease);
8786
- target.addEventListener("pointermove", onMove);
8787
8852
  target.addEventListener("pointerup", onRelease);
8853
+ // Read from the window rather than from the element while the pointer
8854
+ // is not this gesture's: without a capture a move is delivered to
8855
+ // whatever is under the pointer, and a hand that has left the element
8856
+ // is exactly the hand this gesture is trying to make sense of. The
8857
+ // capture phase reaches the window before anything else, captured or
8858
+ // not, so there is one place to read whichever way the gesture ends up.
8859
+ const onPointerMove = pointerEvent => {
8860
+ if (pointerEvent.pointerId !== grabEvent.pointerId) {
8861
+ return;
8862
+ }
8863
+ onMove(pointerEvent);
8864
+ };
8865
+ window.addEventListener("pointermove", onPointerMove, true);
8788
8866
  // The end of the pointer is also listened for on the window, because
8789
8867
  // the end is the one event a gesture cannot afford to miss and the
8790
8868
  // element it is captured on is not always on its way: a pointer can
@@ -8814,15 +8892,17 @@ const createDragGestureController = (options = {}) => {
8814
8892
  grabTarget.removeEventListener("touchmove", preventTouchScroll);
8815
8893
  target.removeEventListener("lostpointercapture", onCaptureLost);
8816
8894
  target.removeEventListener("pointercancel", onRelease);
8817
- target.removeEventListener("pointermove", onMove);
8895
+ window.removeEventListener("pointermove", onPointerMove, true);
8818
8896
  target.removeEventListener("pointerup", onRelease);
8819
8897
  window.removeEventListener("pointerup", onPointerEnd, true);
8820
8898
  window.removeEventListener("pointercancel", onPointerEnd, true);
8821
- // Asked for only while there is something to give back: a pointer
8822
- // that is up no longer exists, the browser has already dropped the
8899
+ // Only what this gesture took, and only while there is something to
8900
+ // give back: a capture on that element may be someone else's (two
8901
+ // gestures reading the same press hold the same node), and a pointer
8902
+ // that is up no longer exists — the browser has already dropped the
8823
8903
  // capture with it, and asking again throws ("No active pointer with
8824
- // the given id is found") on the most ordinary release there is.
8825
- if (target.hasPointerCapture(grabEvent.pointerId)) {
8904
+ // the given id is found") on the most ordinary release there is.
8905
+ if (captured && target.hasPointerCapture(grabEvent.pointerId)) {
8826
8906
  target.releasePointerCapture(grabEvent.pointerId);
8827
8907
  }
8828
8908
  };
@@ -9066,12 +9146,16 @@ const css$4 = /* css */`
9066
9146
  /* A source taken by long press must let the scroll through until the grab —
9067
9147
  which is exactly what the long press is there to tell apart. Zoom has
9068
9148
  nothing to do with the gesture and nobody should lose it by resting a
9069
- finger on a word. */
9149
+ finger on a word.
9150
+
9151
+ Vertical, because that is the way the page and the lists in it go: a
9152
+ source dragged along one axis is surrounded by something scrolling along
9153
+ that same axis (a row of a list runs the way the list scrolls), and a
9154
+ source dragged both ways sits on the usual vertical page. */
9070
9155
  touch-action: pan-y pinch-zoom;
9071
9156
  }
9072
9157
  [data-drag-source="x"] {
9073
- /* The axis is the one thing the caller has to say, being the only one who
9074
- knows which way what surrounds the source scrolls. */
9158
+ /* …and the sideways one, for the same reason read the other way. */
9075
9159
  touch-action: pan-x pinch-zoom;
9076
9160
  }
9077
9161
  [data-drag-on-contact] [data-drag-source],
@@ -9107,23 +9191,36 @@ import.meta.css = [css$4, "@jsenv/dom/src/interaction/drag/drag_after_intent.js"
9107
9191
  *
9108
9192
  * On the element and not on the window, so the rest of the page keeps its
9109
9193
  * touches on the compositor's fast path.
9194
+ *
9195
+ * Exported because a drag does not always begin on a drag source: a copy caught
9196
+ * on its way home is pressed through the pictures of a view transition, and the
9197
+ * touch lands on the document root (see letCopyBeCaught in drag_to.js). Same
9198
+ * rule, other element — and it has to be the same function, or the listener put
9199
+ * down is not the one taken back off.
9110
9200
  */
9111
9201
  const keepTouchRefusable = () => {
9112
9202
  // Being registered IS the whole of it — see above.
9113
9203
  };
9114
9204
 
9115
9205
  /**
9116
- * Says an element is something a drag can start from.
9206
+ * Says an element is something a drag can start from, and which way that drag
9207
+ * goes.
9208
+ *
9209
+ * The axes are written in the DOM rather than kept here because they are what
9210
+ * someone ELSE reads: a box above this one that travels under the same finger
9211
+ * (a row of slides, a sheet pushed down to close it) has to know which axes are
9212
+ * already spoken for before it answers the press — the same thing a travel says
9213
+ * about itself with `data-travel-by-drag`. It is also what leaves the browser
9214
+ * the pan it may still do until the grab (see the stylesheet above).
9117
9215
  *
9118
9216
  * @param {Element} element
9119
- * @param {string} [axes]
9120
- * Which way the SURROUNDINGS scroll, so the other axis is left to them until
9121
- * the grab: `"x"` for a source inside something travelling sideways, anything
9122
- * else for the usual vertical page.
9217
+ * @param {"x"|"y"|"xy"} [axes="xy"]
9218
+ * Which way the drag walks. A list reordered along its own line says `"y"`;
9219
+ * something carried across a board, or thrown, goes both ways.
9123
9220
  * @returns {function} Takes the mark back off.
9124
9221
  */
9125
- const markDragSource = (element, axes) => {
9126
- element.setAttribute("data-drag-source", axes === "x" ? "x" : "");
9222
+ const markDragSource = (element, axes = "xy") => {
9223
+ element.setAttribute("data-drag-source", axes);
9127
9224
  element.addEventListener("touchmove", keepTouchRefusable, {
9128
9225
  passive: false
9129
9226
  });
@@ -9223,7 +9320,13 @@ const dragAfterDistance = (grabEvent, dragGestureInitializer, threshold) => {
9223
9320
  }
9224
9321
  });
9225
9322
  const significantDragGesture = significantDragGestureController.grabViaPointer(grabEvent, {
9226
- element: grabEvent.target
9323
+ element: grabEvent.target,
9324
+ // This one owns nothing: it measures a distance to find out whether there
9325
+ // is a gesture at all, and it is over the moment there is. Taking the
9326
+ // pointer for that would take it from whoever is already holding this same
9327
+ // element for a gesture of their own — and giving it back at the threshold
9328
+ // would tell them theirs is over.
9329
+ pointerCaptureDeferred: true
9227
9330
  });
9228
9331
  };
9229
9332
  const dragAfterLongPress = (grabEvent, dragGestureInitializer, {
@@ -11512,6 +11615,18 @@ const css$1 = /* css */`
11512
11615
  pointer-events: auto;
11513
11616
  }
11514
11617
 
11618
+ /* …and a FINGER reaching for it does not land on it: the pictures of the
11619
+ transition cover the page, so as far as the browser is concerned the touch
11620
+ began on the document root. What a touch may do is decided there and at that
11621
+ moment, so the root says it for as long as the copy can be caught — the pan
11622
+ is ours (nothing should scroll while something is landing), zoom stays the
11623
+ reader's. Half of a pair: without the non-passive listener put down at the
11624
+ same moment (see letCopyBeCaught) every touchmove arrives already
11625
+ non-cancelable and refusing it does nothing. */
11626
+ [data-drag-catchable] {
11627
+ touch-action: pinch-zoom;
11628
+ }
11629
+
11515
11630
  /* Ce qui a été lancé: il continue dans la direction du geste jusqu'à sortir de
11516
11631
  l'écran, et revient par le même chemin si la réponse refuse. */
11517
11632
  [navi-drag-clone-wrapper][data-tossed] {
@@ -12152,6 +12267,12 @@ const resolveDropMeaning = ({
12152
12267
  tossDistance = TOSS_DISTANCE_TO_COMMIT,
12153
12268
  tossSpeed = TOSS_SPEED_TO_COMMIT
12154
12269
  }) => {
12270
+ if (gestureInfo.cancelled) {
12271
+ // Nobody let go of anything: the gesture was taken away mid-air (the
12272
+ // pointer cancelled, another gesture taking it). Where the thing happened
12273
+ // to be at that moment is not a place it was put.
12274
+ return "cancel";
12275
+ }
12155
12276
  if (canToss) {
12156
12277
  const {
12157
12278
  xDelta,
@@ -12451,7 +12572,15 @@ const startDragToCarryCopy = (event, {
12451
12572
  return dragGesture;
12452
12573
  }, {
12453
12574
  threshold,
12454
- longPress,
12575
+ // A copy caught on its way home is not an ambiguous press: the hand
12576
+ // reached for something moving, and the press was already matched
12577
+ // against the copy's own box before it got here. The wait a finger is
12578
+ // asked for elsewhere tells a scroll from a drag, and there is no scroll
12579
+ // to tell it from — the copy covers that spot from the top layer. Asked
12580
+ // for anyway it cannot even be answered: the wait is about as long as the
12581
+ // journey, so the thing is home before the proof is done, while a mouse
12582
+ // takes it in five pixels.
12583
+ longPress: cloneWrapperCaught ? false : longPress,
12455
12584
  longPressDelay,
12456
12585
  longPressSlop,
12457
12586
  onPressStart,
@@ -12641,6 +12770,17 @@ const letCopyBeCaught = (cloneWrapper, carryAgain) => {
12641
12770
  // gesture holds what it grabbed rather than whatever was behind.
12642
12771
  cloneWrapper.setAttribute("data-catchable", "");
12643
12772
  document.addEventListener("pointerdown", onPointerDown, true);
12773
+ // The touch half of the same reach, said on the root because that is where a
12774
+ // finger pressing through the pictures lands (see the stylesheet). Both go
12775
+ // down before the copy sets off, since what a touch may do is settled when it
12776
+ // begins: put down later, the press is still read, the carry still starts, and
12777
+ // the browser cancels the pointer one move afterwards — a copy that cannot be
12778
+ // caught with a finger and can with a mouse.
12779
+ const root = document.documentElement;
12780
+ root.setAttribute("data-drag-catchable", "");
12781
+ root.addEventListener("touchmove", keepTouchRefusable, {
12782
+ passive: false
12783
+ });
12644
12784
  return {
12645
12785
  settled: async () => {
12646
12786
  // A hand that lets go and presses again while the copy is still there is
@@ -12652,6 +12792,8 @@ const letCopyBeCaught = (cloneWrapper, carryAgain) => {
12652
12792
  }
12653
12793
  cloneWrapper.removeAttribute("data-catchable");
12654
12794
  document.removeEventListener("pointerdown", onPointerDown, true);
12795
+ root.removeAttribute("data-drag-catchable");
12796
+ root.removeEventListener("touchmove", keepTouchRefusable);
12655
12797
  return caught;
12656
12798
  }
12657
12799
  };
@@ -12943,10 +13085,13 @@ const DRAG_FLICK_DISTANCE = 8;
12943
13085
  // way. Let go and it comes back — a wall one can lean on, never walk through.
12944
13086
  const DRAG_RESISTANCE = 0.3;
12945
13087
 
12946
- // What a drag must not start on: something that reads the pointer itself. A
12947
- // button or a link is not in the list — dragging from one travels, and the
12948
- // click it would have made is swallowed on the way out.
12949
- const DRAG_EXCLUDED_SELECTOR = ["input", "textarea", "select", '[contenteditable=""]', '[contenteditable="true"]', "[data-no-drag-travel]"].join(",");
13088
+ // What a drag must not start on: something that reads the pointer itself, whole,
13089
+ // with no axis left to share. A button or a link is not in the list — dragging
13090
+ // from one travels, and the click it would have made is swallowed on the way
13091
+ // out. A drag SOURCE is not either: it says which way it goes and only takes
13092
+ // that (see DRAG_SOURCE_AXES_ATTRIBUTE) — but a dedicated handle is, being a
13093
+ // place whose only purpose is to be taken hold of, from the first pixel.
13094
+ const DRAG_EXCLUDED_SELECTOR = ["input", "textarea", "select", '[contenteditable=""]', '[contenteditable="true"]', "[data-drag-handle]", "[data-no-drag-travel]"].join(",");
12950
13095
 
12951
13096
  // Which axes a box travels on, one attribute per gesture, said in the DOM by
12952
13097
  // whoever owns the box: it is what a box ABOVE another reads to know the
@@ -12954,6 +13099,12 @@ const DRAG_EXCLUDED_SELECTOR = ["input", "textarea", "select", '[contenteditable
12954
13099
  // from the outside.
12955
13100
  const DRAG_AXES_ATTRIBUTE = "data-travel-by-drag";
12956
13101
  const WHEEL_AXES_ATTRIBUTE = "data-travel-by-wheel";
13102
+ // The same thing said by something that is PICKED UP rather than travelled: a
13103
+ // row taken out of a list, a card carried across a board (see markDragSource).
13104
+ // It holds the pointer from the press exactly as a nested travel does, so it is
13105
+ // read exactly as one — a list reordered along its own line takes the axis it
13106
+ // runs on and leaves the other to whoever is above.
13107
+ const DRAG_SOURCE_AXES_ATTRIBUTE = "data-drag-source";
12957
13108
 
12958
13109
  // A surface the browser paints in the top layer: it is still a DOM descendant
12959
13110
  // of whatever it was written in, and it is nowhere near it on screen — it
@@ -13169,10 +13320,12 @@ const startDragToTravel = (pointerDownEvent, {
13169
13320
  if (!target.closest || target.closest(DRAG_EXCLUDED_SELECTOR)) {
13170
13321
  return null;
13171
13322
  }
13172
- // A box between the finger and this one that travels the same way: the
13173
- // gesture is its, and this one is left with the axes it does not walk — none
13174
- // at all, most of the time, and then there is no gesture here to read.
13175
- const axesLeft = axesLeftBy(axes, target, element, DRAG_AXES_ATTRIBUTE);
13323
+ // A box between the finger and this one that travels the same way, and then
13324
+ // anything between them that is picked up and carried the same way: the
13325
+ // gesture is theirs, and this one is left with the axes none of them walks
13326
+ // none at all, most of the time, and then there is no gesture here to read.
13327
+ const axesLeftByTravels = axesLeftBy(axes, target, element, DRAG_AXES_ATTRIBUTE);
13328
+ const axesLeft = axesLeftByTravels && axesLeftBy(axesLeftByTravels, target, element, DRAG_SOURCE_AXES_ATTRIBUTE);
13176
13329
  if (!axesLeft) {
13177
13330
  return null;
13178
13331
  }
@@ -13339,6 +13492,9 @@ const startDragToTravel = (pointerDownEvent, {
13339
13492
  pulled: started.slack || 0
13340
13493
  };
13341
13494
  document.documentElement.setAttribute(WALKING_ATTRIBUTE, axis);
13495
+ // The travel exists: from here the pointer is this box's, and it is
13496
+ // followed wherever it goes.
13497
+ dragGesture.capturePointer();
13342
13498
  }
13343
13499
  const {
13344
13500
  axis
@@ -13410,9 +13566,12 @@ const startDragToTravel = (pointerDownEvent, {
13410
13566
  const towardsSomething = pulled > 0 ? travel.travelBack : travel.travelOn;
13411
13567
  const velocity = axis === "x" ? gestureInfo.velocityX : gestureInfo.velocityY;
13412
13568
  // A gesture taken away rather than let go of (the browser scrolling
13413
- // something else, a call coming in) said nothing: things go back.
13569
+ // something else, a call coming in, another gesture taking the pointer)
13570
+ // said nothing: things go back.
13414
13571
  const releaseEvent = gestureInfo.releaseEvent || gestureInfo.dragEvent;
13415
- const cancelled = releaseEvent?.type === "pointercancel";
13572
+ const {
13573
+ cancelled
13574
+ } = gestureInfo;
13416
13575
  onEnd({
13417
13576
  axis,
13418
13577
  pulled,
@@ -13443,7 +13602,15 @@ const startDragToTravel = (pointerDownEvent, {
13443
13602
  // gesture may take that away (a page that travels navigates, and the
13444
13603
  // router unmounts the page being left), and a capture whose element
13445
13604
  // leaves the document is a capture the browser drops.
13446
- pointerCaptureElement: element
13605
+ pointerCaptureElement: element,
13606
+ // A travel is established in two steps, and the pointer is only owned
13607
+ // after the second: the distance below says the press is not a click, and
13608
+ // the first move says which axis it leans on — which this box may not
13609
+ // walk, or the caller may refuse. Taken at the first step, the capture
13610
+ // would be taken away from whoever else is reading the same press for
13611
+ // gestures that give themselves up one event later. It is claimed once
13612
+ // the travel exists, in onDrag below.
13613
+ pointerCaptureDeferred: true
13447
13614
  });
13448
13615
  return dragGesture;
13449
13616
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/dom",
3
- "version": "0.17.18",
3
+ "version": "0.17.20",
4
4
  "type": "module",
5
5
  "description": "DOM utilities for writing frontend code",
6
6
  "repository": {