@jsenv/dom 0.17.18 → 0.17.19

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 +101 -22
  2. package/package.json +1 -1
package/dist/jsenv_dom.js CHANGED
@@ -8244,7 +8244,12 @@ const createDragGestureController = (options = {}) => {
8244
8244
  // metadata about interaction sources
8245
8245
  grabEvent: event,
8246
8246
  dragEvent: null,
8247
- releaseEvent: null
8247
+ releaseEvent: null,
8248
+ // The gesture ended without the hand ever letting go: the browser took
8249
+ // the touch to scroll with, another gesture took the pointer. Whoever
8250
+ // reads it must not commit anything — a release that was not asked for
8251
+ // says nothing about where things belong.
8252
+ cancelled: false
8248
8253
  };
8249
8254
  definePropertyAsReadOnly(gestureInfo, "name");
8250
8255
  definePropertyAsReadOnly(gestureInfo, "direction");
@@ -8556,9 +8561,13 @@ const createDragGestureController = (options = {}) => {
8556
8561
  };
8557
8562
  const release = ({
8558
8563
  event = new CustomEvent("programmatic"),
8564
+ cancelled = event.type === "pointercancel",
8559
8565
  releaseX = gestureInfo.dragX,
8560
8566
  releaseY = gestureInfo.dragY
8561
8567
  } = {}) => {
8568
+ // Written before the last drag is reported, so the callbacks reading the
8569
+ // end of the gesture all see the same one.
8570
+ gestureInfo.cancelled = cancelled;
8562
8571
  drag(releaseX, releaseY, {
8563
8572
  event,
8564
8573
  isRelease: true
@@ -8613,10 +8622,13 @@ const createDragGestureController = (options = {}) => {
8613
8622
  event: dragEvent
8614
8623
  });
8615
8624
  };
8616
- const releaseViaPointer = mouseupEvent => {
8617
- const [mouseReleaseX, mouseReleaseY] = mouseEventCoords(mouseupEvent);
8625
+ const releaseViaPointer = (pointerEvent, {
8626
+ cancelled
8627
+ } = {}) => {
8628
+ const [mouseReleaseX, mouseReleaseY] = mouseEventCoords(pointerEvent);
8618
8629
  dragGesture.release({
8619
- event: mouseupEvent,
8630
+ event: pointerEvent,
8631
+ cancelled,
8620
8632
  releaseX: mouseReleaseX,
8621
8633
  releaseY: mouseReleaseY
8622
8634
  });
@@ -8713,7 +8725,8 @@ const createDragGestureController = (options = {}) => {
8713
8725
  const cleanup = initializer({
8714
8726
  onMove: dragViaPointer,
8715
8727
  onRelease: releaseViaPointer,
8716
- gestureInfo: dragGesture.gestureInfo
8728
+ gestureInfo: dragGesture.gestureInfo,
8729
+ dragGesture
8717
8730
  });
8718
8731
  dragGesture.addReleaseCallback(() => {
8719
8732
  cleanup();
@@ -8725,7 +8738,8 @@ const createDragGestureController = (options = {}) => {
8725
8738
  return initDragByPointer(grabEvent, options, ({
8726
8739
  onMove,
8727
8740
  onRelease,
8728
- gestureInfo
8741
+ gestureInfo,
8742
+ dragGesture
8729
8743
  }) => {
8730
8744
  // Captured on something that will still be there at the end of the
8731
8745
  // gesture: the browser releases the capture when its element leaves the
@@ -8733,7 +8747,21 @@ const createDragGestureController = (options = {}) => {
8733
8747
  // finger would lose the pointer at its first move. Callers whose target
8734
8748
  // is stable have nothing to say and keep it.
8735
8749
  const target = options?.pointerCaptureElement || grabEvent.target;
8736
- target.setPointerCapture(grabEvent.pointerId);
8750
+ // The capture is ONE per pointer for the whole document: taking it is
8751
+ // taking it away from whoever had it, who is then told the exact same
8752
+ // thing it is told when its own gesture ends. So it is taken when this
8753
+ // gesture is established and not a moment earlier — for most callers
8754
+ // that is the grab itself (the intent was settled before, by a handle
8755
+ // or a long press), and for a caller that is still deciding what the
8756
+ // press means, it is whenever it says so (see capturePointer).
8757
+ let captured = false;
8758
+ dragGesture.capturePointer = () => {
8759
+ captured = true;
8760
+ target.setPointerCapture(grabEvent.pointerId);
8761
+ };
8762
+ if (!options?.pointerCaptureDeferred) {
8763
+ dragGesture.capturePointer();
8764
+ }
8737
8765
  /*
8738
8766
  * A touchmove left alone is the browser deciding the touch belongs to
8739
8767
  * it: it takes it to scroll with, and a touch it has taken is a pointer
@@ -8767,7 +8795,7 @@ const createDragGestureController = (options = {}) => {
8767
8795
  grabTarget.addEventListener("touchmove", preventTouchScroll, {
8768
8796
  passive: false
8769
8797
  });
8770
- // Only OUR capture ending means this gesture is over:
8798
+ // Only OUR capture ending is this gesture's business:
8771
8799
  // lostpointercapture bubbles, so a descendant giving up its own capture
8772
8800
  // walks straight into this listener. That is not a rare shape — it is
8773
8801
  // exactly what happens when a gesture hands over to another one (a
@@ -8775,16 +8803,37 @@ const createDragGestureController = (options = {}) => {
8775
8803
  // the pressed element, while the real one is being held on a container
8776
8804
  // above it), and taken as our own it kills the new gesture one
8777
8805
  // millisecond after it started.
8806
+ //
8807
+ // And when it IS ours, it is a loss, never an end: the ends a gesture
8808
+ // has are the pointer going up and the pointer being cancelled, both
8809
+ // listened for below. A capture that goes while the pointer is still
8810
+ // down was taken — by another gesture, or by the element it was held
8811
+ // on leaving the document — and what was being carried must go back
8812
+ // rather than land wherever the hand happened to be.
8778
8813
  const onCaptureLost = pointerEvent => {
8779
- if (pointerEvent.target !== target) {
8814
+ if (!captured || pointerEvent.target !== target) {
8780
8815
  return;
8781
8816
  }
8782
- onRelease(pointerEvent);
8817
+ onRelease(pointerEvent, {
8818
+ cancelled: true
8819
+ });
8783
8820
  };
8784
8821
  target.addEventListener("lostpointercapture", onCaptureLost);
8785
8822
  target.addEventListener("pointercancel", onRelease);
8786
- target.addEventListener("pointermove", onMove);
8787
8823
  target.addEventListener("pointerup", onRelease);
8824
+ // Read from the window rather than from the element while the pointer
8825
+ // is not this gesture's: without a capture a move is delivered to
8826
+ // whatever is under the pointer, and a hand that has left the element
8827
+ // is exactly the hand this gesture is trying to make sense of. The
8828
+ // capture phase reaches the window before anything else, captured or
8829
+ // not, so there is one place to read whichever way the gesture ends up.
8830
+ const onPointerMove = pointerEvent => {
8831
+ if (pointerEvent.pointerId !== grabEvent.pointerId) {
8832
+ return;
8833
+ }
8834
+ onMove(pointerEvent);
8835
+ };
8836
+ window.addEventListener("pointermove", onPointerMove, true);
8788
8837
  // The end of the pointer is also listened for on the window, because
8789
8838
  // the end is the one event a gesture cannot afford to miss and the
8790
8839
  // element it is captured on is not always on its way: a pointer can
@@ -8814,15 +8863,17 @@ const createDragGestureController = (options = {}) => {
8814
8863
  grabTarget.removeEventListener("touchmove", preventTouchScroll);
8815
8864
  target.removeEventListener("lostpointercapture", onCaptureLost);
8816
8865
  target.removeEventListener("pointercancel", onRelease);
8817
- target.removeEventListener("pointermove", onMove);
8866
+ window.removeEventListener("pointermove", onPointerMove, true);
8818
8867
  target.removeEventListener("pointerup", onRelease);
8819
8868
  window.removeEventListener("pointerup", onPointerEnd, true);
8820
8869
  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
8870
+ // Only what this gesture took, and only while there is something to
8871
+ // give back: a capture on that element may be someone else's (two
8872
+ // gestures reading the same press hold the same node), and a pointer
8873
+ // that is up no longer exists — the browser has already dropped the
8823
8874
  // 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)) {
8875
+ // the given id is found") on the most ordinary release there is.
8876
+ if (captured && target.hasPointerCapture(grabEvent.pointerId)) {
8826
8877
  target.releasePointerCapture(grabEvent.pointerId);
8827
8878
  }
8828
8879
  };
@@ -9223,7 +9274,13 @@ const dragAfterDistance = (grabEvent, dragGestureInitializer, threshold) => {
9223
9274
  }
9224
9275
  });
9225
9276
  const significantDragGesture = significantDragGestureController.grabViaPointer(grabEvent, {
9226
- element: grabEvent.target
9277
+ element: grabEvent.target,
9278
+ // This one owns nothing: it measures a distance to find out whether there
9279
+ // is a gesture at all, and it is over the moment there is. Taking the
9280
+ // pointer for that would take it from whoever is already holding this same
9281
+ // element for a gesture of their own — and giving it back at the threshold
9282
+ // would tell them theirs is over.
9283
+ pointerCaptureDeferred: true
9227
9284
  });
9228
9285
  };
9229
9286
  const dragAfterLongPress = (grabEvent, dragGestureInitializer, {
@@ -12152,6 +12209,12 @@ const resolveDropMeaning = ({
12152
12209
  tossDistance = TOSS_DISTANCE_TO_COMMIT,
12153
12210
  tossSpeed = TOSS_SPEED_TO_COMMIT
12154
12211
  }) => {
12212
+ if (gestureInfo.cancelled) {
12213
+ // Nobody let go of anything: the gesture was taken away mid-air (the
12214
+ // pointer cancelled, another gesture taking it). Where the thing happened
12215
+ // to be at that moment is not a place it was put.
12216
+ return "cancel";
12217
+ }
12155
12218
  if (canToss) {
12156
12219
  const {
12157
12220
  xDelta,
@@ -12945,8 +13008,10 @@ const DRAG_RESISTANCE = 0.3;
12945
13008
 
12946
13009
  // What a drag must not start on: something that reads the pointer itself. A
12947
13010
  // 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(",");
13011
+ // click it would have made is swallowed on the way out. A drag source is: it
13012
+ // answers the same press, and a travel starting there takes the pointer capture
13013
+ // away from a gesture already carrying something.
13014
+ const DRAG_EXCLUDED_SELECTOR = ["input", "textarea", "select", '[contenteditable=""]', '[contenteditable="true"]', "[data-drag-source]", "[data-drag-handle]", "[data-no-drag-travel]"].join(",");
12950
13015
 
12951
13016
  // Which axes a box travels on, one attribute per gesture, said in the DOM by
12952
13017
  // whoever owns the box: it is what a box ABOVE another reads to know the
@@ -13339,6 +13404,9 @@ const startDragToTravel = (pointerDownEvent, {
13339
13404
  pulled: started.slack || 0
13340
13405
  };
13341
13406
  document.documentElement.setAttribute(WALKING_ATTRIBUTE, axis);
13407
+ // The travel exists: from here the pointer is this box's, and it is
13408
+ // followed wherever it goes.
13409
+ dragGesture.capturePointer();
13342
13410
  }
13343
13411
  const {
13344
13412
  axis
@@ -13410,9 +13478,12 @@ const startDragToTravel = (pointerDownEvent, {
13410
13478
  const towardsSomething = pulled > 0 ? travel.travelBack : travel.travelOn;
13411
13479
  const velocity = axis === "x" ? gestureInfo.velocityX : gestureInfo.velocityY;
13412
13480
  // A gesture taken away rather than let go of (the browser scrolling
13413
- // something else, a call coming in) said nothing: things go back.
13481
+ // something else, a call coming in, another gesture taking the pointer)
13482
+ // said nothing: things go back.
13414
13483
  const releaseEvent = gestureInfo.releaseEvent || gestureInfo.dragEvent;
13415
- const cancelled = releaseEvent?.type === "pointercancel";
13484
+ const {
13485
+ cancelled
13486
+ } = gestureInfo;
13416
13487
  onEnd({
13417
13488
  axis,
13418
13489
  pulled,
@@ -13443,7 +13514,15 @@ const startDragToTravel = (pointerDownEvent, {
13443
13514
  // gesture may take that away (a page that travels navigates, and the
13444
13515
  // router unmounts the page being left), and a capture whose element
13445
13516
  // leaves the document is a capture the browser drops.
13446
- pointerCaptureElement: element
13517
+ pointerCaptureElement: element,
13518
+ // A travel is established in two steps, and the pointer is only owned
13519
+ // after the second: the distance below says the press is not a click, and
13520
+ // the first move says which axis it leans on — which this box may not
13521
+ // walk, or the caller may refuse. Taken at the first step, the capture
13522
+ // would be taken away from whoever else is reading the same press for
13523
+ // gestures that give themselves up one event later. It is claimed once
13524
+ // the travel exists, in onDrag below.
13525
+ pointerCaptureDeferred: true
13447
13526
  });
13448
13527
  return dragGesture;
13449
13528
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/dom",
3
- "version": "0.17.18",
3
+ "version": "0.17.19",
4
4
  "type": "module",
5
5
  "description": "DOM utilities for writing frontend code",
6
6
  "repository": {