@jsenv/dom 0.17.43 → 0.17.45

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 +1345 -1176
  2. package/package.json +1 -1
package/dist/jsenv_dom.js CHANGED
@@ -8487,17 +8487,9 @@ const isolateInteractions = (elements) => {
8487
8487
  };
8488
8488
  };
8489
8489
 
8490
- installImportMetaCssBuild(import.meta);/**
8491
- * Drag Gesture System
8492
- *
8493
- * TODO: rename moveX/moveY en juste x/y
8494
- * puisque move c'est perturbant sachant que c'est drag + scroll
8495
- * et que drag c'est juste la partie mouvement de la souris
8496
- *
8497
- * donc juste x/y ca seras surement mieux
8498
- *
8499
- */
8500
- const css$5 = /* css */`.navi_drag_gesture_backdrop {
8490
+ installImportMetaCssBuild(import.meta);
8491
+
8492
+ const css$5 = /* css */ `.navi_drag_gesture_backdrop {
8501
8493
  touch-action: none;
8502
8494
  user-select: none;
8503
8495
  position: fixed;
@@ -8521,6 +8513,51 @@ import.meta.css = [css$5, "@jsenv/dom/src/interaction/drag/drag_gesture.js"];
8521
8513
  * apart: `lostpointercapture` says the same thing either way.
8522
8514
  */
8523
8515
  const captureHolderByPointerId = new Map();
8516
+
8517
+ /**
8518
+ * The drag loop: it takes the pointer, follows it, and says when it is let go of.
8519
+ * What the movement MEANS is the caller's — this one carries nothing, moves
8520
+ * nothing and commits nothing on its own, which is what makes it the primitive
8521
+ * for a gesture whose product is a value (an angle, a scale, an offset) rather
8522
+ * than an element that ends up somewhere.
8523
+ *
8524
+ * While it runs it owns the document: everything but the dragged element (and the
8525
+ * `[data-droppable]` places) goes inert, a backdrop unifies the cursor and takes
8526
+ * the pointer events, the focus moves to what is held and comes back at the
8527
+ * release, and the selection is refused for the length of the gesture.
8528
+ *
8529
+ * Whether the element may be grabbed AT ALL is decided before the finger lands
8530
+ * and elsewhere: see `markDragSource` in drag_after_intent.js for the
8531
+ * touch-action half, and `dragAfterIntent` for when a press becomes a drag.
8532
+ *
8533
+ * @param {object} [options]
8534
+ * @param {string} [options.name] Carried on `gestureInfo`, for whoever reads a gesture they did not start.
8535
+ * @param {(gestureInfo: object) => void} [options.onGrab]
8536
+ * The pointer went down: there is a gesture, which may still turn out to be a press.
8537
+ * @param {(gestureInfo: object) => void} [options.onDragStart] The threshold was crossed — this one IS a drag.
8538
+ * @param {(gestureInfo: object, someLayoutChange: boolean) => void} [options.onDrag]
8539
+ * Every move, and every scroll of the container: `someLayoutChange` is false when
8540
+ * nothing about the element changed, which still matters to whatever sticks to the scroll.
8541
+ * @param {(gestureInfo: object) => void} [options.onRelease]
8542
+ * The hand let go — or did not: `gestureInfo.cancelled` says the gesture was taken away
8543
+ * (the browser took the touch to scroll with, another gesture took the pointer), and a
8544
+ * release nobody asked for says nothing about where things belong, so nothing read from
8545
+ * it may be committed.
8546
+ * @param {number} [options.threshold=5] How far (px) the pointer travels before the drag starts. `0` starts it at the grab.
8547
+ * @param {{x: boolean, y: boolean}} [options.direction] Which axes the gesture reads.
8548
+ * @param {"auto"|"manual"} [options.documentInteractions="auto"]
8549
+ * `"manual"` leaves the document alone — no inertness, no backdrop, no focus taken. For a
8550
+ * gesture that only measures and hands over (see `dragAfterIntent`).
8551
+ * @param {boolean} [options.backdrop=true]
8552
+ * @param {number} [options.backdropZIndex=999999]
8553
+ * @returns {{grab: function, grabViaPointer: function}}
8554
+ * `grabViaPointer(pointerdownEvent, { element, direction, cursor, scrollContainer })` for a
8555
+ * gesture a hand starts, `grab({ element, grabX, grabY })` for one nothing pressed. Both give
8556
+ * back the gesture — `gestureInfo` (its `layout` with `xDelta`/`yDelta`, its velocity, the way
8557
+ * it is going), `drag`, `release`, and `addDragCallback`/`addReleaseCallback` for whoever joins
8558
+ * after it started. `grabViaPointer` gives back `null` when the press is not one it can read: a
8559
+ * secondary button, a text node.
8560
+ */
8524
8561
  const createDragGestureController = (options = {}) => {
8525
8562
  const {
8526
8563
  name,
@@ -8529,18 +8566,17 @@ const createDragGestureController = (options = {}) => {
8529
8566
  onDrag,
8530
8567
  onRelease,
8531
8568
  threshold = 5,
8532
- direction: defaultDirection = {
8533
- x: true,
8534
- y: true
8535
- },
8569
+ direction: defaultDirection = { x: true, y: true },
8536
8570
  documentInteractions = "auto",
8537
8571
  backdrop = true,
8538
- backdropZIndex = 999999
8572
+ backdropZIndex = 999999,
8539
8573
  } = options;
8574
+
8540
8575
  const dragGestureController = {
8541
8576
  grab: null,
8542
- gravViaPointer: null
8577
+ gravViaPointer: null,
8543
8578
  };
8579
+
8544
8580
  const grab = ({
8545
8581
  element,
8546
8582
  direction = defaultDirection,
@@ -8550,7 +8586,7 @@ const createDragGestureController = (options = {}) => {
8550
8586
  cursor = "grabbing",
8551
8587
  scrollContainer = document.documentElement,
8552
8588
  layoutScrollableLeft: scrollableLeftAtGrab = 0,
8553
- layoutScrollableTop: scrollableTopAtGrab = 0
8589
+ layoutScrollableTop: scrollableTopAtGrab = 0,
8554
8590
  } = {}) => {
8555
8591
  if (!element) {
8556
8592
  throw new Error("element is required");
@@ -8558,6 +8594,7 @@ const createDragGestureController = (options = {}) => {
8558
8594
  if (!direction.x && !direction.y) {
8559
8595
  return null;
8560
8596
  }
8597
+
8561
8598
  const [publishBeforeDrag, addBeforeDragCallback] = createPubSub();
8562
8599
  const [publishDrag, addDragCallback] = createPubSub();
8563
8600
  const [publishRelease, addReleaseCallback] = createPubSub();
@@ -8567,15 +8604,13 @@ const createDragGestureController = (options = {}) => {
8567
8604
  if (onRelease) {
8568
8605
  addReleaseCallback(onRelease);
8569
8606
  }
8607
+
8570
8608
  const scrollLeftAtGrab = scrollContainer.scrollLeft;
8571
8609
  const scrollTopAtGrab = scrollContainer.scrollTop;
8572
8610
  const leftAtGrab = scrollLeftAtGrab + scrollableLeftAtGrab;
8573
8611
  const topAtGrab = scrollTopAtGrab + scrollableTopAtGrab;
8574
8612
  const createLayout = (x, y) => {
8575
- const {
8576
- scrollLeft,
8577
- scrollTop
8578
- } = scrollContainer;
8613
+ const { scrollLeft, scrollTop } = scrollContainer;
8579
8614
  const left = scrollableLeftAtGrab + x;
8580
8615
  const top = scrollableTopAtGrab + y;
8581
8616
  const scrollableLeft = left - scrollLeft;
@@ -8595,30 +8630,33 @@ const createDragGestureController = (options = {}) => {
8595
8630
  top,
8596
8631
  // Delta since grab (number representing how much we dragged)
8597
8632
  xDelta: left - leftAtGrab,
8598
- yDelta: top - topAtGrab
8633
+ yDelta: top - topAtGrab,
8599
8634
  };
8600
8635
  return layoutProps;
8601
8636
  };
8602
- const grabLayout = createLayout(grabX + scrollContainer.scrollLeft, grabY + scrollContainer.scrollTop);
8637
+
8638
+ const grabLayout = createLayout(
8639
+ grabX + scrollContainer.scrollLeft,
8640
+ grabY + scrollContainer.scrollTop,
8641
+ );
8603
8642
  const gestureInfo = {
8604
8643
  name,
8605
8644
  direction,
8606
8645
  started: !threshold,
8607
8646
  status: "grabbed",
8647
+
8608
8648
  element,
8609
8649
  scrollContainer,
8610
- grabX,
8611
- // x grab coordinate (excluding scroll)
8612
- grabY,
8613
- // y grab coordinate (excluding scroll)
8650
+ grabX, // x grab coordinate (excluding scroll)
8651
+ grabY, // y grab coordinate (excluding scroll)
8614
8652
  grabLayout,
8615
8653
  leftAtGrab,
8616
8654
  topAtGrab,
8617
- dragX: grabX,
8618
- // coordinate of the last drag (excluding scroll of the scrollContainer)
8619
- dragY: grabY,
8620
- // coordinate of the last drag (excluding scroll of the scrollContainer)
8655
+
8656
+ dragX: grabX, // coordinate of the last drag (excluding scroll of the scrollContainer)
8657
+ dragY: grabY, // coordinate of the last drag (excluding scroll of the scrollContainer)
8621
8658
  layout: grabLayout,
8659
+
8622
8660
  isGoingUp: undefined,
8623
8661
  isGoingDown: undefined,
8624
8662
  isGoingLeft: undefined,
@@ -8627,11 +8665,13 @@ const createDragGestureController = (options = {}) => {
8627
8665
  intentGoingDown: false,
8628
8666
  intentGoingLeft: false,
8629
8667
  intentGoingRight: false,
8668
+
8630
8669
  // How fast the pointer is going, in px/ms, signed per axis
8631
8670
  // (see measureVelocity)
8632
8671
  velocityX: 0,
8633
8672
  velocityY: 0,
8634
8673
  velocity: 0,
8674
+
8635
8675
  // metadata about interaction sources
8636
8676
  grabEvent: event,
8637
8677
  dragEvent: null,
@@ -8640,7 +8680,7 @@ const createDragGestureController = (options = {}) => {
8640
8680
  // the touch to scroll with, another gesture took the pointer. Whoever
8641
8681
  // reads it must not commit anything — a release that was not asked for
8642
8682
  // says nothing about where things belong.
8643
- cancelled: false
8683
+ cancelled: false,
8644
8684
  };
8645
8685
  definePropertyAsReadOnly(gestureInfo, "name");
8646
8686
  definePropertyAsReadOnly(gestureInfo, "direction");
@@ -8656,6 +8696,7 @@ const createDragGestureController = (options = {}) => {
8656
8696
  // matter of speed, and the gesture is the only place that sees the timing of
8657
8697
  // the events it receives.
8658
8698
  const measureVelocity = createVelocityMeter(grabX, grabY);
8699
+
8659
8700
  document_interactions: {
8660
8701
  if (documentInteractions === "manual") {
8661
8702
  break document_interactions;
@@ -8668,18 +8709,23 @@ const createDragGestureController = (options = {}) => {
8668
8709
  2. Break the visual feedback (inconsistent cursors, hover states)
8669
8710
  3. Cause unwanted scrolling (keyboard shortcuts, wheel events in restricted directions)
8670
8711
  4. Create accessibility issues (focus jumping, screen reader confusion)
8671
- STRATEGY: Create a controlled interaction environment by:
8712
+
8713
+ STRATEGY: Create a controlled interaction environment by:
8672
8714
  1. VISUAL CONTROL: Use a backdrop to unify cursor appearance and block pointer events
8673
8715
  2. INTERACTION ISOLATION: Make non-dragged elements inert to prevent interference
8674
8716
  3. FOCUS MANAGEMENT: Control focus location and prevent focus changes during drag
8675
8717
  4. SELECTIVE SCROLLING: Allow scrolling only in directions supported by the drag gesture
8676
- IMPLEMENTATION:
8718
+
8719
+ IMPLEMENTATION:
8677
8720
  */
8678
8721
 
8679
8722
  // 1. INTERACTION ISOLATION: Make everything except the dragged element inert
8680
8723
  // This prevents keyboard events, pointer interactions, and screen reader navigation
8681
8724
  // on non-relevant elements during the drag operation
8682
- const cleanupInert = isolateInteractions([element, ...Array.from(document.querySelectorAll("[data-droppable]"))]);
8725
+ const cleanupInert = isolateInteractions([
8726
+ element,
8727
+ ...Array.from(document.querySelectorAll("[data-droppable]")),
8728
+ ]);
8683
8729
  addReleaseCallback(() => {
8684
8730
  cleanupInert();
8685
8731
  });
@@ -8696,14 +8742,14 @@ const createDragGestureController = (options = {}) => {
8696
8742
  // Handle wheel events on backdrop for directionally-constrained drag gestures
8697
8743
  // (e.g., table column resize should only allow horizontal scrolling)
8698
8744
  if (!direction.x || !direction.y) {
8699
- backdropElement.onwheel = e => {
8745
+ backdropElement.onwheel = (e) => {
8700
8746
  e.preventDefault();
8701
8747
  const scrollX = direction.x ? e.deltaX : 0;
8702
8748
  const scrollY = direction.y ? e.deltaY : 0;
8703
8749
  scrollContainer.scrollBy({
8704
8750
  left: scrollX,
8705
8751
  top: scrollY,
8706
- behavior: "auto"
8752
+ behavior: "auto",
8707
8753
  });
8708
8754
  };
8709
8755
  }
@@ -8714,9 +8760,7 @@ const createDragGestureController = (options = {}) => {
8714
8760
  }
8715
8761
 
8716
8762
  // 3. FOCUS MANAGEMENT: Control and stabilize focus during drag
8717
- const {
8718
- activeElement
8719
- } = document;
8763
+ const { activeElement } = document;
8720
8764
  const focusableElement = findFocusable(element);
8721
8765
  // Focus the dragged element (or document.body as fallback) to establish clear focus context
8722
8766
  // This also ensure any keydown event listened by the currently focused element
@@ -8724,17 +8768,17 @@ const createDragGestureController = (options = {}) => {
8724
8768
  const elementToFocus = focusableElement || document.body;
8725
8769
  elementToFocus.setAttribute("data-drag-focus", "");
8726
8770
  elementToFocus.focus({
8727
- preventScroll: true
8771
+ preventScroll: true,
8728
8772
  });
8729
8773
  addReleaseCallback(() => {
8730
8774
  elementToFocus.removeAttribute("data-drag-focus");
8731
8775
  // Restore original focus on release
8732
8776
  activeElement.focus({
8733
- preventScroll: true
8777
+ preventScroll: true,
8734
8778
  });
8735
8779
  });
8736
8780
  // Prevent Tab navigation entirely (focus should stay stable)
8737
- const onkeydown = e => {
8781
+ const onkeydown = (e) => {
8738
8782
  if (e.key === "Tab") {
8739
8783
  e.preventDefault();
8740
8784
  return;
@@ -8747,16 +8791,27 @@ const createDragGestureController = (options = {}) => {
8747
8791
 
8748
8792
  // 4. SELECTIVE SCROLLING: Allow keyboard scrolling only in supported directions
8749
8793
  {
8750
- const onDocumentKeydown = keyboardEvent => {
8794
+ const onDocumentKeydown = (keyboardEvent) => {
8751
8795
  // Vertical scrolling keys - prevent if vertical movement not supported
8752
- if (keyboardEvent.key === "ArrowUp" || keyboardEvent.key === "ArrowDown" || keyboardEvent.key === " " || keyboardEvent.key === "PageUp" || keyboardEvent.key === "PageDown" || keyboardEvent.key === "Home" || keyboardEvent.key === "End") {
8796
+ if (
8797
+ keyboardEvent.key === "ArrowUp" ||
8798
+ keyboardEvent.key === "ArrowDown" ||
8799
+ keyboardEvent.key === " " ||
8800
+ keyboardEvent.key === "PageUp" ||
8801
+ keyboardEvent.key === "PageDown" ||
8802
+ keyboardEvent.key === "Home" ||
8803
+ keyboardEvent.key === "End"
8804
+ ) {
8753
8805
  if (!direction.y) {
8754
8806
  keyboardEvent.preventDefault();
8755
8807
  }
8756
8808
  return;
8757
8809
  }
8758
8810
  // Horizontal scrolling keys - prevent if horizontal movement not supported
8759
- if (keyboardEvent.key === "ArrowLeft" || keyboardEvent.key === "ArrowRight") {
8811
+ if (
8812
+ keyboardEvent.key === "ArrowLeft" ||
8813
+ keyboardEvent.key === "ArrowRight"
8814
+ ) {
8760
8815
  if (!direction.x) {
8761
8816
  keyboardEvent.preventDefault();
8762
8817
  }
@@ -8773,38 +8828,36 @@ const createDragGestureController = (options = {}) => {
8773
8828
  // Set up scroll event handling to adjust drag position when scrolling occurs
8774
8829
  {
8775
8830
  let isHandlingScroll = false;
8776
- const handleScroll = scrollEvent => {
8831
+ const handleScroll = (scrollEvent) => {
8777
8832
  if (isHandlingScroll) {
8778
8833
  return;
8779
8834
  }
8780
8835
  isHandlingScroll = true;
8781
- drag(gestureInfo.dragX, gestureInfo.dragY, {
8782
- event: scrollEvent
8783
- });
8836
+ drag(gestureInfo.dragX, gestureInfo.dragY, { event: scrollEvent });
8784
8837
  isHandlingScroll = false;
8785
8838
  };
8786
- const scrollEventReceiver = scrollContainer === document.documentElement ? document : scrollContainer;
8839
+ const scrollEventReceiver =
8840
+ scrollContainer === document.documentElement
8841
+ ? document
8842
+ : scrollContainer;
8787
8843
  scrollEventReceiver.addEventListener("scroll", handleScroll, {
8788
- passive: true
8844
+ passive: true,
8789
8845
  });
8790
8846
  addReleaseCallback(() => {
8791
8847
  scrollEventReceiver.removeEventListener("scroll", handleScroll, {
8792
- passive: true
8848
+ passive: true,
8793
8849
  });
8794
8850
  });
8795
8851
  }
8852
+
8796
8853
  const determineDragData = ({
8797
8854
  dragX,
8798
8855
  dragY,
8799
8856
  dragEvent,
8800
- isRelease = false
8857
+ isRelease = false,
8801
8858
  }) => {
8802
8859
  // === ÉTAT INITIAL (au moment du grab) ===
8803
- const {
8804
- grabX,
8805
- grabY,
8806
- grabLayout
8807
- } = gestureInfo;
8860
+ const { grabX, grabY, grabLayout } = gestureInfo;
8808
8861
  // === CE QUI EST DEMANDÉ (où on veut aller) ===
8809
8862
  // Calcul de la direction basé sur le mouvement précédent
8810
8863
  // (ne tient pas compte du mouvement final une fois les contraintes appliquées)
@@ -8816,38 +8869,56 @@ const createDragGestureController = (options = {}) => {
8816
8869
  const isGoingRight = dragX > currentDragX;
8817
8870
  const isGoingUp = dragY < currentDragY;
8818
8871
  const isGoingDown = dragY > currentDragY;
8819
- const layoutXRequested = direction.x ? scrollContainer.scrollLeft + (dragX - grabX) : grabLayout.scrollLeft;
8820
- const layoutYRequested = direction.y ? scrollContainer.scrollTop + (dragY - grabY) : grabLayout.scrollTop;
8872
+
8873
+ const layoutXRequested = direction.x
8874
+ ? scrollContainer.scrollLeft + (dragX - grabX)
8875
+ : grabLayout.scrollLeft;
8876
+ const layoutYRequested = direction.y
8877
+ ? scrollContainer.scrollTop + (dragY - grabY)
8878
+ : grabLayout.scrollTop;
8821
8879
  const layoutRequested = createLayout(layoutXRequested, layoutYRequested);
8822
8880
  const currentLayout = gestureInfo.layout;
8823
8881
  let layout;
8824
- if (layoutRequested.x === currentLayout.x && layoutRequested.y === currentLayout.y) {
8882
+ if (
8883
+ layoutRequested.x === currentLayout.x &&
8884
+ layoutRequested.y === currentLayout.y
8885
+ ) {
8825
8886
  layout = currentLayout;
8826
8887
  } else {
8827
8888
  // === APPLICATION DES CONTRAINTES ===
8828
8889
  let layoutConstrained = layoutRequested;
8829
8890
  const limitLayout = (left, top) => {
8830
- layoutConstrained = createLayout(left === undefined ? layoutConstrained.x : left - scrollableLeftAtGrab, top === undefined ? layoutConstrained.y : top - scrollableTopAtGrab);
8891
+ layoutConstrained = createLayout(
8892
+ left === undefined
8893
+ ? layoutConstrained.x
8894
+ : left - scrollableLeftAtGrab,
8895
+ top === undefined ? layoutConstrained.y : top - scrollableTopAtGrab,
8896
+ );
8831
8897
  };
8898
+
8832
8899
  publishBeforeDrag(layoutRequested, currentLayout, limitLayout, {
8833
8900
  dragEvent,
8834
- isRelease
8901
+ isRelease,
8835
8902
  });
8836
8903
  // === ÉTAT FINAL ===
8837
8904
  layout = layoutConstrained;
8838
8905
  }
8906
+
8839
8907
  const dragData = {
8840
8908
  dragX,
8841
8909
  dragY,
8842
8910
  layout,
8911
+
8843
8912
  isGoingLeft,
8844
8913
  isGoingRight,
8845
8914
  isGoingUp,
8846
8915
  isGoingDown,
8916
+
8847
8917
  status: isRelease ? "released" : "dragging",
8848
8918
  dragEvent: isRelease ? gestureInfo.dragEvent : dragEvent,
8849
- releaseEvent: isRelease ? dragEvent : null
8919
+ releaseEvent: isRelease ? dragEvent : null,
8850
8920
  };
8921
+
8851
8922
  if (isRelease) {
8852
8923
  return dragData;
8853
8924
  }
@@ -8872,6 +8943,7 @@ const createDragGestureController = (options = {}) => {
8872
8943
  }
8873
8944
  return dragData;
8874
8945
  };
8946
+
8875
8947
  const markAsStarted = () => {
8876
8948
  const clickSuppressionIsOver = suppressClickAfterGesture();
8877
8949
  addReleaseCallback(clickSuppressionIsOver);
@@ -8884,7 +8956,7 @@ const createDragGestureController = (options = {}) => {
8884
8956
  // would then outlive it: what one sees is a click swallowed long after
8885
8957
  // the drag it belonged to.
8886
8958
  dispatchPublicCustomEvent(element, "navi_drag_start", {
8887
- gestureInfo
8959
+ gestureInfo,
8888
8960
  });
8889
8961
  onDragStart?.(gestureInfo);
8890
8962
  };
@@ -8899,19 +8971,18 @@ const createDragGestureController = (options = {}) => {
8899
8971
  gestureInfo.started = true;
8900
8972
  markAsStarted();
8901
8973
  };
8902
- const drag = (dragX = gestureInfo.dragX,
8903
- // Scroll container relative X coordinate
8904
- dragY = gestureInfo.dragY,
8905
- // Scroll container relative Y coordinate
8906
- {
8907
- event = new CustomEvent("programmatic"),
8908
- isRelease = false
8909
- } = {}) => {
8974
+
8975
+ const drag = (
8976
+ dragX = gestureInfo.dragX, // Scroll container relative X coordinate
8977
+ dragY = gestureInfo.dragY, // Scroll container relative Y coordinate
8978
+ { event = new CustomEvent("programmatic"), isRelease = false } = {},
8979
+ ) => {
8980
+
8910
8981
  const dragData = determineDragData({
8911
8982
  dragX,
8912
8983
  dragY,
8913
8984
  dragEvent: event,
8914
- isRelease
8985
+ isRelease,
8915
8986
  });
8916
8987
  const [velocityX, velocityY] = measureVelocity(dragX, dragY);
8917
8988
  const startedPrevious = gestureInfo.started;
@@ -8941,36 +9012,33 @@ const createDragGestureController = (options = {}) => {
8941
9012
  const someLayoutChange = gestureInfo.layout !== layoutPrevious;
8942
9013
  dispatchPublicCustomEvent(element, "navi_drag", {
8943
9014
  gestureInfo,
8944
- someLayoutChange
9015
+ someLayoutChange,
8945
9016
  });
8946
- publishDrag(gestureInfo,
8947
- // we still publish drag event even when unchanged
8948
- // because UI might need to adjust when document scrolls
8949
- // even if nothing truly changes visually the element
8950
- // can decide to stick to the scroll for example
8951
- someLayoutChange);
9017
+ publishDrag(
9018
+ gestureInfo,
9019
+ // we still publish drag event even when unchanged
9020
+ // because UI might need to adjust when document scrolls
9021
+ // even if nothing truly changes visually the element
9022
+ // can decide to stick to the scroll for example
9023
+ someLayoutChange,
9024
+ );
8952
9025
  };
9026
+
8953
9027
  const release = ({
8954
9028
  event = new CustomEvent("programmatic"),
8955
9029
  cancelled = event.type === "pointercancel",
8956
9030
  releaseX = gestureInfo.dragX,
8957
- releaseY = gestureInfo.dragY
9031
+ releaseY = gestureInfo.dragY,
8958
9032
  } = {}) => {
8959
9033
  // Written before the last drag is reported, so the callbacks reading the
8960
9034
  // end of the gesture all see the same one.
8961
9035
  gestureInfo.cancelled = cancelled;
8962
- drag(releaseX, releaseY, {
8963
- event,
8964
- isRelease: true
8965
- });
8966
- dispatchPublicCustomEvent(element, "navi_drag_release", {
8967
- gestureInfo
8968
- });
9036
+ drag(releaseX, releaseY, { event, isRelease: true });
9037
+ dispatchPublicCustomEvent(element, "navi_drag_release", { gestureInfo });
8969
9038
  publishRelease(gestureInfo);
8970
9039
  };
8971
- dispatchPublicCustomEvent(element, "navi_drag_grab", {
8972
- gestureInfo
8973
- });
9040
+
9041
+ dispatchPublicCustomEvent(element, "navi_drag_grab", { gestureInfo });
8974
9042
  onGrab?.(gestureInfo);
8975
9043
  const dragGesture = {
8976
9044
  gestureInfo,
@@ -8979,11 +9047,12 @@ const createDragGestureController = (options = {}) => {
8979
9047
  addReleaseCallback,
8980
9048
  start,
8981
9049
  drag,
8982
- release
9050
+ release,
8983
9051
  };
8984
9052
  return dragGesture;
8985
9053
  };
8986
9054
  dragGestureController.grab = grab;
9055
+
8987
9056
  const initDragByPointer = (grabEvent, dragOptions, initializer) => {
8988
9057
  if (!isPrimaryButtonEvent(grabEvent)) {
8989
9058
  return null;
@@ -8993,11 +9062,8 @@ const createDragGestureController = (options = {}) => {
8993
9062
  // target is a text node
8994
9063
  return null;
8995
9064
  }
8996
- const mouseEventCoords = mouseEvent => {
8997
- const {
8998
- clientX,
8999
- clientY
9000
- } = mouseEvent;
9065
+ const mouseEventCoords = (mouseEvent) => {
9066
+ const { clientX, clientY } = mouseEvent;
9001
9067
  return [clientX, clientY];
9002
9068
  };
9003
9069
  const [grabX, grabY] = mouseEventCoords(grabEvent);
@@ -9005,23 +9071,21 @@ const createDragGestureController = (options = {}) => {
9005
9071
  grabX,
9006
9072
  grabY,
9007
9073
  event: grabEvent,
9008
- ...dragOptions
9074
+ ...dragOptions,
9009
9075
  });
9010
- const dragViaPointer = dragEvent => {
9076
+ const dragViaPointer = (dragEvent) => {
9011
9077
  const [mouseDragX, mouseDragY] = mouseEventCoords(dragEvent);
9012
9078
  dragGesture.drag(mouseDragX, mouseDragY, {
9013
- event: dragEvent
9079
+ event: dragEvent,
9014
9080
  });
9015
9081
  };
9016
- const releaseViaPointer = (pointerEvent, {
9017
- cancelled
9018
- } = {}) => {
9082
+ const releaseViaPointer = (pointerEvent, { cancelled } = {}) => {
9019
9083
  const [mouseReleaseX, mouseReleaseY] = mouseEventCoords(pointerEvent);
9020
9084
  dragGesture.release({
9021
9085
  event: pointerEvent,
9022
9086
  cancelled,
9023
9087
  releaseX: mouseReleaseX,
9024
- releaseY: mouseReleaseY
9088
+ releaseY: mouseReleaseY,
9025
9089
  });
9026
9090
  };
9027
9091
  dragGesture.dragViaPointer = dragViaPointer;
@@ -9040,7 +9104,7 @@ const createDragGestureController = (options = {}) => {
9040
9104
  * everybody: the element would stop being selectable even when nobody is
9041
9105
  * dragging it. Here it is refused for the length of one gesture.
9042
9106
  */
9043
- const preventSelectStart = selectStartEvent => {
9107
+ const preventSelectStart = (selectStartEvent) => {
9044
9108
  selectStartEvent.preventDefault();
9045
9109
  };
9046
9110
  document.addEventListener("selectstart", preventSelectStart);
@@ -9075,7 +9139,7 @@ const createDragGestureController = (options = {}) => {
9075
9139
  * waiting for a click that never comes costs nothing until then.
9076
9140
  */
9077
9141
  removePendingMultiClickListener();
9078
- const onClick = clickEvent => {
9142
+ const onClick = (clickEvent) => {
9079
9143
  const clickCount = clickEvent.detail;
9080
9144
  if (clickCount < 2) {
9081
9145
  return;
@@ -9091,7 +9155,10 @@ const createDragGestureController = (options = {}) => {
9091
9155
  // is doing its own selecting — a second opinion there would only fight
9092
9156
  // it.
9093
9157
  const clickTarget = clickEvent.target;
9094
- if (clickTarget !== grabEvent.target && !clickTarget.contains(grabEvent.target)) {
9158
+ if (
9159
+ clickTarget !== grabEvent.target &&
9160
+ !clickTarget.contains(grabEvent.target)
9161
+ ) {
9095
9162
  return;
9096
9163
  }
9097
9164
  // Text the page says is not selectable stays not selectable: a
@@ -9117,204 +9184,204 @@ const createDragGestureController = (options = {}) => {
9117
9184
  onMove: dragViaPointer,
9118
9185
  onRelease: releaseViaPointer,
9119
9186
  gestureInfo: dragGesture.gestureInfo,
9120
- dragGesture
9187
+ dragGesture,
9121
9188
  });
9122
9189
  dragGesture.addReleaseCallback(() => {
9123
9190
  cleanup();
9124
9191
  });
9125
9192
  return dragGesture;
9126
9193
  };
9194
+
9127
9195
  const grabViaPointer = (grabEvent, options) => {
9128
9196
  if (grabEvent.type === "pointerdown") {
9129
- return initDragByPointer(grabEvent, options, ({
9130
- onMove,
9131
- onRelease,
9132
- gestureInfo,
9133
- dragGesture
9134
- }) => {
9135
- // Captured on something that will still be there at the end of the
9136
- // gesture: the browser releases the capture when its element leaves the
9137
- // document, and a gesture whose own effect replaces the DOM under the
9138
- // finger would lose the pointer at its first move. Callers whose target
9139
- // is stable have nothing to say and keep it.
9140
- const target = options?.pointerCaptureElement || grabEvent.target;
9141
- // The capture is ONE per pointer for the whole document: taking it is
9142
- // taking it away from whoever had it, who is then told the exact same
9143
- // thing it is told when its own gesture ends. So it is taken when this
9144
- // gesture is established and not a moment earlier — for most callers
9145
- // that is the grab itself (the intent was settled before, by a handle
9146
- // or a long press), and for a caller that is still deciding what the
9147
- // press means, it is whenever it says so (see capturePointer).
9148
- let captured = false;
9149
- dragGesture.capturePointer = () => {
9150
- captured = true;
9151
- // Written down before it is taken: this is the only place a capture
9152
- // is ever taken from, so what this map says is who asked for it
9153
- // last — which is what tells a hand-over from a capture the browser
9154
- // dropped on its own (see onCaptureLost).
9155
- captureHolderByPointerId.set(grabEvent.pointerId, dragGesture);
9156
- target.setPointerCapture(grabEvent.pointerId);
9157
- };
9158
- if (!options?.pointerCaptureDeferred) {
9159
- dragGesture.capturePointer();
9160
- }
9161
- /*
9162
- * A touchmove left alone is the browser deciding the touch belongs to
9163
- * it: it takes it to scroll with, and a touch it has taken is a pointer
9164
- * stream it CANCELS — the gesture dies mid-move, the finger is still
9165
- * down, and nothing reads it anymore.
9166
- *
9167
- * Refused only once the gesture is established (a `touch-action: none`
9168
- * would take the touch from everyone who merely brushes past the
9169
- * element), but LISTENED FOR from the grab: whether a touchmove can be
9170
- * refused at all is decided when the touch begins, from the listeners
9171
- * present at that moment. Registered later, the listener is handed
9172
- * events that are already `cancelable: false` — refusing them does
9173
- * nothing, and the reason is invisible in the code that refuses.
9174
- *
9175
- * On the window in capture AND on the grabbed element: a touch keeps
9176
- * being dispatched at the node it started on, and a gesture may take
9177
- * that node out of the document (a page that travels navigates) — from
9178
- * then on the event never passes through the window on its way
9179
- * anywhere.
9180
- */
9181
- const preventTouchScroll = touchMoveEvent => {
9182
- if (gestureInfo.started && touchMoveEvent.cancelable) {
9183
- touchMoveEvent.preventDefault();
9184
- }
9185
- };
9186
- const grabTarget = grabEvent.target;
9187
- window.addEventListener("touchmove", preventTouchScroll, {
9188
- passive: false,
9189
- capture: true
9190
- });
9191
- grabTarget.addEventListener("touchmove", preventTouchScroll, {
9192
- passive: false
9193
- });
9194
- // Only OUR capture ending is this gesture's business:
9195
- // lostpointercapture bubbles, so a descendant giving up its own capture
9196
- // walks straight into this listener. That is not a rare shape — it is
9197
- // exactly what happens when a gesture hands over to another one (a
9198
- // press that becomes a drag releases its intermediate gesture, held on
9199
- // the pressed element, while the real one is being held on a container
9200
- // above it), and taken as our own it kills the new gesture one
9201
- // millisecond after it started.
9202
- //
9203
- // And when it IS ours, it is a loss and never an end: the ends a
9204
- // gesture has are the pointer going up and the pointer being
9205
- // cancelled, both listened for below. What a loss MEANS is the
9206
- // question, and the event does not answer it — two very different
9207
- // things arrive as the same one:
9208
- //
9209
- // - it was HANDED OVER: another gesture took the pointer, or the
9210
- // element it was held on left the document. There is nothing to go
9211
- // on with, and what was being carried must go back rather than land
9212
- // wherever the hand happened to be.
9213
- // - it was simply LET GO OF by the browser, with the hand still down
9214
- // and still moving. It happens, and not rarely: the capture is a
9215
- // guarantee that events keep coming to one element, and the browser
9216
- // drops it for reasons of its own that no code here can see. Killing
9217
- // the gesture for that is dropping an object mid-air — the copy
9218
- // vanishes, the place the hint had lit up is thrown away, and the
9219
- // hand is left having done nothing.
9220
- //
9221
- // They are told apart by who asked (see captureHolderByPointerId): a
9222
- // capture nobody here took, on an element still in the document, was
9223
- // let go of. The gesture does not need it — every move and the release
9224
- // are read at the WINDOW, not at the element — so it goes on.
9225
- const onCaptureLost = pointerEvent => {
9226
- if (!captured || pointerEvent.target !== target) {
9227
- return;
9228
- }
9229
- const handedOver = captureHolderByPointerId.get(grabEvent.pointerId) !== dragGesture;
9230
- if (!handedOver && target.isConnected) {
9231
- // Nobody took it and the element it was held on is still there:
9232
- // the browser let the capture go by itself, which it does — a
9233
- // node moved by a re-render and put straight back, a decision of
9234
- // its own we are not told the reason for. The hand has not let go
9235
- // of anything, so neither does the gesture: it is a guarantee that
9236
- // was lost, not the gesture. Every move and the release are read
9237
- // at the window (see below), so it goes on without it rather than
9238
- // dropping what is still being carried — and the drop the hand was
9239
- // aiming at, which the hint had already lit up, still happens.
9240
- captured = false;
9241
- return;
9197
+ return initDragByPointer(
9198
+ grabEvent,
9199
+ options,
9200
+ ({ onMove, onRelease, gestureInfo, dragGesture }) => {
9201
+ // Captured on something that will still be there at the end of the
9202
+ // gesture: the browser releases the capture when its element leaves the
9203
+ // document, and a gesture whose own effect replaces the DOM under the
9204
+ // finger would lose the pointer at its first move. Callers whose target
9205
+ // is stable have nothing to say and keep it.
9206
+ const target = options?.pointerCaptureElement || grabEvent.target;
9207
+ // The capture is ONE per pointer for the whole document: taking it is
9208
+ // taking it away from whoever had it, who is then told the exact same
9209
+ // thing it is told when its own gesture ends. So it is taken when this
9210
+ // gesture is established and not a moment earlier — for most callers
9211
+ // that is the grab itself (the intent was settled before, by a handle
9212
+ // or a long press), and for a caller that is still deciding what the
9213
+ // press means, it is whenever it says so (see capturePointer).
9214
+ let captured = false;
9215
+ dragGesture.capturePointer = () => {
9216
+ captured = true;
9217
+ // Written down before it is taken: this is the only place a capture
9218
+ // is ever taken from, so what this map says is who asked for it
9219
+ // last — which is what tells a hand-over from a capture the browser
9220
+ // dropped on its own (see onCaptureLost).
9221
+ captureHolderByPointerId.set(grabEvent.pointerId, dragGesture);
9222
+ target.setPointerCapture(grabEvent.pointerId);
9223
+ };
9224
+ if (!options?.pointerCaptureDeferred) {
9225
+ dragGesture.capturePointer();
9242
9226
  }
9243
- onRelease(pointerEvent, {
9244
- cancelled: true
9227
+ /*
9228
+ * A touchmove left alone is the browser deciding the touch belongs to
9229
+ * it: it takes it to scroll with, and a touch it has taken is a pointer
9230
+ * stream it CANCELS — the gesture dies mid-move, the finger is still
9231
+ * down, and nothing reads it anymore.
9232
+ *
9233
+ * Refused only once the gesture is established (a `touch-action: none`
9234
+ * would take the touch from everyone who merely brushes past the
9235
+ * element), but LISTENED FOR from the grab: whether a touchmove can be
9236
+ * refused at all is decided when the touch begins, from the listeners
9237
+ * present at that moment. Registered later, the listener is handed
9238
+ * events that are already `cancelable: false` — refusing them does
9239
+ * nothing, and the reason is invisible in the code that refuses.
9240
+ *
9241
+ * On the window in capture AND on the grabbed element: a touch keeps
9242
+ * being dispatched at the node it started on, and a gesture may take
9243
+ * that node out of the document (a page that travels navigates) — from
9244
+ * then on the event never passes through the window on its way
9245
+ * anywhere.
9246
+ */
9247
+ const preventTouchScroll = (touchMoveEvent) => {
9248
+ if (gestureInfo.started && touchMoveEvent.cancelable) {
9249
+ touchMoveEvent.preventDefault();
9250
+ }
9251
+ };
9252
+ const grabTarget = grabEvent.target;
9253
+ window.addEventListener("touchmove", preventTouchScroll, {
9254
+ passive: false,
9255
+ capture: true,
9245
9256
  });
9246
- };
9247
- target.addEventListener("lostpointercapture", onCaptureLost);
9248
- target.addEventListener("pointercancel", onRelease);
9249
- target.addEventListener("pointerup", onRelease);
9250
- // Read from the window rather than from the element while the pointer
9251
- // is not this gesture's: without a capture a move is delivered to
9252
- // whatever is under the pointer, and a hand that has left the element
9253
- // is exactly the hand this gesture is trying to make sense of. The
9254
- // capture phase reaches the window before anything else, captured or
9255
- // not, so there is one place to read whichever way the gesture ends up.
9256
- const onPointerMove = pointerEvent => {
9257
- if (pointerEvent.pointerId !== grabEvent.pointerId) {
9258
- return;
9259
- }
9260
- onMove(pointerEvent);
9261
- };
9262
- window.addEventListener("pointermove", onPointerMove, true);
9263
- // The end of the pointer is also listened for on the window, because
9264
- // the end is the one event a gesture cannot afford to miss and the
9265
- // element it is captured on is not always on its way: a pointer can
9266
- // be delivered somewhere else entirely (a browser view transition
9267
- // sends presses to the document root), and a cancel dispatched there
9268
- // never passes through this element. Missed, the gesture never ends —
9269
- // whatever it was holding stays held.
9270
- let released = false;
9271
- const onPointerEnd = pointerEvent => {
9272
- if (pointerEvent.pointerId !== grabEvent.pointerId || released) {
9273
- return;
9274
- }
9275
- released = true;
9276
- onRelease(pointerEvent);
9277
- };
9278
- window.addEventListener("pointerup", onPointerEnd, true);
9279
- window.addEventListener("pointercancel", onPointerEnd, true);
9280
- return () => {
9281
- // Listeners first, capture last: giving the pointer back is the
9282
- // one thing here that can throw, and a gesture that fails to clean
9283
- // up half way is worse than one that never cleaned up at all — its
9284
- // listeners stay on the element and answer the NEXT gesture, from
9285
- // a gesture whose pointer is long gone.
9286
- window.removeEventListener("touchmove", preventTouchScroll, {
9287
- capture: true
9257
+ grabTarget.addEventListener("touchmove", preventTouchScroll, {
9258
+ passive: false,
9288
9259
  });
9289
- grabTarget.removeEventListener("touchmove", preventTouchScroll);
9290
- target.removeEventListener("lostpointercapture", onCaptureLost);
9291
- target.removeEventListener("pointercancel", onRelease);
9292
- window.removeEventListener("pointermove", onPointerMove, true);
9293
- target.removeEventListener("pointerup", onRelease);
9294
- window.removeEventListener("pointerup", onPointerEnd, true);
9295
- window.removeEventListener("pointercancel", onPointerEnd, true);
9296
- // Only what this gesture took, and only while there is something to
9297
- // give back: a capture on that element may be someone else's (two
9298
- // gestures reading the same press hold the same node), and a pointer
9299
- // that is up no longer exists — the browser has already dropped the
9300
- // capture with it, and asking again throws ("No active pointer with
9301
- // the given id is found") on the most ordinary release there is.
9302
- if (captureHolderByPointerId.get(grabEvent.pointerId) === dragGesture) {
9303
- captureHolderByPointerId.delete(grabEvent.pointerId);
9304
- }
9305
- if (captured && target.hasPointerCapture(grabEvent.pointerId)) {
9306
- target.releasePointerCapture(grabEvent.pointerId);
9307
- }
9308
- };
9309
- });
9260
+ // Only OUR capture ending is this gesture's business:
9261
+ // lostpointercapture bubbles, so a descendant giving up its own capture
9262
+ // walks straight into this listener. That is not a rare shape — it is
9263
+ // exactly what happens when a gesture hands over to another one (a
9264
+ // press that becomes a drag releases its intermediate gesture, held on
9265
+ // the pressed element, while the real one is being held on a container
9266
+ // above it), and taken as our own it kills the new gesture one
9267
+ // millisecond after it started.
9268
+ //
9269
+ // And when it IS ours, it is a loss and never an end: the ends a
9270
+ // gesture has are the pointer going up and the pointer being
9271
+ // cancelled, both listened for below. What a loss MEANS is the
9272
+ // question, and the event does not answer it — two very different
9273
+ // things arrive as the same one:
9274
+ //
9275
+ // - it was HANDED OVER: another gesture took the pointer, or the
9276
+ // element it was held on left the document. There is nothing to go
9277
+ // on with, and what was being carried must go back rather than land
9278
+ // wherever the hand happened to be.
9279
+ // - it was simply LET GO OF by the browser, with the hand still down
9280
+ // and still moving. It happens, and not rarely: the capture is a
9281
+ // guarantee that events keep coming to one element, and the browser
9282
+ // drops it for reasons of its own that no code here can see. Killing
9283
+ // the gesture for that is dropping an object mid-air — the copy
9284
+ // vanishes, the place the hint had lit up is thrown away, and the
9285
+ // hand is left having done nothing.
9286
+ //
9287
+ // They are told apart by who asked (see captureHolderByPointerId): a
9288
+ // capture nobody here took, on an element still in the document, was
9289
+ // let go of. The gesture does not need it — every move and the release
9290
+ // are read at the WINDOW, not at the element — so it goes on.
9291
+ const onCaptureLost = (pointerEvent) => {
9292
+ if (!captured || pointerEvent.target !== target) {
9293
+ return;
9294
+ }
9295
+ const handedOver =
9296
+ captureHolderByPointerId.get(grabEvent.pointerId) !== dragGesture;
9297
+ if (!handedOver && target.isConnected) {
9298
+ // Nobody took it and the element it was held on is still there:
9299
+ // the browser let the capture go by itself, which it does — a
9300
+ // node moved by a re-render and put straight back, a decision of
9301
+ // its own we are not told the reason for. The hand has not let go
9302
+ // of anything, so neither does the gesture: it is a guarantee that
9303
+ // was lost, not the gesture. Every move and the release are read
9304
+ // at the window (see below), so it goes on without it rather than
9305
+ // dropping what is still being carried — and the drop the hand was
9306
+ // aiming at, which the hint had already lit up, still happens.
9307
+ captured = false;
9308
+ return;
9309
+ }
9310
+ onRelease(pointerEvent, { cancelled: true });
9311
+ };
9312
+ target.addEventListener("lostpointercapture", onCaptureLost);
9313
+ target.addEventListener("pointercancel", onRelease);
9314
+ target.addEventListener("pointerup", onRelease);
9315
+ // Read from the window rather than from the element while the pointer
9316
+ // is not this gesture's: without a capture a move is delivered to
9317
+ // whatever is under the pointer, and a hand that has left the element
9318
+ // is exactly the hand this gesture is trying to make sense of. The
9319
+ // capture phase reaches the window before anything else, captured or
9320
+ // not, so there is one place to read whichever way the gesture ends up.
9321
+ const onPointerMove = (pointerEvent) => {
9322
+ if (pointerEvent.pointerId !== grabEvent.pointerId) {
9323
+ return;
9324
+ }
9325
+ onMove(pointerEvent);
9326
+ };
9327
+ window.addEventListener("pointermove", onPointerMove, true);
9328
+ // The end of the pointer is also listened for on the window, because
9329
+ // the end is the one event a gesture cannot afford to miss and the
9330
+ // element it is captured on is not always on its way: a pointer can
9331
+ // be delivered somewhere else entirely (a browser view transition
9332
+ // sends presses to the document root), and a cancel dispatched there
9333
+ // never passes through this element. Missed, the gesture never ends —
9334
+ // whatever it was holding stays held.
9335
+ let released = false;
9336
+ const onPointerEnd = (pointerEvent) => {
9337
+ if (pointerEvent.pointerId !== grabEvent.pointerId || released) {
9338
+ return;
9339
+ }
9340
+ released = true;
9341
+ onRelease(pointerEvent);
9342
+ };
9343
+ window.addEventListener("pointerup", onPointerEnd, true);
9344
+ window.addEventListener("pointercancel", onPointerEnd, true);
9345
+ return () => {
9346
+ // Listeners first, capture last: giving the pointer back is the
9347
+ // one thing here that can throw, and a gesture that fails to clean
9348
+ // up half way is worse than one that never cleaned up at all — its
9349
+ // listeners stay on the element and answer the NEXT gesture, from
9350
+ // a gesture whose pointer is long gone.
9351
+ window.removeEventListener("touchmove", preventTouchScroll, {
9352
+ capture: true,
9353
+ });
9354
+ grabTarget.removeEventListener("touchmove", preventTouchScroll);
9355
+ target.removeEventListener("lostpointercapture", onCaptureLost);
9356
+ target.removeEventListener("pointercancel", onRelease);
9357
+ window.removeEventListener("pointermove", onPointerMove, true);
9358
+ target.removeEventListener("pointerup", onRelease);
9359
+ window.removeEventListener("pointerup", onPointerEnd, true);
9360
+ window.removeEventListener("pointercancel", onPointerEnd, true);
9361
+ // Only what this gesture took, and only while there is something to
9362
+ // give back: a capture on that element may be someone else's (two
9363
+ // gestures reading the same press hold the same node), and a pointer
9364
+ // that is up no longer exists — the browser has already dropped the
9365
+ // capture with it, and asking again throws ("No active pointer with
9366
+ // the given id is found") on the most ordinary release there is.
9367
+ if (
9368
+ captureHolderByPointerId.get(grabEvent.pointerId) === dragGesture
9369
+ ) {
9370
+ captureHolderByPointerId.delete(grabEvent.pointerId);
9371
+ }
9372
+ if (captured && target.hasPointerCapture(grabEvent.pointerId)) {
9373
+ target.releasePointerCapture(grabEvent.pointerId);
9374
+ }
9375
+ };
9376
+ },
9377
+ );
9310
9378
  }
9311
9379
  if (grabEvent.type === "mousedown") {
9312
- console.warn(`Received "mousedown" event, "pointerdown" events are recommended to perform drag gestures.`);
9313
- return initDragByPointer(grabEvent, options, ({
9314
- onMove,
9315
- onRelease
9316
- }) => {
9317
- const onPointerUp = pointerEvent => {
9380
+ console.warn(
9381
+ `Received "mousedown" event, "pointerdown" events are recommended to perform drag gestures.`,
9382
+ );
9383
+ return initDragByPointer(grabEvent, options, ({ onMove, onRelease }) => {
9384
+ const onPointerUp = (pointerEvent) => {
9318
9385
  // <button disabled> for example does not emit mouseup if we release mouse over it
9319
9386
  // -> we add "pointerup" to catch mouseup occuring on disabled element
9320
9387
  if (pointerEvent.pointerType === "mouse") {
@@ -9331,15 +9398,19 @@ const createDragGestureController = (options = {}) => {
9331
9398
  };
9332
9399
  });
9333
9400
  }
9334
- throw new Error(`Unsupported "${grabEvent.type}" evenet passed to grabViaPointer. "pointerdown" was expected.`);
9401
+ throw new Error(
9402
+ `Unsupported "${grabEvent.type}" evenet passed to grabViaPointer. "pointerdown" was expected.`,
9403
+ );
9335
9404
  };
9336
9405
  dragGestureController.grabViaPointer = grabViaPointer;
9406
+
9337
9407
  return dragGestureController;
9338
9408
  };
9339
9409
 
9340
9410
  // Only the primary button drags: a right click (or any secondary button) opens
9341
9411
  // a context menu, it never grabs anything.
9342
- const isPrimaryButtonEvent = event => event.button === undefined || event.button === 0;
9412
+ const isPrimaryButtonEvent = (event) =>
9413
+ event.button === undefined || event.button === 0;
9343
9414
 
9344
9415
  /*
9345
9416
  * Speed over the last VELOCITY_WINDOW_MS rather than between the last two
@@ -9352,18 +9423,11 @@ const isPrimaryButtonEvent = event => event.button === undefined || event.button
9352
9423
  */
9353
9424
  const VELOCITY_WINDOW_MS = 100;
9354
9425
  const createVelocityMeter = (grabX, grabY) => {
9355
- const samples = [{
9356
- time: performance.now(),
9357
- x: grabX,
9358
- y: grabY
9359
- }];
9426
+ const samples = [{ time: performance.now(), x: grabX, y: grabY }];
9427
+
9360
9428
  const measureVelocity = (x, y) => {
9361
9429
  const time = performance.now();
9362
- samples.push({
9363
- time,
9364
- x,
9365
- y
9366
- });
9430
+ samples.push({ time, x, y });
9367
9431
  while (samples.length > 2 && time - samples[1].time > VELOCITY_WINDOW_MS) {
9368
9432
  samples.shift();
9369
9433
  }
@@ -9374,14 +9438,17 @@ const createVelocityMeter = (grabX, grabY) => {
9374
9438
  }
9375
9439
  return [(x - oldestSample.x) / elapsed, (y - oldestSample.y) / elapsed];
9376
9440
  };
9441
+
9377
9442
  return measureVelocity;
9378
9443
  };
9444
+
9379
9445
  const definePropertyAsReadOnly = (object, propertyName) => {
9380
9446
  Object.defineProperty(object, propertyName, {
9381
9447
  writable: false,
9382
- value: object[propertyName]
9448
+ value: object[propertyName],
9383
9449
  });
9384
9450
  };
9451
+
9385
9452
  const NOOP = () => {};
9386
9453
  let removePendingMultiClickListener = NOOP;
9387
9454
 
@@ -9427,12 +9494,11 @@ const selectParagraphAtPoint = (x, y) => {
9427
9494
  paragraphRange.selectNodeContents(block);
9428
9495
  selection.addRange(paragraphRange);
9429
9496
  };
9430
- const getClosestBlock = node => {
9497
+
9498
+ const getClosestBlock = (node) => {
9431
9499
  let element = node.nodeType === 1 ? node : node.parentElement;
9432
9500
  while (element) {
9433
- const {
9434
- display
9435
- } = window.getComputedStyle(element);
9501
+ const { display } = window.getComputedStyle(element);
9436
9502
  // Everything that is not laid out as a line inside another line: the first
9437
9503
  // ancestor that breaks the flow is the one whose text reads as a paragraph
9438
9504
  // of its own.
@@ -9443,6 +9509,7 @@ const getClosestBlock = node => {
9443
9509
  }
9444
9510
  return null;
9445
9511
  };
9512
+
9446
9513
  const createCaretRange = (x, y) => {
9447
9514
  if (document.caretPositionFromPoint) {
9448
9515
  const caretPosition = document.caretPositionFromPoint(x, y);
@@ -9459,7 +9526,8 @@ const createCaretRange = (x, y) => {
9459
9526
  }
9460
9527
  return null;
9461
9528
  };
9462
- const isSelectable = element => {
9529
+
9530
+ const isSelectable = (element) => {
9463
9531
  if (!element || element.nodeType !== 1) {
9464
9532
  return true;
9465
9533
  }
@@ -9467,6 +9535,7 @@ const isSelectable = element => {
9467
9535
  const userSelect = computedStyle.userSelect || computedStyle.webkitUserSelect;
9468
9536
  return userSelect !== "none";
9469
9537
  };
9538
+
9470
9539
  const collapseSelection = () => {
9471
9540
  const selection = window.getSelection();
9472
9541
  if (selection && !selection.isCollapsed) {
@@ -9474,53 +9543,7 @@ const collapseSelection = () => {
9474
9543
  }
9475
9544
  };
9476
9545
 
9477
- installImportMetaCssBuild(import.meta);/**
9478
- * When a press becomes a drag.
9479
- *
9480
- * A pointer going down on a draggable element is ambiguous — it may be a click,
9481
- * a text selection, a scroll, or a drag — and starting the gesture right away
9482
- * would steal all the others. This module picks which signal resolves the
9483
- * ambiguity for the pointer at hand, and only then hands over to the real
9484
- * gesture.
9485
- *
9486
- * There is one gesture, with a trigger per pointer:
9487
- * - a dedicated handle ([data-drag-handle]) says it outright: drag on contact
9488
- * - a mouse resolves it by distance — a mouse scrolls with its wheel, so travel
9489
- * can only mean drag
9490
- * - a finger resolves it by time — travel is exactly what a scroll looks like,
9491
- * so the only unambiguous signal left is a finger that does NOT move
9492
- *
9493
- * Whichever trigger fired, it has established the intent: the gesture then
9494
- * starts at the first pixel, without a second threshold to cross.
9495
- *
9496
- * WHEN A FINGER MAY TRAVEL TOO: [data-drag-on-contact].
9497
- *
9498
- * The wait a finger is asked for is not a rule about fingers, it is the answer to
9499
- * an ambiguity — travel means scroll as much as it means drag, so the two have to
9500
- * be told apart. Where nothing scrolls the ambiguity does not exist, and the wait
9501
- * is asking the hand to prove something nothing else could have meant: inside a
9502
- * dialog that holds the page still, a finger travelling on a piece can only be
9503
- * carrying it.
9504
- *
9505
- * So the attribute says that place, not that element — put on the dialog, every
9506
- * source inside it reads by distance like a mouse does, at the same few pixels.
9507
- * A tap is left alone by that: a press that goes nowhere is still a press, which
9508
- * is what a piece that is also a link or a card needs.
9509
- *
9510
- * A HOLD is not, and nothing in such a place is text to select. Taking the wait
9511
- * away takes away the only thing that answered a finger held still, so the
9512
- * browser answers it alone: its own long press selects the word under the thumb,
9513
- * and the card the hand meant to carry is left blue and handled. Said to every
9514
- * pointer — a mouse there drags from the first few pixels, so a selection begun
9515
- * there is one that could never be finished either. What says its press is its
9516
- * own keeps its text (see the stylesheet below).
9517
- *
9518
- * It is opt-in and cannot be anything else. Nothing here can see whether the
9519
- * surroundings scroll — a page scrolls by default, an overflow is one CSS
9520
- * property away, and getting it wrong the wrong way means the list runs away
9521
- * under the finger that meant to reorder it. Only the application knows it has
9522
- * taken the scroll away.
9523
- */
9546
+ installImportMetaCssBuild(import.meta);
9524
9547
 
9525
9548
  /* At module scope, and on the markers rather than on the pressed element: both
9526
9549
  rules below have to be true BEFORE the finger lands — a stylesheet, never a
@@ -9541,7 +9564,7 @@ installImportMetaCssBuild(import.meta);/**
9541
9564
  lets the page scroll and still makes the refusal effective — provided the
9542
9565
  listener that will refuse is already known too, which is markDragSource's
9543
9566
  half of the same rule. */
9544
- const css$4 = /* css */`[data-drag-handle], [data-drag-source] {
9567
+ const css$4 = /* css */ `[data-drag-handle], [data-drag-source] {
9545
9568
  -webkit-touch-callout: none;
9546
9569
  }
9547
9570
 
@@ -9632,9 +9655,7 @@ const keepTouchRefusable = () => {
9632
9655
  */
9633
9656
  const markDragSource = (element, axes = "xy") => {
9634
9657
  element.setAttribute("data-drag-source", axes);
9635
- element.addEventListener("touchmove", keepTouchRefusable, {
9636
- passive: false
9637
- });
9658
+ element.addEventListener("touchmove", keepTouchRefusable, { passive: false });
9638
9659
  return () => {
9639
9660
  element.removeAttribute("data-drag-source");
9640
9661
  element.removeEventListener("touchmove", keepTouchRefusable);
@@ -9671,40 +9692,49 @@ const markDragSource = (element, axes = "xy") => {
9671
9692
  * @param {function} [options.onPress]
9672
9693
  * The wait completed and the object is now held (haptics, scale…).
9673
9694
  */
9674
- const dragAfterIntent = (grabEvent, dragGestureInitializer, {
9675
- threshold = 5,
9676
- longPress = "if-touch",
9677
- longPressDelay = 400,
9678
- longPressSlop = 8,
9679
- onPressStart,
9680
- onPressCancel,
9681
- onPress
9682
- } = {}) => {
9695
+ const dragAfterIntent = (
9696
+ grabEvent,
9697
+ dragGestureInitializer,
9698
+ {
9699
+ threshold = 5,
9700
+ longPress = "if-touch",
9701
+ longPressDelay = 400,
9702
+ longPressSlop = 8,
9703
+ onPressStart,
9704
+ onPressCancel,
9705
+ onPress,
9706
+ } = {},
9707
+ ) => {
9683
9708
  if (!isPrimaryButtonEvent(grabEvent)) {
9684
9709
  return;
9685
9710
  }
9686
9711
  const target = grabEvent.target;
9687
- const isDedicatedHandle = target.closest && target.closest("[data-drag-handle]");
9712
+ const isDedicatedHandle =
9713
+ target.closest && target.closest("[data-drag-handle]");
9688
9714
  if (isDedicatedHandle) {
9689
9715
  startDragGesture(dragGestureInitializer);
9690
9716
  return;
9691
9717
  }
9692
- const startsOnLongPress = longPress === true || longPress === "if-touch" && grabEvent.pointerType === "touch" &&
9693
- // The wait tells a scroll from a drag, and here there is no scroll to tell
9694
- // it from — see [data-drag-on-contact] at the top of this file.
9695
- !(target.closest && target.closest("[data-drag-on-contact]"));
9718
+ const startsOnLongPress =
9719
+ longPress === true ||
9720
+ (longPress === "if-touch" &&
9721
+ grabEvent.pointerType === "touch" &&
9722
+ // The wait tells a scroll from a drag, and here there is no scroll to tell
9723
+ // it from — see [data-drag-on-contact] at the top of this file.
9724
+ !(target.closest && target.closest("[data-drag-on-contact]")));
9696
9725
  if (startsOnLongPress) {
9697
9726
  dragAfterLongPress(grabEvent, dragGestureInitializer, {
9698
9727
  longPressDelay,
9699
9728
  longPressSlop,
9700
9729
  onPressStart,
9701
9730
  onPressCancel,
9702
- onPress
9731
+ onPress,
9703
9732
  });
9704
9733
  return;
9705
9734
  }
9706
9735
  dragAfterDistance(grabEvent, dragGestureInitializer, threshold);
9707
9736
  };
9737
+
9708
9738
  const startDragGesture = (dragGestureInitializer, catchUpEvent) => {
9709
9739
  const dragGesture = dragGestureInitializer();
9710
9740
  if (!dragGesture) {
@@ -9718,6 +9748,7 @@ const startDragGesture = (dragGestureInitializer, catchUpEvent) => {
9718
9748
  }
9719
9749
  return dragGesture;
9720
9750
  };
9751
+
9721
9752
  const dragAfterDistance = (grabEvent, dragGestureInitializer, threshold) => {
9722
9753
  const significantDragGestureController = createDragGestureController({
9723
9754
  threshold,
@@ -9725,28 +9756,28 @@ const dragAfterDistance = (grabEvent, dragGestureInitializer, threshold) => {
9725
9756
  // user should still be able to scroll or interact with the document
9726
9757
  // only once the gesture is significant we take control
9727
9758
  documentInteractions: "manual",
9728
- onDragStart: gestureInfo => {
9759
+ onDragStart: (gestureInfo) => {
9729
9760
  significantDragGesture.release(); // kill that gesture
9730
9761
  startDragGesture(dragGestureInitializer, gestureInfo.dragEvent);
9731
- }
9732
- });
9733
- const significantDragGesture = significantDragGestureController.grabViaPointer(grabEvent, {
9734
- element: grabEvent.target,
9735
- // This one owns nothing: it measures a distance to find out whether there
9736
- // is a gesture at all, and it is over the moment there is. Taking the
9737
- // pointer for that would take it from whoever is already holding this same
9738
- // element for a gesture of their own — and giving it back at the threshold
9739
- // would tell them theirs is over.
9740
- pointerCaptureDeferred: true
9762
+ },
9741
9763
  });
9764
+ const significantDragGesture =
9765
+ significantDragGestureController.grabViaPointer(grabEvent, {
9766
+ element: grabEvent.target,
9767
+ // This one owns nothing: it measures a distance to find out whether there
9768
+ // is a gesture at all, and it is over the moment there is. Taking the
9769
+ // pointer for that would take it from whoever is already holding this same
9770
+ // element for a gesture of their own — and giving it back at the threshold
9771
+ // would tell them theirs is over.
9772
+ pointerCaptureDeferred: true,
9773
+ });
9742
9774
  };
9743
- const dragAfterLongPress = (grabEvent, dragGestureInitializer, {
9744
- longPressDelay,
9745
- longPressSlop,
9746
- onPressStart,
9747
- onPressCancel,
9748
- onPress
9749
- }) => {
9775
+
9776
+ const dragAfterLongPress = (
9777
+ grabEvent,
9778
+ dragGestureInitializer,
9779
+ { longPressDelay, longPressSlop, onPressStart, onPressCancel, onPress },
9780
+ ) => {
9750
9781
  /*
9751
9782
  * Nothing is done here to keep the touch refusable: whether it can be refused
9752
9783
  * at all was settled when the finger landed, from what the element already
@@ -9760,9 +9791,7 @@ const dragAfterLongPress = (grabEvent, dragGestureInitializer, {
9760
9791
  slop: longPressSlop,
9761
9792
  onPressStart,
9762
9793
  onPressCancel,
9763
- onPressHeld: (pressEvent, {
9764
- endPress
9765
- }) => {
9794
+ onPressHeld: (pressEvent, { endPress }) => {
9766
9795
  onPress?.(pressEvent);
9767
9796
  const dragGesture = startDragGesture(dragGestureInitializer);
9768
9797
  if (!dragGesture) {
@@ -9772,7 +9801,7 @@ const dragAfterLongPress = (grabEvent, dragGestureInitializer, {
9772
9801
  dragGesture.addReleaseCallback(() => {
9773
9802
  endPress();
9774
9803
  });
9775
- }
9804
+ },
9776
9805
  });
9777
9806
  };
9778
9807
 
@@ -10292,7 +10321,9 @@ const getDragCoordinates = (
10292
10321
  return [leftRelativeToScrollContainer, topRelativeToScrollContainer];
10293
10322
  };
10294
10323
 
10295
- installImportMetaCssBuild(import.meta);const css$3 = /* css */`.navi_constraint_feedback_line {
10324
+ installImportMetaCssBuild(import.meta);
10325
+
10326
+ const css$3 = /* css */ `.navi_constraint_feedback_line {
10296
10327
  z-index: 9998;
10297
10328
  visibility: hidden;
10298
10329
  transform-origin: 0;
@@ -10306,6 +10337,7 @@ installImportMetaCssBuild(import.meta);const css$3 = /* css */`.navi_constraint_
10306
10337
  visibility: visible;
10307
10338
  }
10308
10339
  `;
10340
+
10309
10341
  const setupConstraintFeedbackLine = () => {
10310
10342
  import.meta.css = [css$3, "@jsenv/dom/src/interaction/drag/constraint_feedback_line.js"];
10311
10343
  const constraintFeedbackLine = createConstraintFeedbackLine();
@@ -10315,17 +10347,18 @@ const setupConstraintFeedbackLine = () => {
10315
10347
  let lastMouseY = null;
10316
10348
 
10317
10349
  // Internal function to update constraint feedback line
10318
- const onDrag = gestureInfo => {
10319
- const {
10320
- grabEvent,
10321
- dragEvent
10322
- } = gestureInfo;
10323
- if (grabEvent.type === "programmatic" ||
10324
- // dragEvent can be null when only mousedown without yet any mousemove
10325
- !dragEvent || dragEvent.type === "programmatic") {
10350
+ const onDrag = (gestureInfo) => {
10351
+ const { grabEvent, dragEvent } = gestureInfo;
10352
+ if (
10353
+ grabEvent.type === "programmatic" ||
10354
+ // dragEvent can be null when only mousedown without yet any mousemove
10355
+ !dragEvent ||
10356
+ dragEvent.type === "programmatic"
10357
+ ) {
10326
10358
  // programmatic drag
10327
10359
  return;
10328
10360
  }
10361
+
10329
10362
  const mouseX = dragEvent.clientX;
10330
10363
  const mouseY = dragEvent.clientY;
10331
10364
  // Use last known position if current position not available (e.g., during scroll)
@@ -10338,6 +10371,7 @@ const setupConstraintFeedbackLine = () => {
10338
10371
  // Store current mouse position for potential use during scroll
10339
10372
  lastMouseX = mouseX;
10340
10373
  lastMouseY = mouseY;
10374
+
10341
10375
  const grabClientX = grabEvent.clientX;
10342
10376
  const grabClientY = grabEvent.clientY;
10343
10377
 
@@ -10366,28 +10400,33 @@ const setupConstraintFeedbackLine = () => {
10366
10400
  constraintFeedbackLine.style.opacity = `${maxOpacity * opacityFactor}`;
10367
10401
  constraintFeedbackLine.setAttribute("data-visible", "");
10368
10402
  };
10403
+
10369
10404
  return {
10370
10405
  onDrag,
10371
10406
  onRelease: () => {
10372
10407
  constraintFeedbackLine.remove();
10373
- }
10408
+ },
10374
10409
  };
10375
10410
  };
10411
+
10376
10412
  const createConstraintFeedbackLine = () => {
10377
10413
  const line = document.createElement("div");
10378
10414
  line.className = "navi_constraint_feedback_line";
10379
- line.title = "Constraint feedback - shows distance between mouse and moving grab point";
10415
+ line.title =
10416
+ "Constraint feedback - shows distance between mouse and moving grab point";
10380
10417
  document.body.appendChild(line);
10381
10418
  return line;
10382
10419
  };
10383
10420
 
10384
- installImportMetaCssBuild(import.meta);// Keep visual markers (debug markers, obstacle markers, constraint feedback line) in DOM after drag ends
10421
+ installImportMetaCssBuild(import.meta);
10385
10422
  const MARKER_SIZE = 12;
10423
+
10386
10424
  let currentDebugMarkers = [];
10387
10425
  let currentConstraintMarkers = [];
10388
10426
  let currentReferenceElementMarker = null;
10389
10427
  let currentElementMarker = null;
10390
- const css$2 = /* css */`.navi_debug_markers_container {
10428
+
10429
+ const css$2 = /* css */ `.navi_debug_markers_container {
10391
10430
  z-index: 999998;
10392
10431
  pointer-events: none;
10393
10432
  --marker-size: ${MARKER_SIZE}px;
@@ -10554,9 +10593,8 @@ const css$2 = /* css */`.navi_debug_markers_container {
10554
10593
  left: 0;
10555
10594
  }
10556
10595
  `;
10557
- const setupDragDebugMarkers = (dragGesture, {
10558
- referenceElement
10559
- }) => {
10596
+
10597
+ const setupDragDebugMarkers = (dragGesture, { referenceElement }) => {
10560
10598
  import.meta.css = [css$2, "@jsenv/dom/src/interaction/drag/drag_debug_markers.js"];
10561
10599
 
10562
10600
  // Clean up any existing persistent markers from previous drag gestures
@@ -10567,27 +10605,29 @@ const setupDragDebugMarkers = (dragGesture, {
10567
10605
  container.innerHTML = ""; // Clear all markers efficiently
10568
10606
  }
10569
10607
  }
10570
- const {
10571
- direction,
10572
- scrollContainer
10573
- } = dragGesture.gestureInfo;
10608
+
10609
+ const { direction, scrollContainer } = dragGesture.gestureInfo;
10610
+
10574
10611
  return {
10575
- onConstraints: (constraints, {
10576
- left,
10577
- top,
10578
- right,
10579
- bottom,
10580
- autoScrollArea
10581
- }) => {
10612
+ onConstraints: (
10613
+ constraints,
10614
+ { left, top, right, bottom, autoScrollArea },
10615
+ ) => {
10582
10616
  // Schedule removal of previous markers if they exist
10583
10617
  const previousDebugMarkers = [...currentDebugMarkers];
10584
10618
  const previousConstraintMarkers = [...currentConstraintMarkers];
10585
10619
  const previousReferenceElementMarker = currentReferenceElementMarker;
10586
10620
  const previousElementMarker = currentElementMarker;
10587
- if (previousDebugMarkers.length > 0 || previousConstraintMarkers.length > 0 || previousReferenceElementMarker || previousElementMarker) {
10621
+
10622
+ if (
10623
+ previousDebugMarkers.length > 0 ||
10624
+ previousConstraintMarkers.length > 0 ||
10625
+ previousReferenceElementMarker ||
10626
+ previousElementMarker
10627
+ ) {
10588
10628
  setTimeout(() => {
10589
- previousDebugMarkers.forEach(marker => marker.remove());
10590
- previousConstraintMarkers.forEach(marker => marker.remove());
10629
+ previousDebugMarkers.forEach((marker) => marker.remove());
10630
+ previousConstraintMarkers.forEach((marker) => marker.remove());
10591
10631
  if (previousReferenceElementMarker) {
10592
10632
  previousReferenceElementMarker.remove();
10593
10633
  }
@@ -10616,7 +10656,7 @@ const setupDragDebugMarkers = (dragGesture, {
10616
10656
  bottom,
10617
10657
  scrollContainer,
10618
10658
  label: elementLabel,
10619
- color: elementColor
10659
+ color: elementColor,
10620
10660
  });
10621
10661
 
10622
10662
  // Create reference element marker if reference element exists
@@ -10626,47 +10666,52 @@ const setupDragDebugMarkers = (dragGesture, {
10626
10666
  top,
10627
10667
  right,
10628
10668
  bottom,
10629
- scrollContainer
10669
+ scrollContainer,
10630
10670
  });
10631
10671
  }
10632
10672
 
10633
10673
  // Collect all markers to be created, then merge duplicates
10634
10674
  const markersToCreate = [];
10675
+
10635
10676
  {
10636
10677
  if (direction.x) {
10637
10678
  markersToCreate.push({
10638
- name: autoScrollArea.paddingLeft ? `autoscroll.left + padding(${autoScrollArea.paddingLeft})` : "autoscroll.left",
10679
+ name: autoScrollArea.paddingLeft
10680
+ ? `autoscroll.left + padding(${autoScrollArea.paddingLeft})`
10681
+ : "autoscroll.left",
10639
10682
  x: autoScrollArea.left,
10640
10683
  y: 0,
10641
- color: "0 128 0",
10642
- // green
10643
- side: "left"
10684
+ color: "0 128 0", // green
10685
+ side: "left",
10644
10686
  });
10645
10687
  markersToCreate.push({
10646
- name: autoScrollArea.paddingRight ? `autoscroll.right + padding(${autoScrollArea.paddingRight})` : "autoscroll.right",
10688
+ name: autoScrollArea.paddingRight
10689
+ ? `autoscroll.right + padding(${autoScrollArea.paddingRight})`
10690
+ : "autoscroll.right",
10647
10691
  x: autoScrollArea.right,
10648
10692
  y: 0,
10649
- color: "0 128 0",
10650
- // green
10651
- side: "right"
10693
+ color: "0 128 0", // green
10694
+ side: "right",
10652
10695
  });
10653
10696
  }
10654
10697
  if (direction.y) {
10655
10698
  markersToCreate.push({
10656
- name: autoScrollArea.paddingTop ? `autoscroll.top + padding(${autoScrollArea.paddingTop})` : "autoscroll.top",
10699
+ name: autoScrollArea.paddingTop
10700
+ ? `autoscroll.top + padding(${autoScrollArea.paddingTop})`
10701
+ : "autoscroll.top",
10657
10702
  x: 0,
10658
10703
  y: autoScrollArea.top,
10659
- color: "255 0 0",
10660
- // red
10661
- side: "top"
10704
+ color: "255 0 0", // red
10705
+ side: "top",
10662
10706
  });
10663
10707
  markersToCreate.push({
10664
- name: autoScrollArea.paddingBottom ? `autoscroll.bottom + padding(${autoScrollArea.paddingBottom})` : "autoscroll.bottom",
10708
+ name: autoScrollArea.paddingBottom
10709
+ ? `autoscroll.bottom + padding(${autoScrollArea.paddingBottom})`
10710
+ : "autoscroll.bottom",
10665
10711
  x: 0,
10666
10712
  y: autoScrollArea.bottom,
10667
- color: "255 165 0",
10668
- // orange
10669
- side: "bottom"
10713
+ color: "255 165 0", // orange
10714
+ side: "bottom",
10670
10715
  });
10671
10716
  }
10672
10717
  }
@@ -10674,9 +10719,7 @@ const setupDragDebugMarkers = (dragGesture, {
10674
10719
  // Process each constraint individually to preserve names
10675
10720
  for (const constraint of constraints) {
10676
10721
  if (constraint.type === "bounds") {
10677
- const {
10678
- bounds
10679
- } = constraint;
10722
+ const { bounds } = constraint;
10680
10723
 
10681
10724
  // Create individual markers for each bound with constraint name
10682
10725
  if (direction.x) {
@@ -10685,9 +10728,8 @@ const setupDragDebugMarkers = (dragGesture, {
10685
10728
  name: `${constraint.name}.left`,
10686
10729
  x: bounds.left,
10687
10730
  y: 0,
10688
- color: "128 0 128",
10689
- // purple
10690
- side: "left"
10731
+ color: "128 0 128", // purple
10732
+ side: "left",
10691
10733
  });
10692
10734
  }
10693
10735
  if (bounds.right !== undefined) {
@@ -10697,9 +10739,8 @@ const setupDragDebugMarkers = (dragGesture, {
10697
10739
  name: `${constraint.name}.right`,
10698
10740
  x: bounds.right,
10699
10741
  y: 0,
10700
- color: "128 0 128",
10701
- // purple
10702
- side: "right"
10742
+ color: "128 0 128", // purple
10743
+ side: "right",
10703
10744
  });
10704
10745
  }
10705
10746
  }
@@ -10709,9 +10750,8 @@ const setupDragDebugMarkers = (dragGesture, {
10709
10750
  name: `${constraint.name}.top`,
10710
10751
  x: 0,
10711
10752
  y: bounds.top,
10712
- color: "128 0 128",
10713
- // purple
10714
- side: "top"
10753
+ color: "128 0 128", // purple
10754
+ side: "top",
10715
10755
  });
10716
10756
  }
10717
10757
  if (bounds.bottom !== undefined) {
@@ -10721,28 +10761,37 @@ const setupDragDebugMarkers = (dragGesture, {
10721
10761
  name: `${constraint.name}.bottom`,
10722
10762
  x: 0,
10723
10763
  y: bounds.bottom,
10724
- color: "128 0 128",
10725
- // purple
10726
- side: "bottom"
10764
+ color: "128 0 128", // purple
10765
+ side: "bottom",
10727
10766
  });
10728
10767
  }
10729
10768
  }
10730
10769
  } else if (constraint.type === "obstacle") {
10731
- const obstacleMarker = createObstacleMarker(constraint, scrollContainer);
10770
+ const obstacleMarker = createObstacleMarker(
10771
+ constraint,
10772
+ scrollContainer,
10773
+ );
10732
10774
  currentConstraintMarkers.push(obstacleMarker);
10733
10775
  }
10734
10776
  }
10735
10777
 
10736
10778
  // Create markers with merging for overlapping positions
10737
- const createdMarkers = createMergedMarkers(markersToCreate, scrollContainer);
10738
- currentDebugMarkers.push(...createdMarkers.filter(m => m.type !== "constraint"));
10739
- currentConstraintMarkers.push(...createdMarkers.filter(m => m.type === "constraint"));
10779
+ const createdMarkers = createMergedMarkers(
10780
+ markersToCreate,
10781
+ scrollContainer,
10782
+ );
10783
+ currentDebugMarkers.push(
10784
+ ...createdMarkers.filter((m) => m.type !== "constraint"),
10785
+ );
10786
+ currentConstraintMarkers.push(
10787
+ ...createdMarkers.filter((m) => m.type === "constraint"),
10788
+ );
10740
10789
  },
10741
10790
  onRelease: () => {
10742
10791
  {
10743
10792
  return;
10744
10793
  }
10745
- }
10794
+ },
10746
10795
  };
10747
10796
  };
10748
10797
 
@@ -10761,9 +10810,8 @@ const getMarkersContainer = () => {
10761
10810
  // Convert document-relative coordinates to viewport coordinates for marker positioning
10762
10811
  // Takes the scroll container into account for proper positioning relative to the container
10763
10812
  const getDebugMarkerPos = (x, y, scrollContainer, side = null) => {
10764
- const {
10765
- documentElement
10766
- } = document;
10813
+ const { documentElement } = document;
10814
+
10767
10815
  const leftWithoutScroll = x - scrollContainer.scrollLeft;
10768
10816
  const topWithoutScroll = y - scrollContainer.scrollTop;
10769
10817
  let baseX;
@@ -10795,6 +10843,7 @@ const getDebugMarkerPos = (x, y, scrollContainer, side = null) => {
10795
10843
  // For obstacles and other markers: use converted coordinates directly
10796
10844
  return [baseX, baseY];
10797
10845
  };
10846
+
10798
10847
  const createMergedMarkers = (markersToCreate, scrollContainer) => {
10799
10848
  const mergedMarkers = [];
10800
10849
  const positionMap = new Map();
@@ -10802,6 +10851,7 @@ const createMergedMarkers = (markersToCreate, scrollContainer) => {
10802
10851
  // Group markers by position and side
10803
10852
  for (const marker of markersToCreate) {
10804
10853
  const key = `${marker.x},${marker.y},${marker.side}`;
10854
+
10805
10855
  if (!positionMap.has(key)) {
10806
10856
  positionMap.set(key, []);
10807
10857
  }
@@ -10819,36 +10869,43 @@ const createMergedMarkers = (markersToCreate, scrollContainer) => {
10819
10869
  } else {
10820
10870
  // Multiple markers at same position - merge labels
10821
10871
  const firstMarker = markers[0];
10822
- const combinedName = markers.map(m => m.name).join(" + ");
10872
+ const combinedName = markers.map((m) => m.name).join(" + ");
10823
10873
 
10824
10874
  // Use the first marker's color, or mix colors if needed
10825
- const domMarker = createDebugMarker({
10826
- ...firstMarker,
10827
- name: combinedName
10828
- }, scrollContainer);
10829
- domMarker.type = markers.some(m => m.name.includes("Bound")) ? "constraint" : "visible";
10875
+ const domMarker = createDebugMarker(
10876
+ {
10877
+ ...firstMarker,
10878
+ name: combinedName,
10879
+ },
10880
+ scrollContainer,
10881
+ );
10882
+ domMarker.type = markers.some((m) => m.name.includes("Bound"))
10883
+ ? "constraint"
10884
+ : "visible";
10830
10885
  mergedMarkers.push(domMarker);
10831
10886
  }
10832
10887
  }
10888
+
10833
10889
  return mergedMarkers;
10834
10890
  };
10835
- const createDebugMarker = ({
10836
- name,
10837
- x,
10838
- y,
10839
- color = "255 0 0",
10840
- side
10841
- }, scrollContainer) => {
10891
+
10892
+ const createDebugMarker = (
10893
+ { name, x, y, color = "255 0 0", side },
10894
+ scrollContainer,
10895
+ ) => {
10842
10896
  // Convert coordinates from document-relative to viewport
10843
10897
  const [viewportX, viewportY] = getDebugMarkerPos(x, y, scrollContainer, side);
10898
+
10844
10899
  const marker = document.createElement("div");
10845
10900
  marker.className = `navi_debug_marker`;
10846
10901
  marker.setAttribute(`data-${side}`, "");
10847
10902
  // Set the color as a CSS custom property
10848
10903
  marker.style.setProperty("--marker-color", `rgb(${color})`);
10849
10904
  // Position markers exactly at the boundary coordinates
10850
- marker.style.left = side === "right" ? `${viewportX - MARKER_SIZE}px` : `${viewportX}px`;
10851
- marker.style.top = side === "bottom" ? `${viewportY - MARKER_SIZE}px` : `${viewportY}px`;
10905
+ marker.style.left =
10906
+ side === "right" ? `${viewportX - MARKER_SIZE}px` : `${viewportX}px`;
10907
+ marker.style.top =
10908
+ side === "bottom" ? `${viewportY - MARKER_SIZE}px` : `${viewportY}px`;
10852
10909
  marker.title = name;
10853
10910
 
10854
10911
  // Add label
@@ -10856,6 +10913,7 @@ const createDebugMarker = ({
10856
10913
  label.className = `navi_debug_marker_label`;
10857
10914
  label.textContent = name;
10858
10915
  marker.appendChild(label);
10916
+
10859
10917
  const container = getMarkersContainer();
10860
10918
  container.appendChild(marker);
10861
10919
  return marker;
@@ -10865,7 +10923,13 @@ const createObstacleMarker = (obstacleObj, scrollContainer) => {
10865
10923
  const height = obstacleObj.bounds.bottom - obstacleObj.bounds.top;
10866
10924
 
10867
10925
  // Convert document-relative coordinates to viewport coordinates
10868
- const [x, y] = getDebugMarkerPos(obstacleObj.bounds.left, obstacleObj.bounds.top, scrollContainer, "obstacle");
10926
+ const [x, y] = getDebugMarkerPos(
10927
+ obstacleObj.bounds.left,
10928
+ obstacleObj.bounds.top,
10929
+ scrollContainer,
10930
+ "obstacle",
10931
+ );
10932
+
10869
10933
  const marker = document.createElement("div");
10870
10934
  marker.className = "navi_obstacle_marker";
10871
10935
  marker.style.left = `${x}px`;
@@ -10879,10 +10943,12 @@ const createObstacleMarker = (obstacleObj, scrollContainer) => {
10879
10943
  label.className = "navi_obstacle_marker_label";
10880
10944
  label.textContent = obstacleObj.name;
10881
10945
  marker.appendChild(label);
10946
+
10882
10947
  const container = getMarkersContainer();
10883
10948
  container.appendChild(marker);
10884
10949
  return marker;
10885
10950
  };
10951
+
10886
10952
  const createElementMarker = ({
10887
10953
  left,
10888
10954
  top,
@@ -10890,12 +10956,13 @@ const createElementMarker = ({
10890
10956
  bottom,
10891
10957
  scrollContainer,
10892
10958
  label = "Element",
10893
- color = "0, 200, 0" // Default green color
10959
+ color = "0, 200, 0", // Default green color
10894
10960
  }) => {
10895
10961
  const width = right - left;
10896
10962
  const height = bottom - top;
10897
10963
  // Convert document-relative coordinates to viewport coordinates
10898
10964
  const [x, y] = getDebugMarkerPos(left, top, scrollContainer, "element");
10965
+
10899
10966
  const marker = document.createElement("div");
10900
10967
  marker.className = "navi_element_marker";
10901
10968
  marker.style.left = `${x}px`;
@@ -10913,21 +10980,24 @@ const createElementMarker = ({
10913
10980
  labelEl.className = "navi_element_marker_label";
10914
10981
  labelEl.textContent = label;
10915
10982
  marker.appendChild(labelEl);
10983
+
10916
10984
  const container = getMarkersContainer();
10917
10985
  container.appendChild(marker);
10918
10986
  return marker;
10919
10987
  };
10988
+
10920
10989
  const createReferenceElementMarker = ({
10921
10990
  left,
10922
10991
  top,
10923
10992
  right,
10924
10993
  bottom,
10925
- scrollContainer
10994
+ scrollContainer,
10926
10995
  }) => {
10927
10996
  const width = right - left;
10928
10997
  const height = bottom - top;
10929
10998
  // Convert document-relative coordinates to viewport coordinates
10930
10999
  const [x, y] = getDebugMarkerPos(left, top, scrollContainer, "reference");
11000
+
10931
11001
  const marker = document.createElement("div");
10932
11002
  marker.className = "navi_reference_element_marker";
10933
11003
  marker.style.left = `${x}px`;
@@ -10941,6 +11011,7 @@ const createReferenceElementMarker = ({
10941
11011
  label.className = "navi_reference_element_marker_label";
10942
11012
  label.textContent = "Reference Element";
10943
11013
  marker.appendChild(label);
11014
+
10944
11015
  const container = getMarkersContainer();
10945
11016
  container.appendChild(marker);
10946
11017
  return marker;
@@ -11847,39 +11918,8 @@ const createStickyFrontierOnAxis = (
11847
11918
  return matchingStickyFrontiers;
11848
11919
  };
11849
11920
 
11850
- installImportMetaCssBuild(import.meta);/**
11851
- * A drag, and what it is FOR.
11852
- *
11853
- * What a hand does is always the same — pick the thing up, carry it, let go — so
11854
- * the gesture is not what distinguishes these. What distinguishes them is the
11855
- * outcome the caller asked for, and that is what `startDragTo` takes:
11856
- *
11857
- * - **move**: it stays where it was put. The element ITSELF travels and keeps the
11858
- * place the hand gave it.
11859
- * - **reorder**: it takes a place in a list. A COPY travels while the original
11860
- * keeps its place in the layout, which is what makes the gesture possible at
11861
- * all — nothing else moves while the hand looks for a place, so there is a
11862
- * stable row of items to look between.
11863
- * - **toss**: it is gotten rid of. The same copy, for the opposite reason: the
11864
- * original stays until the answer says it is really gone.
11865
- * - **land**: it comes down ON something. Also a copy, and the closest to
11866
- * `reorder` — the difference is what a target IS: a row of a list is a place
11867
- * BETWEEN two others, whereas a square of a board is a place of its own, which
11868
- * may already be taken. So nothing is inserted and nothing is a no-op: the
11869
- * answer is "this one came down on that one", and what that means (take the
11870
- * place, swap the two, refuse) is the caller's.
11871
- *
11872
- * The caller lists which outcomes ITS element can answer, and only the machinery
11873
- * those need runs: no copy for a move, no drop hint for something that can only be
11874
- * thrown away, no landing looked for where nothing lands. `reorder` and `toss`
11875
- * combine (dropped on a row, or thrown off the screen); `move` and `reorder` cannot
11876
- * both be true of one release, and the caller is the one who must not ask for both.
11877
- *
11878
- * `createDragToMoveGestureController` below is the layer under all of that — the
11879
- * translation, the auto-scroll, the constraints — and stays usable on its own for
11880
- * anything that is none of the three (a table column being dragged, a sticky
11881
- * frontier being moved).
11882
- */
11921
+ installImportMetaCssBuild(import.meta);
11922
+
11883
11923
  const dragStyleController = createStyleController("drag_to_move");
11884
11924
 
11885
11925
  // How long the copy takes to leave the screen, and to come back. Written into the
@@ -11888,7 +11928,8 @@ const dragStyleController = createStyleController("drag_to_move");
11888
11928
  const TOSS_DURATION_MS = 320;
11889
11929
  // Far enough to be off any screen, in the direction the hand was going.
11890
11930
  const TOSS_DISTANCE = 900;
11891
- const css$1 = /* css */`.navi_drop_hint {
11931
+
11932
+ const css$1 = /* css */ `.navi_drop_hint {
11892
11933
  inset: auto;
11893
11934
  top: var(--drop-hint-y);
11894
11935
  left: calc(var(--drop-target-left) + var(--drop-hint-margin-x, 0px));
@@ -12052,7 +12093,8 @@ import.meta.css = [css$1, "@jsenv/dom/src/interaction/drag/drag_to.js"];
12052
12093
  // stays a descendant of whatever it is anchored in (a callout next to a button,
12053
12094
  // in the card that carries both), and the press bubbles through the surface as
12054
12095
  // if it had landed on it.
12055
- const DRAG_IGNORED_SELECTOR = '[data-drag-ignore],[data-self-interactions~="drag"],[data-self-interactions~="*"],[popover],dialog';
12096
+ const DRAG_IGNORED_SELECTOR =
12097
+ '[data-drag-ignore],[data-self-interactions~="drag"],[data-self-interactions~="*"],[popover],dialog';
12056
12098
 
12057
12099
  /**
12058
12100
  * Starts a drag-to-reorder interaction on a list item.
@@ -12213,13 +12255,12 @@ const createDragToMoveGestureController = ({
12213
12255
  cancelAnimationEasing = "ease-out",
12214
12256
  ...options
12215
12257
  } = {}) => {
12216
- const initGrabToMoveElement = (dragGesture, {
12217
- element,
12218
- referenceElement,
12219
- elementToMove,
12220
- convertScrollablePosition
12221
- }) => {
12258
+ const initGrabToMoveElement = (
12259
+ dragGesture,
12260
+ { element, referenceElement, elementToMove, convertScrollablePosition },
12261
+ ) => {
12222
12262
  const scrollContainer = dragGesture.gestureInfo.scrollContainer;
12263
+
12223
12264
  const direction = dragGesture.gestureInfo.direction;
12224
12265
  // elementImpacted is either an externally provided elementToMove (e.g. a drag clone)
12225
12266
  const elementImpacted = elementToMove || element;
@@ -12227,9 +12268,13 @@ const createDragToMoveGestureController = ({
12227
12268
  // (e.g. a drag clone passed by the caller) or the element itself.
12228
12269
  // Capture any pre-existing translate so we can accumulate on top of it
12229
12270
  // rather than resetting it to zero on the first drag event.
12230
- const transformAtGrab = dragStyleController.getUnderlyingValue(elementImpacted, "transform");
12271
+ const transformAtGrab = dragStyleController.getUnderlyingValue(
12272
+ elementImpacted,
12273
+ "transform",
12274
+ );
12231
12275
  const translateXAtGrab = transformAtGrab.translateX;
12232
12276
  const translateYAtGrab = transformAtGrab.translateY;
12277
+
12233
12278
  const cancelPosition = () => {
12234
12279
  dragStyleController.clear(elementImpacted);
12235
12280
  };
@@ -12239,7 +12284,7 @@ const createDragToMoveGestureController = ({
12239
12284
  // animation has only to bridge the two.
12240
12285
  const cancelPositionAnimated = ({
12241
12286
  duration = cancelAnimationDuration,
12242
- easing = cancelAnimationEasing
12287
+ easing = cancelAnimationEasing,
12243
12288
  } = {}) => {
12244
12289
  const transformWhileHeld = getComputedStyle(elementImpacted).transform;
12245
12290
  cancelPosition();
@@ -12249,14 +12294,10 @@ const createDragToMoveGestureController = ({
12249
12294
  }
12250
12295
  // No fill: the element already sits at its resting transform, the
12251
12296
  // animation only replays the way back to it.
12252
- return elementImpacted.animate([{
12253
- transform: transformWhileHeld
12254
- }, {
12255
- transform: transformAtRest
12256
- }], {
12257
- duration,
12258
- easing
12259
- });
12297
+ return elementImpacted.animate(
12298
+ [{ transform: transformWhileHeld }, { transform: transformAtRest }],
12299
+ { duration, easing },
12300
+ );
12260
12301
  };
12261
12302
  const commitPosition = () => {
12262
12303
  dragStyleController.commit(elementImpacted);
@@ -12264,6 +12305,7 @@ const createDragToMoveGestureController = ({
12264
12305
  dragGesture.gestureInfo.cancelPosition = cancelPosition;
12265
12306
  dragGesture.gestureInfo.cancelPositionAnimated = cancelPositionAnimated;
12266
12307
  dragGesture.gestureInfo.commitPosition = commitPosition;
12308
+
12267
12309
  dragGesture.addReleaseCallback(() => {
12268
12310
  if (releasePositionEffect === "cancel") {
12269
12311
  cancelPosition();
@@ -12274,6 +12316,7 @@ const createDragToMoveGestureController = ({
12274
12316
  }
12275
12317
  // "manual": caller handles cleanup, do nothing.
12276
12318
  });
12319
+
12277
12320
  let elementWidth;
12278
12321
  let elementHeight;
12279
12322
  {
@@ -12285,6 +12328,7 @@ const createDragToMoveGestureController = ({
12285
12328
  updateElementDimension();
12286
12329
  dragGesture.addBeforeDragCallback(updateElementDimension);
12287
12330
  }
12331
+
12288
12332
  let scrollArea;
12289
12333
  {
12290
12334
  // Snapshot at grab time so that DOM mutations during dragging
@@ -12293,9 +12337,10 @@ const createDragToMoveGestureController = ({
12293
12337
  left: 0,
12294
12338
  top: 0,
12295
12339
  right: scrollContainer.scrollWidth,
12296
- bottom: scrollContainer.scrollHeight
12340
+ bottom: scrollContainer.scrollHeight,
12297
12341
  };
12298
12342
  }
12343
+
12299
12344
  let scrollport;
12300
12345
  let autoScrollArea;
12301
12346
  {
@@ -12306,11 +12351,14 @@ const createDragToMoveGestureController = ({
12306
12351
  scrollport = getScrollport(scrollBox, scrollContainer);
12307
12352
  autoScrollArea = scrollport;
12308
12353
  if (stickyFrontiers) {
12309
- autoScrollArea = applyStickyFrontiersToAutoScrollArea(autoScrollArea, {
12310
- scrollContainer,
12311
- direction
12312
- // dragGestureName,
12313
- });
12354
+ autoScrollArea = applyStickyFrontiersToAutoScrollArea(
12355
+ autoScrollArea,
12356
+ {
12357
+ scrollContainer,
12358
+ direction,
12359
+ // dragGestureName,
12360
+ },
12361
+ );
12314
12362
  }
12315
12363
  if (autoScrollAreaPadding > 0) {
12316
12364
  autoScrollArea = {
@@ -12321,7 +12369,7 @@ const createDragToMoveGestureController = ({
12321
12369
  left: autoScrollArea.left + autoScrollAreaPadding,
12322
12370
  top: autoScrollArea.top + autoScrollAreaPadding,
12323
12371
  right: autoScrollArea.right - autoScrollAreaPadding,
12324
- bottom: autoScrollArea.bottom - autoScrollAreaPadding
12372
+ bottom: autoScrollArea.bottom - autoScrollAreaPadding,
12325
12373
  };
12326
12374
  }
12327
12375
  };
@@ -12344,77 +12392,97 @@ const createDragToMoveGestureController = ({
12344
12392
  obstacleAttributeName,
12345
12393
  showConstraintFeedbackLine,
12346
12394
  showDebugMarkers,
12347
- referenceElement
12395
+ referenceElement,
12348
12396
  });
12349
- dragGesture.addBeforeDragCallback((layoutRequested, currentLayout, limitLayout, {
12350
- dragEvent
12351
- }) => {
12352
- dragConstraints.applyConstraints(layoutRequested, currentLayout, limitLayout, {
12353
- elementWidth,
12354
- elementHeight,
12355
- scrollArea,
12356
- scrollport,
12357
- hasCrossedScrollportLeftOnce,
12358
- hasCrossedScrollportTopOnce,
12359
- autoScrollArea,
12360
- dragEvent
12361
- });
12362
- });
12363
- const dragToMove = gestureInfo => {
12364
- const {
12365
- isGoingDown,
12366
- isGoingUp,
12367
- isGoingLeft,
12368
- isGoingRight,
12369
- layout
12370
- } = gestureInfo;
12397
+ dragGesture.addBeforeDragCallback(
12398
+ (layoutRequested, currentLayout, limitLayout, { dragEvent }) => {
12399
+ dragConstraints.applyConstraints(
12400
+ layoutRequested,
12401
+ currentLayout,
12402
+ limitLayout,
12403
+ {
12404
+ elementWidth,
12405
+ elementHeight,
12406
+ scrollArea,
12407
+ scrollport,
12408
+ hasCrossedScrollportLeftOnce,
12409
+ hasCrossedScrollportTopOnce,
12410
+ autoScrollArea,
12411
+ dragEvent,
12412
+ },
12413
+ );
12414
+ },
12415
+ );
12416
+
12417
+ const dragToMove = (gestureInfo) => {
12418
+ const { isGoingDown, isGoingUp, isGoingLeft, isGoingRight, layout } =
12419
+ gestureInfo;
12371
12420
  const left = layout.left;
12372
12421
  const top = layout.top;
12373
12422
  const right = left + elementWidth;
12374
12423
  const bottom = top + elementHeight;
12424
+
12375
12425
  {
12376
- hasCrossedScrollportLeftOnce = hasCrossedScrollportLeftOnce || left < scrollport.left;
12377
- hasCrossedScrollportTopOnce = hasCrossedScrollportTopOnce || top < scrollport.top;
12378
- const getScrollMove = axis => {
12426
+ hasCrossedScrollportLeftOnce =
12427
+ hasCrossedScrollportLeftOnce || left < scrollport.left;
12428
+ hasCrossedScrollportTopOnce =
12429
+ hasCrossedScrollportTopOnce || top < scrollport.top;
12430
+
12431
+ const getScrollMove = (axis) => {
12379
12432
  const isGoingPositive = axis === "x" ? isGoingRight : isGoingDown;
12380
12433
  if (isGoingPositive) {
12381
12434
  const elementEnd = axis === "x" ? right : bottom;
12382
- const autoScrollAreaEnd = axis === "x" ? autoScrollArea.right : autoScrollArea.bottom;
12435
+ const autoScrollAreaEnd =
12436
+ axis === "x" ? autoScrollArea.right : autoScrollArea.bottom;
12437
+
12383
12438
  if (elementEnd <= autoScrollAreaEnd) {
12384
12439
  return 0;
12385
12440
  }
12386
12441
  const scrollAmountNeeded = elementEnd - autoScrollAreaEnd;
12387
12442
  return scrollAmountNeeded;
12388
12443
  }
12444
+
12389
12445
  const isGoingNegative = axis === "x" ? isGoingLeft : isGoingUp;
12390
12446
  if (!isGoingNegative) {
12391
12447
  return 0;
12392
12448
  }
12449
+
12393
12450
  const referenceOrEl = referenceElement || element;
12394
- const canAutoScrollNegative = axis === "x" ? !referenceOrEl.hasAttribute("data-sticky-left") || hasCrossedScrollportLeftOnce : !referenceOrEl.hasAttribute("data-sticky-top") || hasCrossedScrollportTopOnce;
12451
+ const canAutoScrollNegative =
12452
+ axis === "x"
12453
+ ? !referenceOrEl.hasAttribute("data-sticky-left") ||
12454
+ hasCrossedScrollportLeftOnce
12455
+ : !referenceOrEl.hasAttribute("data-sticky-top") ||
12456
+ hasCrossedScrollportTopOnce;
12395
12457
  if (!canAutoScrollNegative) {
12396
12458
  return 0;
12397
12459
  }
12460
+
12398
12461
  const elementStart = axis === "x" ? left : top;
12399
- const autoScrollAreaStart = axis === "x" ? autoScrollArea.left : autoScrollArea.top;
12462
+ const autoScrollAreaStart =
12463
+ axis === "x" ? autoScrollArea.left : autoScrollArea.top;
12400
12464
  if (elementStart >= autoScrollAreaStart) {
12401
12465
  return 0;
12402
12466
  }
12467
+
12403
12468
  const scrollAmountNeeded = autoScrollAreaStart - elementStart;
12404
12469
  return -scrollAmountNeeded;
12405
12470
  };
12471
+
12406
12472
  let scrollLeftTarget;
12407
12473
  let scrollTopTarget;
12408
12474
  if (direction.x) {
12409
12475
  const containerScrollLeftMove = getScrollMove("x");
12410
12476
  if (containerScrollLeftMove) {
12411
- scrollLeftTarget = scrollContainer.scrollLeft + containerScrollLeftMove;
12477
+ scrollLeftTarget =
12478
+ scrollContainer.scrollLeft + containerScrollLeftMove;
12412
12479
  }
12413
12480
  }
12414
12481
  if (direction.y) {
12415
12482
  const containerScrollTopMove = getScrollMove("y");
12416
12483
  if (containerScrollTopMove) {
12417
- scrollTopTarget = scrollContainer.scrollTop + containerScrollTopMove;
12484
+ scrollTopTarget =
12485
+ scrollContainer.scrollTop + containerScrollTopMove;
12418
12486
  }
12419
12487
  }
12420
12488
  // now we know what to do, do it
@@ -12425,12 +12493,13 @@ const createDragToMoveGestureController = ({
12425
12493
  scrollContainer.scrollTop = scrollTopTarget;
12426
12494
  }
12427
12495
  }
12496
+
12428
12497
  {
12429
- const {
12498
+ const { scrollableLeft, scrollableTop } = layout;
12499
+ const [positionedLeft, positionedTop] = convertScrollablePosition(
12430
12500
  scrollableLeft,
12431
- scrollableTop
12432
- } = layout;
12433
- const [positionedLeft, positionedTop] = convertScrollablePosition(scrollableLeft, scrollableTop);
12501
+ scrollableTop,
12502
+ );
12434
12503
  // Build the transform to apply, preserving any transforms that were
12435
12504
  // already on the element before the grab (e.g. rotate from another
12436
12505
  // controller), and accumulating from the pre-grab translate baseline.
@@ -12442,32 +12511,33 @@ const createDragToMoveGestureController = ({
12442
12511
  // the distance covered. Dragging moves things on screen, so its translate
12443
12512
  // has to come first, whatever else the element carries. The spread still
12444
12513
  // wins on the value when the element already had a translate of its own.
12445
- const transform = {
12446
- translateX: 0,
12447
- translateY: 0,
12448
- ...transformAtGrab
12449
- };
12514
+ const transform = { translateX: 0, translateY: 0, ...transformAtGrab };
12450
12515
  if (direction.x) {
12451
12516
  const leftTarget = positionedLeft;
12452
12517
  const leftAtGrab = dragGesture.gestureInfo.leftAtGrab;
12453
12518
  const leftDelta = leftTarget - leftAtGrab;
12454
- const translateX = translateXAtGrab ? translateXAtGrab + leftDelta : leftDelta;
12519
+ const translateX = translateXAtGrab
12520
+ ? translateXAtGrab + leftDelta
12521
+ : leftDelta;
12455
12522
  transform.translateX = translateX;
12456
12523
  }
12457
12524
  if (direction.y) {
12458
12525
  const topTarget = positionedTop;
12459
12526
  const topAtGrab = dragGesture.gestureInfo.topAtGrab;
12460
12527
  const topDelta = topTarget - topAtGrab;
12461
- const translateY = translateYAtGrab ? translateYAtGrab + topDelta : topDelta;
12528
+ const translateY = translateYAtGrab
12529
+ ? translateYAtGrab + topDelta
12530
+ : topDelta;
12462
12531
  transform.translateY = translateY;
12463
12532
  }
12464
12533
  dragStyleController.set(elementImpacted, {
12465
- transform
12534
+ transform,
12466
12535
  });
12467
12536
  }
12468
12537
  };
12469
12538
  dragGesture.addDragCallback(dragToMove);
12470
12539
  };
12540
+
12471
12541
  const dragGestureController = createDragGestureController(options);
12472
12542
  const grab = dragGestureController.grab;
12473
12543
  dragGestureController.grab = ({
@@ -12478,23 +12548,28 @@ const createDragToMoveGestureController = ({
12478
12548
  ...rest
12479
12549
  } = {}) => {
12480
12550
  const scrollContainer = getScrollContainer(referenceElement || element);
12481
- const [elementScrollableLeft, elementScrollableTop, convertScrollablePosition] = createDragElementPositioner(element, referenceElement, elementToMove);
12551
+ const [
12552
+ elementScrollableLeft,
12553
+ elementScrollableTop,
12554
+ convertScrollablePosition,
12555
+ ] = createDragElementPositioner(element, referenceElement, elementToMove);
12482
12556
  const dragGesture = grab({
12483
12557
  element,
12484
12558
  scrollContainer,
12485
12559
  layoutScrollableLeft: elementScrollableLeft,
12486
12560
  layoutScrollableTop: elementScrollableTop,
12487
12561
  event,
12488
- ...rest
12562
+ ...rest,
12489
12563
  });
12490
12564
  initGrabToMoveElement(dragGesture, {
12491
12565
  element,
12492
12566
  referenceElement,
12493
12567
  elementToMove,
12494
- convertScrollablePosition
12568
+ convertScrollablePosition,
12495
12569
  });
12496
12570
  return dragGesture;
12497
12571
  };
12572
+
12498
12573
  return dragGestureController;
12499
12574
  };
12500
12575
 
@@ -12547,10 +12622,11 @@ const createDragToMoveGestureController = ({
12547
12622
  * The gesture holds its copy until what `onReorder` returns settles, so returning
12548
12623
  * the transition is what makes the landing continuous.
12549
12624
  */
12550
- const startDragTo = (event, effects, {
12551
- draggedElement = event.currentTarget,
12552
- ...options
12553
- } = {}) => {
12625
+ const startDragTo = (
12626
+ event,
12627
+ effects,
12628
+ { draggedElement = event.currentTarget, ...options } = {},
12629
+ ) => {
12554
12630
  // An area that opted out of dragging (a text one wants to select, a control that
12555
12631
  // owns the gesture, a callout): the press there is none of our business.
12556
12632
  if (isPressIgnored(event.target, draggedElement)) {
@@ -12569,13 +12645,10 @@ const startDragTo = (event, effects, {
12569
12645
  canReorder,
12570
12646
  canToss,
12571
12647
  canLand,
12572
- ...options
12648
+ ...options,
12573
12649
  });
12574
12650
  }
12575
- return startDragToMoveElement(event, {
12576
- draggedElement,
12577
- ...options
12578
- });
12651
+ return startDragToMoveElement(event, { draggedElement, ...options });
12579
12652
  };
12580
12653
 
12581
12654
  /**
@@ -12584,70 +12657,71 @@ const startDragTo = (event, effects, {
12584
12657
  * No copy, unlike the two others: what is being moved is the thing and not a
12585
12658
  * stand-in for it, so there is nothing to put back and nothing to reveal.
12586
12659
  */
12587
- const startDragToMoveElement = (event, {
12588
- draggedElement,
12589
- onMove,
12590
- threshold,
12591
- longPress,
12592
- longPressDelay,
12593
- longPressSlop,
12594
- onPressStart,
12595
- onPressCancel,
12596
- onPress,
12597
- ...options
12598
- }) => {
12599
- event.preventDefault();
12600
- return dragAfterIntent(event, () => {
12601
- const gestureController = createDragToMoveGestureController({
12602
- releasePositionEffect: "manual",
12603
- ...options
12604
- });
12605
- const dragGesture = gestureController.grabViaPointer(event, {
12606
- element: draggedElement
12607
- });
12608
- if (!dragGesture) {
12609
- return null;
12610
- }
12611
- dragGesture.addReleaseCallback(async gestureInfo => {
12612
- const {
12613
- xDelta,
12614
- yDelta
12615
- } = gestureInfo.layout;
12616
- if (!xDelta && !yDelta) {
12617
- // Picked up and put back down: nothing moved, so nobody is told.
12618
- gestureInfo.cancelPosition();
12619
- return;
12620
- }
12621
- // Committed before the answer rather than after: the hand let go of it
12622
- // there, and a thing that snaps home while a request is in flight says the
12623
- // gesture was not understood.
12624
- gestureInfo.commitPosition();
12625
- try {
12626
- await onMove?.({
12627
- gestureInfo,
12628
- x: xDelta,
12629
- y: yDelta
12630
- });
12631
- } catch {
12632
- gestureInfo.cancelPositionAnimated();
12633
- }
12634
- });
12635
- return dragGesture;
12636
- }, {
12660
+ const startDragToMoveElement = (
12661
+ event,
12662
+ {
12663
+ draggedElement,
12664
+ onMove,
12637
12665
  threshold,
12638
12666
  longPress,
12639
12667
  longPressDelay,
12640
12668
  longPressSlop,
12641
12669
  onPressStart,
12642
12670
  onPressCancel,
12643
- onPress
12644
- });
12671
+ onPress,
12672
+ ...options
12673
+ },
12674
+ ) => {
12675
+ event.preventDefault();
12676
+ return dragAfterIntent(
12677
+ event,
12678
+ () => {
12679
+ const gestureController = createDragToMoveGestureController({
12680
+ releasePositionEffect: "manual",
12681
+ ...options,
12682
+ });
12683
+ const dragGesture = gestureController.grabViaPointer(event, {
12684
+ element: draggedElement,
12685
+ });
12686
+ if (!dragGesture) {
12687
+ return null;
12688
+ }
12689
+ dragGesture.addReleaseCallback(async (gestureInfo) => {
12690
+ const { xDelta, yDelta } = gestureInfo.layout;
12691
+ if (!xDelta && !yDelta) {
12692
+ // Picked up and put back down: nothing moved, so nobody is told.
12693
+ gestureInfo.cancelPosition();
12694
+ return;
12695
+ }
12696
+ // Committed before the answer rather than after: the hand let go of it
12697
+ // there, and a thing that snaps home while a request is in flight says the
12698
+ // gesture was not understood.
12699
+ gestureInfo.commitPosition();
12700
+ try {
12701
+ await onMove?.({ gestureInfo, x: xDelta, y: yDelta });
12702
+ } catch {
12703
+ gestureInfo.cancelPositionAnimated();
12704
+ }
12705
+ });
12706
+ return dragGesture;
12707
+ },
12708
+ {
12709
+ threshold,
12710
+ longPress,
12711
+ longPressDelay,
12712
+ longPressSlop,
12713
+ onPressStart,
12714
+ onPressCancel,
12715
+ onPress,
12716
+ },
12717
+ );
12645
12718
  };
12646
12719
 
12647
12720
  // Far and fast, both at once: one without the other is moving the thing while
12648
12721
  // hesitating, and nothing is thrown away on a hesitation — it comes back.
12649
12722
  const TOSS_DISTANCE_TO_COMMIT = 110;
12650
12723
  const TOSS_SPEED_TO_COMMIT = 0.45;
12724
+
12651
12725
  const resolveDropMeaning = ({
12652
12726
  gestureInfo,
12653
12727
  hasDropTarget,
@@ -12655,7 +12729,7 @@ const resolveDropMeaning = ({
12655
12729
  canToss,
12656
12730
  canLand,
12657
12731
  tossDistance = TOSS_DISTANCE_TO_COMMIT,
12658
- tossSpeed = TOSS_SPEED_TO_COMMIT
12732
+ tossSpeed = TOSS_SPEED_TO_COMMIT,
12659
12733
  }) => {
12660
12734
  if (gestureInfo.cancelled) {
12661
12735
  // Nobody let go of anything: the gesture was taken away mid-air (the
@@ -12664,10 +12738,7 @@ const resolveDropMeaning = ({
12664
12738
  return "cancel";
12665
12739
  }
12666
12740
  if (canToss) {
12667
- const {
12668
- xDelta,
12669
- yDelta
12670
- } = gestureInfo.layout;
12741
+ const { xDelta, yDelta } = gestureInfo.layout;
12671
12742
  const distance = Math.hypot(xDelta, yDelta);
12672
12743
  if (distance > tossDistance && gestureInfo.velocity > tossSpeed) {
12673
12744
  return "toss";
@@ -12692,47 +12763,44 @@ const resolveDropMeaning = ({
12692
12763
  * for the opposite reason: the original stays until the answer says it is really
12693
12764
  * gone.
12694
12765
  */
12695
- const startDragToCarryCopy = (event, {
12696
- draggedElement,
12697
- canReorder,
12698
- canToss,
12699
- canLand,
12700
- // Something that can be thrown away has to be able to LEAVE. The default of
12701
- // the layer below keeps what is dragged inside its scroll area, which is right
12702
- // for a reorder (a row belongs to its list) and makes a throw impossible — the
12703
- // copy hits the edge of the list and no distance is ever covered, so no throw
12704
- // ever happens and no sideways movement is even visible.
12705
- // Destructured with the default here rather than written at the call below: a
12706
- // caller passing `areaConstraint: undefined` (which is what saying nothing
12707
- // through an options object looks like) would otherwise put the layer below
12708
- // back on its own default and undo this.
12709
- areaConstraint = canToss ? "none" : undefined,
12710
- containerElement = draggedElement.parentElement,
12711
- itemSelector,
12712
- getItemId,
12713
- onReorder,
12714
- onLand,
12715
- onToss,
12716
- tossDistance,
12717
- tossSpeed,
12718
- // A list runs one way and reordering walks it; a board has places all around,
12719
- // so something landing on one of them goes wherever the hand takes it.
12720
- direction = canLand ? {
12721
- x: true,
12722
- y: true
12723
- } : {
12724
- x: false,
12725
- y: true
12766
+ const startDragToCarryCopy = (
12767
+ event,
12768
+ {
12769
+ draggedElement,
12770
+ canReorder,
12771
+ canToss,
12772
+ canLand,
12773
+ // Something that can be thrown away has to be able to LEAVE. The default of
12774
+ // the layer below keeps what is dragged inside its scroll area, which is right
12775
+ // for a reorder (a row belongs to its list) and makes a throw impossible — the
12776
+ // copy hits the edge of the list and no distance is ever covered, so no throw
12777
+ // ever happens and no sideways movement is even visible.
12778
+ // Destructured with the default here rather than written at the call below: a
12779
+ // caller passing `areaConstraint: undefined` (which is what saying nothing
12780
+ // through an options object looks like) would otherwise put the layer below
12781
+ // back on its own default and undo this.
12782
+ areaConstraint = canToss ? "none" : undefined,
12783
+ containerElement = draggedElement.parentElement,
12784
+ itemSelector,
12785
+ getItemId,
12786
+ onReorder,
12787
+ onLand,
12788
+ onToss,
12789
+ tossDistance,
12790
+ tossSpeed,
12791
+ // A list runs one way and reordering walks it; a board has places all around,
12792
+ // so something landing on one of them goes wherever the hand takes it.
12793
+ direction = canLand ? { x: true, y: true } : { x: false, y: true },
12794
+ threshold,
12795
+ longPress,
12796
+ longPressDelay,
12797
+ longPressSlop,
12798
+ onPressStart,
12799
+ onPressCancel,
12800
+ onPress,
12801
+ ...options
12726
12802
  },
12727
- threshold,
12728
- longPress,
12729
- longPressDelay,
12730
- longPressSlop,
12731
- onPressStart,
12732
- onPressCancel,
12733
- onPress,
12734
- ...options
12735
- }) => {
12803
+ ) => {
12736
12804
  // An area that opted out of dragging (a text one wants to select, a control
12737
12805
  // that owns the gesture, a callout): the press there is none of our business.
12738
12806
  if (isPressIgnored(event.target, draggedElement)) {
@@ -12748,243 +12816,310 @@ const startDragToCarryCopy = (event, {
12748
12816
  // had got to.
12749
12817
  const startCarry = (pointerEvent, cloneWrapperCaught, onCarryStart) => {
12750
12818
  pointerEvent.preventDefault();
12751
- return dragAfterIntent(pointerEvent, () => {
12752
- // Here and nowhere else is where a press has turned into a carry — the
12753
- // one moment both ways in (a finger held still, a mouse travelled)
12754
- // agree on.
12755
- onCarryStart?.();
12756
- const cloneWrapper = cloneWrapperCaught || createDragClone(draggedElement, pointerEvent);
12757
- if (cloneWrapperCaught) {
12758
- liftDragClone(cloneWrapperCaught, pointerEvent);
12759
- }
12760
- draggedElement.setAttribute("navi-drag-clone-source", "");
12761
- const gestureController = createDragToMoveGestureController({
12762
- direction,
12763
- releasePositionEffect: "manual",
12764
- areaConstraint,
12765
- ...options
12766
- });
12767
- const dragGesture = gestureController.grabViaPointer(pointerEvent, {
12768
- element: draggedElement,
12769
- elementToMove: cloneWrapper
12770
- });
12771
- // getDropTargetInfo uses gestureInfo.elementImpacted to compute the dragged rect.
12772
- // Point it at the clone so drop detection tracks the clone's current position.
12773
- dragGesture.gestureInfo.elementImpacted = cloneWrapper;
12774
-
12775
- // No place to land, no hint: an element that can only be thrown away has
12776
- // nowhere to be put. What the hint LOOKS like follows what a place is here —
12777
- // a line in the gap between two items, or the place itself lit up.
12778
- const dropHintEl = canLand ? createDropSurface() : canReorder ? createDropHint() : null;
12779
- if (dropHintEl) {
12780
- // In the container it draws into, which is where its own vars are set: the
12781
- // shape of a drop hint is a property of the list or board it belongs to,
12782
- // and reading it from there is inheritance rather than a hand-off.
12783
- draggedElement.parentElement.appendChild(dropHintEl);
12784
- }
12785
- // The hint first, the clone second: that order is what stacks them in the
12786
- // top layer. A copy taken back in hand is already up there, and the hint
12787
- // just went above it — shown again it returns to the top, where what the
12788
- // hand carries belongs.
12789
- dropHintEl?.showPopover();
12790
- if (cloneWrapperCaught) {
12791
- cloneWrapper.hidePopover();
12792
- }
12793
- cloneWrapper.showPopover();
12794
-
12795
- // currentBeforeElement: element before which the grabbed item will be inserted (null = end)
12796
- // currentReleaseElement: the actual hovered drop target — used to snap the clone on release
12797
- let currentBeforeElement;
12798
- let currentReleaseElement;
12799
- const clearDropHintDOM = () => {
12800
- if (!dropHintEl) {
12801
- return;
12819
+ return dragAfterIntent(
12820
+ pointerEvent,
12821
+ () => {
12822
+ // Here and nowhere else is where a press has turned into a carry — the
12823
+ // one moment both ways in (a finger held still, a mouse travelled)
12824
+ // agree on.
12825
+ onCarryStart?.();
12826
+ const cloneWrapper =
12827
+ cloneWrapperCaught || createDragClone(draggedElement, pointerEvent);
12828
+ if (cloneWrapperCaught) {
12829
+ liftDragClone(cloneWrapperCaught, pointerEvent);
12802
12830
  }
12803
- dropHintEl.removeAttribute("data-drop-edge");
12804
- dropHintEl.removeAttribute("data-drop-over");
12805
- dropHintEl.style.removeProperty("--drop-target-top");
12806
- dropHintEl.style.removeProperty("--drop-target-bottom");
12807
- dropHintEl.style.removeProperty("--drop-target-left");
12808
- dropHintEl.style.removeProperty("--drop-target-width");
12809
- dropHintEl.style.removeProperty("--drop-target-height");
12810
- };
12811
- const clearDropHint = () => {
12812
- currentBeforeElement = undefined;
12813
- currentReleaseElement = undefined;
12814
- clearDropHintDOM();
12815
- };
12816
- dragGesture.addDragCallback(gestureInfo => {
12817
- if (!dropHintEl) {
12818
- return;
12831
+ draggedElement.setAttribute("navi-drag-clone-source", "");
12832
+
12833
+ const gestureController = createDragToMoveGestureController({
12834
+ direction,
12835
+ releasePositionEffect: "manual",
12836
+ areaConstraint,
12837
+ ...options,
12838
+ });
12839
+ const dragGesture = gestureController.grabViaPointer(pointerEvent, {
12840
+ element: draggedElement,
12841
+ elementToMove: cloneWrapper,
12842
+ });
12843
+ // getDropTargetInfo uses gestureInfo.elementImpacted to compute the dragged rect.
12844
+ // Point it at the clone so drop detection tracks the clone's current position.
12845
+ dragGesture.gestureInfo.elementImpacted = cloneWrapper;
12846
+
12847
+ // No place to land, no hint: an element that can only be thrown away has
12848
+ // nowhere to be put. What the hint LOOKS like follows what a place is here —
12849
+ // a line in the gap between two items, or the place itself lit up.
12850
+ const dropHintEl = canLand
12851
+ ? createDropSurface()
12852
+ : canReorder
12853
+ ? createDropHint()
12854
+ : null;
12855
+ if (dropHintEl) {
12856
+ // In the container it draws into, which is where its own vars are set: the
12857
+ // shape of a drop hint is a property of the list or board it belongs to,
12858
+ // and reading it from there is inheritance rather than a hand-off.
12859
+ draggedElement.parentElement.appendChild(dropHintEl);
12819
12860
  }
12820
- const allItems = [];
12821
- const items = [];
12822
- for (const el of containerElement.querySelectorAll(itemSelector)) {
12823
- // The copy in hand is a clone of an item, attributes included, and it
12824
- // lives in this same parent: it matches the selector without being
12825
- // an item. Excluded here rather than by stripping attributes off the
12826
- // clone, because a custom itemSelector (a class, a tag) would still
12827
- // match it.
12828
- if (cloneWrapper.contains(el)) {
12829
- continue;
12861
+ // The hint first, the clone second: that order is what stacks them in the
12862
+ // top layer. A copy taken back in hand is already up there, and the hint
12863
+ // just went above it — shown again it returns to the top, where what the
12864
+ // hand carries belongs.
12865
+ dropHintEl?.showPopover();
12866
+ if (cloneWrapperCaught) {
12867
+ cloneWrapper.hidePopover();
12868
+ }
12869
+ cloneWrapper.showPopover();
12870
+
12871
+ // currentBeforeElement: element before which the grabbed item will be inserted (null = end)
12872
+ // currentReleaseElement: the actual hovered drop target — used to snap the clone on release
12873
+ let currentBeforeElement;
12874
+ let currentReleaseElement;
12875
+
12876
+ const clearDropHintDOM = () => {
12877
+ if (!dropHintEl) {
12878
+ return;
12830
12879
  }
12831
- allItems.push(el);
12832
- if (el !== draggedElement) {
12833
- items.push(el);
12880
+ dropHintEl.removeAttribute("data-drop-edge");
12881
+ dropHintEl.removeAttribute("data-drop-over");
12882
+ dropHintEl.style.removeProperty("--drop-target-top");
12883
+ dropHintEl.style.removeProperty("--drop-target-bottom");
12884
+ dropHintEl.style.removeProperty("--drop-target-left");
12885
+ dropHintEl.style.removeProperty("--drop-target-width");
12886
+ dropHintEl.style.removeProperty("--drop-target-height");
12887
+ };
12888
+
12889
+ const clearDropHint = () => {
12890
+ currentBeforeElement = undefined;
12891
+ currentReleaseElement = undefined;
12892
+ clearDropHintDOM();
12893
+ };
12894
+
12895
+ dragGesture.addDragCallback((gestureInfo) => {
12896
+ if (!dropHintEl) {
12897
+ return;
12834
12898
  }
12835
- }
12836
- const dropTargetInfo = getDropTargetInfo(gestureInfo, items, {
12837
- // The edges of a LIST: above the first row means the top of it, below
12838
- // the last one means the end of it. A board has no such reading — away
12839
- // from every place is away from every place.
12840
- fallbackToEdge: !canLand
12841
- });
12842
- gestureInfo.dropTargetInfo = dropTargetInfo || null;
12843
- if (!dropTargetInfo) {
12844
- clearDropHint();
12845
- return;
12846
- }
12847
- if (canLand) {
12848
- // The whole element is the target, so which of its edges the copy came
12849
- // in by says nothing: there is no gap to be on one side of.
12850
- const dropElement = dropTargetInfo.element;
12851
- if (dropElement === currentReleaseElement) {
12899
+ const allItems = [];
12900
+ const items = [];
12901
+ for (const el of containerElement.querySelectorAll(itemSelector)) {
12902
+ // The copy in hand is a clone of an item, attributes included, and it
12903
+ // lives in this same parent: it matches the selector without being
12904
+ // an item. Excluded here rather than by stripping attributes off the
12905
+ // clone, because a custom itemSelector (a class, a tag) would still
12906
+ // match it.
12907
+ if (cloneWrapper.contains(el)) {
12908
+ continue;
12909
+ }
12910
+ allItems.push(el);
12911
+ if (el !== draggedElement) {
12912
+ items.push(el);
12913
+ }
12914
+ }
12915
+
12916
+ const dropTargetInfo = getDropTargetInfo(gestureInfo, items, {
12917
+ // The edges of a LIST: above the first row means the top of it, below
12918
+ // the last one means the end of it. A board has no such reading — away
12919
+ // from every place is away from every place.
12920
+ fallbackToEdge: !canLand,
12921
+ });
12922
+ gestureInfo.dropTargetInfo = dropTargetInfo || null;
12923
+ if (!dropTargetInfo) {
12924
+ clearDropHint();
12852
12925
  return;
12853
12926
  }
12854
- currentReleaseElement = dropElement;
12855
- const dropRect = dropElement.getBoundingClientRect();
12856
- dropHintEl.setAttribute("data-drop-over", "");
12857
- dropHintEl.style.setProperty("--drop-target-top", `${dropRect.top}px`);
12858
- dropHintEl.style.setProperty("--drop-target-left", `${dropRect.left}px`);
12859
- dropHintEl.style.setProperty("--drop-target-width", `${dropRect.width}px`);
12860
- dropHintEl.style.setProperty("--drop-target-height", `${dropRect.height}px`);
12861
- return;
12862
- }
12863
- // Convert {element, edge} to a beforeElement using the items array
12864
- // (not nextElementSibling, which breaks if non-item elements exist between items).
12865
- // edge "start" → insert before the hovered element
12866
- // edge "end" → insert before the next item (null = append at end)
12867
- const edge = dropTargetInfo.elementSide.y;
12868
- const hoveredIndex = items.indexOf(dropTargetInfo.element);
12869
- const beforeElement = edge === "start" ? dropTargetInfo.element : items[hoveredIndex + 1] ?? null;
12870
- // Detect no-op: result would leave the grabbed element in the same position.
12871
- const elementIndex = allItems.indexOf(draggedElement);
12872
- const elementNextItem = allItems[elementIndex + 1] ?? null;
12873
- const isNoop = beforeElement === elementNextItem;
12874
- if (isNoop) {
12875
- clearDropHint();
12876
- return;
12877
- }
12878
- // Early return if nothing changed.
12879
- const releaseElement = dropTargetInfo.element;
12880
- if (beforeElement === currentBeforeElement && releaseElement === currentReleaseElement) {
12881
- return;
12882
- }
12883
- currentBeforeElement = beforeElement;
12884
- currentReleaseElement = releaseElement;
12885
- // Update drop hint CSS vars.
12886
- // beforeElement = null → insert at end (hint after last item)
12887
- // beforeElement = X → insert before X (hint at top edge of X)
12888
- const anchorEl = beforeElement || items[items.length - 1];
12889
- const anchorEdge = beforeElement !== null ? "top" : "bottom";
12890
- // Viewport coordinates, straight from the anchor row: the hint is fixed
12891
- // in the page (see its CSS), so there is no container box to be relative
12892
- // to and no scroll offset to add back.
12893
- const anchorRect = anchorEl.getBoundingClientRect();
12894
- dropHintEl.setAttribute("data-drop-edge", anchorEdge);
12895
- dropHintEl.style.setProperty("--drop-target-top", `${anchorRect.top}px`);
12896
- dropHintEl.style.setProperty("--drop-target-bottom", `${anchorRect.bottom}px`);
12897
- dropHintEl.style.setProperty("--drop-target-left", `${anchorRect.left}px`);
12898
- dropHintEl.style.setProperty("--drop-target-width", `${anchorRect.width}px`);
12899
- });
12900
- dragGesture.addReleaseCallback(async gestureInfo => {
12901
- clearDropHintDOM();
12902
- dropHintEl?.remove();
12903
-
12904
- // What THIS release means, from what the element said it can answer. A
12905
- // throw is asked about first: it is the more insistent of the two, and a
12906
- // hand that sent the thing across the screen has not asked for it to swap
12907
- // places with whatever it happened to fly over.
12908
- const hasDropTarget = canLand ? currentReleaseElement !== undefined : currentBeforeElement !== undefined;
12909
- const dropMeans = resolveDropMeaning({
12910
- gestureInfo,
12911
- hasDropTarget,
12912
- canReorder,
12913
- canToss,
12914
- canLand,
12915
- tossDistance,
12916
- tossSpeed
12927
+ if (canLand) {
12928
+ // The whole element is the target, so which of its edges the copy came
12929
+ // in by says nothing: there is no gap to be on one side of.
12930
+ const dropElement = dropTargetInfo.element;
12931
+ if (dropElement === currentReleaseElement) {
12932
+ return;
12933
+ }
12934
+ currentReleaseElement = dropElement;
12935
+ const dropRect = dropElement.getBoundingClientRect();
12936
+ dropHintEl.setAttribute("data-drop-over", "");
12937
+ dropHintEl.style.setProperty(
12938
+ "--drop-target-top",
12939
+ `${dropRect.top}px`,
12940
+ );
12941
+ dropHintEl.style.setProperty(
12942
+ "--drop-target-left",
12943
+ `${dropRect.left}px`,
12944
+ );
12945
+ dropHintEl.style.setProperty(
12946
+ "--drop-target-width",
12947
+ `${dropRect.width}px`,
12948
+ );
12949
+ dropHintEl.style.setProperty(
12950
+ "--drop-target-height",
12951
+ `${dropRect.height}px`,
12952
+ );
12953
+ return;
12954
+ }
12955
+ // Convert {element, edge} to a beforeElement using the items array
12956
+ // (not nextElementSibling, which breaks if non-item elements exist between items).
12957
+ // edge "start" → insert before the hovered element
12958
+ // edge "end" → insert before the next item (null = append at end)
12959
+ const edge = dropTargetInfo.elementSide.y;
12960
+ const hoveredIndex = items.indexOf(dropTargetInfo.element);
12961
+ const beforeElement =
12962
+ edge === "start"
12963
+ ? dropTargetInfo.element
12964
+ : (items[hoveredIndex + 1] ?? null);
12965
+ // Detect no-op: result would leave the grabbed element in the same position.
12966
+ const elementIndex = allItems.indexOf(draggedElement);
12967
+ const elementNextItem = allItems[elementIndex + 1] ?? null;
12968
+ const isNoop = beforeElement === elementNextItem;
12969
+ if (isNoop) {
12970
+ clearDropHint();
12971
+ return;
12972
+ }
12973
+ // Early return if nothing changed.
12974
+ const releaseElement = dropTargetInfo.element;
12975
+ if (
12976
+ beforeElement === currentBeforeElement &&
12977
+ releaseElement === currentReleaseElement
12978
+ ) {
12979
+ return;
12980
+ }
12981
+ currentBeforeElement = beforeElement;
12982
+ currentReleaseElement = releaseElement;
12983
+ // Update drop hint CSS vars.
12984
+ // beforeElement = null → insert at end (hint after last item)
12985
+ // beforeElement = X → insert before X (hint at top edge of X)
12986
+ const anchorEl = beforeElement || items[items.length - 1];
12987
+ const anchorEdge = beforeElement !== null ? "top" : "bottom";
12988
+ // Viewport coordinates, straight from the anchor row: the hint is fixed
12989
+ // in the page (see its CSS), so there is no container box to be relative
12990
+ // to and no scroll offset to add back.
12991
+ const anchorRect = anchorEl.getBoundingClientRect();
12992
+ dropHintEl.setAttribute("data-drop-edge", anchorEdge);
12993
+ dropHintEl.style.setProperty(
12994
+ "--drop-target-top",
12995
+ `${anchorRect.top}px`,
12996
+ );
12997
+ dropHintEl.style.setProperty(
12998
+ "--drop-target-bottom",
12999
+ `${anchorRect.bottom}px`,
13000
+ );
13001
+ dropHintEl.style.setProperty(
13002
+ "--drop-target-left",
13003
+ `${anchorRect.left}px`,
13004
+ );
13005
+ dropHintEl.style.setProperty(
13006
+ "--drop-target-width",
13007
+ `${anchorRect.width}px`,
13008
+ );
12917
13009
  });
12918
13010
 
12919
- // Let go of and still on the screen: from here until it is taken away
12920
- // the copy can be taken back in hand (see letCopyBeCaught).
12921
- const copyLetGoOf = letCopyBeCaught(cloneWrapper, (pointerDownEvent, whenCarried) => startCarry(pointerDownEvent, cloneWrapper, whenCarried));
12922
-
12923
- // The copy stops where the hand left it, and the answer is given a way to
12924
- // take it the rest of the way — synchronously, inside a view transition, so
12925
- // it is captured where it lands rather than where it was let go of.
12926
- const landCopyOn = async (targetElement, answer) => {
12927
- const clone = cloneWrapper.firstElementChild;
12928
- // Bake the current visual position (transform included) into the CSS vars
12929
- // so the copy stays where the user released it when the transform goes.
12930
- setCloneViewportRect(cloneWrapper, cloneWrapper);
12931
- gestureInfo.cancelPosition();
12932
- // Where the copy comes down is not always the thing it came down ON: a
12933
- // place of a board can be larger than what stands on it, and the copy
12934
- // has to keep its own size and land where the item will be. Said with
12935
- // an element, because the caller has one — the piece already standing
12936
- // there, the empty slot waiting.
12937
- const syncCloneWithDropTarget = (landingElement = targetElement) => {
12938
- setCloneViewportRect(cloneWrapper, landingElement);
12939
- // Removing this attr drops the CSS scale, so the browser captures the
12940
- // copy at scale 1 as the "new" state.
12941
- clone.removeAttribute("navi-drag-clone");
13011
+ dragGesture.addReleaseCallback(async (gestureInfo) => {
13012
+ clearDropHintDOM();
13013
+ dropHintEl?.remove();
13014
+
13015
+ // What THIS release means, from what the element said it can answer. A
13016
+ // throw is asked about first: it is the more insistent of the two, and a
13017
+ // hand that sent the thing across the screen has not asked for it to swap
13018
+ // places with whatever it happened to fly over.
13019
+ const hasDropTarget = canLand
13020
+ ? currentReleaseElement !== undefined
13021
+ : currentBeforeElement !== undefined;
13022
+ const dropMeans = resolveDropMeaning({
13023
+ gestureInfo,
13024
+ hasDropTarget,
13025
+ canReorder,
13026
+ canToss,
13027
+ canLand,
13028
+ tossDistance,
13029
+ tossSpeed,
13030
+ });
13031
+
13032
+ // Let go of and still on the screen: from here until it is taken away
13033
+ // the copy can be taken back in hand (see letCopyBeCaught).
13034
+ const copyLetGoOf = letCopyBeCaught(
13035
+ cloneWrapper,
13036
+ (pointerDownEvent, whenCarried) =>
13037
+ startCarry(pointerDownEvent, cloneWrapper, whenCarried),
13038
+ );
13039
+
13040
+ // The copy stops where the hand left it, and the answer is given a way to
13041
+ // take it the rest of the way — synchronously, inside a view transition, so
13042
+ // it is captured where it lands rather than where it was let go of.
13043
+ const landCopyOn = async (targetElement, answer) => {
13044
+ const clone = cloneWrapper.firstElementChild;
13045
+ // Bake the current visual position (transform included) into the CSS vars
13046
+ // so the copy stays where the user released it when the transform goes.
13047
+ setCloneViewportRect(cloneWrapper, cloneWrapper);
13048
+ gestureInfo.cancelPosition();
13049
+ // Where the copy comes down is not always the thing it came down ON: a
13050
+ // place of a board can be larger than what stands on it, and the copy
13051
+ // has to keep its own size and land where the item will be. Said with
13052
+ // an element, because the caller has one — the piece already standing
13053
+ // there, the empty slot waiting.
13054
+ const syncCloneWithDropTarget = (
13055
+ landingElement = targetElement,
13056
+ ) => {
13057
+ setCloneViewportRect(cloneWrapper, landingElement);
13058
+ // Removing this attr drops the CSS scale, so the browser captures the
13059
+ // copy at scale 1 as the "new" state.
13060
+ clone.removeAttribute("navi-drag-clone");
13061
+ };
13062
+ await answer(syncCloneWithDropTarget);
12942
13063
  };
12943
- await answer(syncCloneWithDropTarget);
12944
- };
12945
- if (dropMeans === "toss") {
12946
- // Bake the position the hand left it at, so the flight starts from
12947
- // there rather than from where the clone was declared.
12948
- setCloneViewportRect(cloneWrapper, cloneWrapper);
12949
- gestureInfo.cancelPosition();
12950
- const gone = await tossCloneAway(cloneWrapper, gestureInfo, onToss);
12951
- if (!gone) {
12952
- // It still exists, so the screen has to say so: the copy comes back
12953
- // over the original, and taking it away then reveals the row in
12954
- // place.
12955
- await settleCloneBack(cloneWrapper, draggedElement);
13064
+
13065
+ if (dropMeans === "toss") {
13066
+ // Bake the position the hand left it at, so the flight starts from
13067
+ // there rather than from where the clone was declared.
13068
+ setCloneViewportRect(cloneWrapper, cloneWrapper);
13069
+ gestureInfo.cancelPosition();
13070
+ const gone = await tossCloneAway(cloneWrapper, gestureInfo, onToss);
13071
+ if (!gone) {
13072
+ // It still exists, so the screen has to say so: the copy comes back
13073
+ // over the original, and taking it away then reveals the row in
13074
+ // place.
13075
+ await settleCloneBack(cloneWrapper, draggedElement);
13076
+ }
13077
+ } else if (dropMeans === "land") {
13078
+ await landCopyOn(currentReleaseElement, (syncCloneWithDropTarget) =>
13079
+ onLand(
13080
+ getItemId(draggedElement),
13081
+ getItemId(currentReleaseElement),
13082
+ syncCloneWithDropTarget,
13083
+ ),
13084
+ );
13085
+ } else if (dropMeans === "reorder") {
13086
+ await landCopyOn(currentReleaseElement, (syncCloneWithDropTarget) =>
13087
+ onReorder(
13088
+ getItemId(draggedElement),
13089
+ currentBeforeElement ? getItemId(currentBeforeElement) : null,
13090
+ syncCloneWithDropTarget,
13091
+ ),
13092
+ );
12956
13093
  }
12957
- } else if (dropMeans === "land") {
12958
- await landCopyOn(currentReleaseElement, syncCloneWithDropTarget => onLand(getItemId(draggedElement), getItemId(currentReleaseElement), syncCloneWithDropTarget));
12959
- } else if (dropMeans === "reorder") {
12960
- await landCopyOn(currentReleaseElement, syncCloneWithDropTarget => onReorder(getItemId(draggedElement), currentBeforeElement ? getItemId(currentBeforeElement) : null, syncCloneWithDropTarget));
12961
- }
12962
- if (await copyLetGoOf.settled()) {
12963
- // In a hand again: the copy, and the place kept for it, are the new
12964
- // gesture's — this one has nothing left to take away.
12965
- return;
12966
- }
12967
- draggedElement.removeAttribute("navi-drag-clone-source");
12968
- cloneWrapper.remove();
12969
- });
12970
- return dragGesture;
12971
- }, {
12972
- threshold,
12973
- // A copy caught on its way home is not an ambiguous press: the hand
12974
- // reached for something moving, and the press was already matched
12975
- // against the copy's own box before it got here. The wait a finger is
12976
- // asked for elsewhere tells a scroll from a drag, and there is no scroll
12977
- // to tell it from — the copy covers that spot from the top layer. Asked
12978
- // for anyway it cannot even be answered: the wait is about as long as the
12979
- // journey, so the thing is home before the proof is done, while a mouse
12980
- // takes it in five pixels.
12981
- longPress: cloneWrapperCaught ? false : longPress,
12982
- longPressDelay,
12983
- longPressSlop,
12984
- onPressStart,
12985
- onPressCancel,
12986
- onPress
12987
- });
13094
+ if (await copyLetGoOf.settled()) {
13095
+ // In a hand again: the copy, and the place kept for it, are the new
13096
+ // gesture's — this one has nothing left to take away.
13097
+ return;
13098
+ }
13099
+ draggedElement.removeAttribute("navi-drag-clone-source");
13100
+ cloneWrapper.remove();
13101
+ });
13102
+
13103
+ return dragGesture;
13104
+ },
13105
+ {
13106
+ threshold,
13107
+ // A copy caught on its way home is not an ambiguous press: the hand
13108
+ // reached for something moving, and the press was already matched
13109
+ // against the copy's own box before it got here. The wait a finger is
13110
+ // asked for elsewhere tells a scroll from a drag, and there is no scroll
13111
+ // to tell it from — the copy covers that spot from the top layer. Asked
13112
+ // for anyway it cannot even be answered: the wait is about as long as the
13113
+ // journey, so the thing is home before the proof is done, while a mouse
13114
+ // takes it in five pixels.
13115
+ longPress: cloneWrapperCaught ? false : longPress,
13116
+ longPressDelay,
13117
+ longPressSlop,
13118
+ onPressStart,
13119
+ onPressCancel,
13120
+ onPress,
13121
+ },
13122
+ );
12988
13123
  };
12989
13124
  return startCarry(event);
12990
13125
  };
@@ -13017,7 +13152,7 @@ const setCloneViewportRect = (cloneWrapper, el) => {
13017
13152
 
13018
13153
  // The chevron is the one the table's column drop preview uses, rotated by the
13019
13154
  // CSS above so each cap points into the line.
13020
- const dropHintTemplate = /* html */`
13155
+ const dropHintTemplate = /* html */ `
13021
13156
  <div
13022
13157
  class="navi_drop_hint"
13023
13158
  popover="manual"
@@ -13043,6 +13178,7 @@ const createDropHint = () => {
13043
13178
  div.innerHTML = dropHintTemplate.trim();
13044
13179
  return div.firstElementChild;
13045
13180
  };
13181
+
13046
13182
  const createDropSurface = () => {
13047
13183
  const div = document.createElement("div");
13048
13184
  div.className = "navi_drop_surface";
@@ -13061,17 +13197,14 @@ const createDropSurface = () => {
13061
13197
  * that means "get rid of this" — nobody waits to watch it go.
13062
13198
  */
13063
13199
  const tossCloneAway = async (cloneWrapper, gestureInfo, onToss) => {
13064
- const {
13065
- xDelta,
13066
- yDelta
13067
- } = gestureInfo.layout;
13200
+ const { xDelta, yDelta } = gestureInfo.layout;
13068
13201
  const distance = Math.hypot(xDelta, yDelta) || 1;
13069
13202
  cloneWrapper.dataset.tossed = "away";
13070
- cloneWrapper.style.translate = `${xDelta / distance * TOSS_DISTANCE}px ${yDelta / distance * TOSS_DISTANCE}px`;
13203
+ cloneWrapper.style.translate = `${(xDelta / distance) * TOSS_DISTANCE}px ${
13204
+ (yDelta / distance) * TOSS_DISTANCE
13205
+ }px`;
13071
13206
  try {
13072
- await onToss?.({
13073
- gestureInfo
13074
- });
13207
+ await onToss?.({ gestureInfo });
13075
13208
  return true;
13076
13209
  } catch {
13077
13210
  return false;
@@ -13088,11 +13221,17 @@ const tossCloneAway = async (cloneWrapper, gestureInfo, onToss) => {
13088
13221
  */
13089
13222
  const settleCloneBack = (cloneWrapper, sourceElement) => {
13090
13223
  const sourceRect = sourceElement.getBoundingClientRect();
13091
- const releaseLeft = parseFloat(cloneWrapper.style.getPropertyValue("--clone-left"));
13092
- const releaseTop = parseFloat(cloneWrapper.style.getPropertyValue("--clone-top"));
13224
+ const releaseLeft = parseFloat(
13225
+ cloneWrapper.style.getPropertyValue("--clone-left"),
13226
+ );
13227
+ const releaseTop = parseFloat(
13228
+ cloneWrapper.style.getPropertyValue("--clone-top"),
13229
+ );
13093
13230
  cloneWrapper.dataset.tossed = "back";
13094
- cloneWrapper.style.translate = `${sourceRect.left - releaseLeft}px ${sourceRect.top - releaseTop}px`;
13095
- return new Promise(resolve => {
13231
+ cloneWrapper.style.translate = `${sourceRect.left - releaseLeft}px ${
13232
+ sourceRect.top - releaseTop
13233
+ }px`;
13234
+ return new Promise((resolve) => {
13096
13235
  setTimeout(resolve, TOSS_DURATION_MS);
13097
13236
  });
13098
13237
  };
@@ -13132,23 +13271,20 @@ const settleCloneBack = (cloneWrapper, sourceElement) => {
13132
13271
  const letCopyBeCaught = (cloneWrapper, carryAgain) => {
13133
13272
  let caught = false;
13134
13273
  let pressIsOver = Promise.resolve();
13135
- const onPointerDown = pointerDownEvent => {
13136
- const {
13137
- left,
13138
- right,
13139
- top,
13140
- bottom
13141
- } = cloneWrapper.getBoundingClientRect();
13142
- const {
13143
- clientX,
13144
- clientY
13145
- } = pointerDownEvent;
13146
- if (clientX < left || clientX > right || clientY < top || clientY > bottom) {
13274
+ const onPointerDown = (pointerDownEvent) => {
13275
+ const { left, right, top, bottom } = cloneWrapper.getBoundingClientRect();
13276
+ const { clientX, clientY } = pointerDownEvent;
13277
+ if (
13278
+ clientX < left ||
13279
+ clientX > right ||
13280
+ clientY < top ||
13281
+ clientY > bottom
13282
+ ) {
13147
13283
  // Somewhere else on the page: this press is not about the copy.
13148
13284
  return;
13149
13285
  }
13150
13286
  let pressIsOverResolve;
13151
- pressIsOver = new Promise(resolve => {
13287
+ pressIsOver = new Promise((resolve) => {
13152
13288
  pressIsOverResolve = resolve;
13153
13289
  });
13154
13290
  const onPointerEnd = () => {
@@ -13176,9 +13312,8 @@ const letCopyBeCaught = (cloneWrapper, carryAgain) => {
13176
13312
  // caught with a finger and can with a mouse.
13177
13313
  const root = document.documentElement;
13178
13314
  root.setAttribute("data-drag-catchable", "");
13179
- root.addEventListener("touchmove", keepTouchRefusable, {
13180
- passive: false
13181
- });
13315
+ root.addEventListener("touchmove", keepTouchRefusable, { passive: false });
13316
+
13182
13317
  return {
13183
13318
  settled: async () => {
13184
13319
  // A hand that lets go and presses again while the copy is still there is
@@ -13193,7 +13328,7 @@ const letCopyBeCaught = (cloneWrapper, carryAgain) => {
13193
13328
  root.removeAttribute("data-drag-catchable");
13194
13329
  root.removeEventListener("touchmove", keepTouchRefusable);
13195
13330
  return caught;
13196
- }
13331
+ },
13197
13332
  };
13198
13333
  };
13199
13334
 
@@ -13203,11 +13338,16 @@ const letCopyBeCaught = (cloneWrapper, carryAgain) => {
13203
13338
  // again is somewhere else on it — so taking one back hands it both again.
13204
13339
  const liftDragClone = (cloneWrapper, pointerEvent) => {
13205
13340
  const rect = cloneWrapper.getBoundingClientRect();
13206
- cloneWrapper.style.setProperty("--drag-origin", `${pointerEvent.clientX - rect.left}px ${pointerEvent.clientY - rect.top}px`);
13341
+ cloneWrapper.style.setProperty(
13342
+ "--drag-origin",
13343
+ `${pointerEvent.clientX - rect.left}px ${pointerEvent.clientY - rect.top}px`,
13344
+ );
13207
13345
  cloneWrapper.firstElementChild.setAttribute("navi-drag-clone", "");
13208
13346
  };
13347
+
13209
13348
  const createDragClone = (element, pointerEvent) => {
13210
13349
  const rect = element.getBoundingClientRect();
13350
+
13211
13351
  const wrapper = document.createElement("div");
13212
13352
  wrapper.setAttribute("navi-drag-clone-wrapper", "");
13213
13353
  // A copy can be caught on its way home (see settleCloneBack), which is a press
@@ -13223,7 +13363,10 @@ const createDragClone = (element, pointerEvent) => {
13223
13363
  // Grab point within the element — used as transform-origin so the
13224
13364
  // scale(1.15) expands from where the user clicked, not the element center.
13225
13365
  // These offsets are element-relative so viewport coords are correct here.
13226
- wrapper.style.setProperty("--drag-origin", `${pointerEvent.clientX - rect.left}px ${pointerEvent.clientY - rect.top}px`);
13366
+ wrapper.style.setProperty(
13367
+ "--drag-origin",
13368
+ `${pointerEvent.clientX - rect.left}px ${pointerEvent.clientY - rect.top}px`,
13369
+ );
13227
13370
  const elementClone = element.cloneNode(true);
13228
13371
  // A deep copy copies the ids too, and two elements answering to one id is a
13229
13372
  // document that lies: getElementById picks whichever comes first, an anchor
@@ -13240,6 +13383,7 @@ const createDragClone = (element, pointerEvent) => {
13240
13383
  // (The original wears it too, but it is hidden — see navi-drag-clone-source.)
13241
13384
  elementClone.setAttribute("data-grabbed", "");
13242
13385
  elementClone.style.viewTransitionName = "navi-drag-clone";
13386
+
13243
13387
  wrapper.appendChild(elementClone);
13244
13388
  // Beside the thing it copies, so it stands where that thing stands: every
13245
13389
  // inherited value and every custom property the original reads, the copy reads
@@ -13248,6 +13392,7 @@ const createDragClone = (element, pointerEvent) => {
13248
13392
  // whatever its depth in the tree, and being fixed keeps it out of the
13249
13393
  // scrollable overflow of the list it sits in.
13250
13394
  element.parentElement.appendChild(wrapper);
13395
+
13251
13396
  return wrapper;
13252
13397
  };
13253
13398
 
@@ -13329,44 +13474,7 @@ const getResizeDirection = (element) => {
13329
13474
  return { x, y };
13330
13475
  };
13331
13476
 
13332
- installImportMetaCssBuild(import.meta);/**
13333
- * What a drag means when it TRAVELS: a whole screen pushed aside to bring in the
13334
- * next one — slides inside one box, pages that are URLs.
13335
- *
13336
- * The pointer itself is not read here: reading a press, waiting for it to become
13337
- * a gesture, capturing it, measuring how fast it goes, swallowing the click it
13338
- * would have made is one gesture system for the whole codebase
13339
- * (@jsenv/dom's drag_gesture + drag_after_intent), and this asks it for the
13340
- * plain version — nothing carried, so no backdrop over the page, nothing made
13341
- * inert, no focus taken: a screen slides and the page keeps its scrolling and
13342
- * its keyboard.
13343
- *
13344
- * What IS here is everything that makes a travel a travel rather than a
13345
- * carry — and it is policy, not plumbing:
13346
- * - the axis is LOCKED by the first movement, instead of being constrained
13347
- * ahead of time;
13348
- * - a press becomes a gesture by distance for every pointer, finger included:
13349
- * a swipe is a travel, and asking a finger to hold still first (the rule for
13350
- * picking an object up) would mean waiting before being allowed to swipe;
13351
- * - what travels walks ONE BOX, resists past its ends, and is measured from
13352
- * where the finger is once it gets there — unless the caller has another box
13353
- * to offer at that edge, and then the gesture walks on into it;
13354
- * - letting go is a question with an answer: a third of a box, or a flick.
13355
- *
13356
- * What is NOT here is geometry: how big a box is, what lies one step that way,
13357
- * what to paint while the finger moves. The caller knows those and nothing else
13358
- * does — this reads the gesture and calls back.
13359
- *
13360
- * Who owns a gesture is decided in three places, and all three are read here:
13361
- * - what says so itself, with [data-no-drag-travel] or by being a field — a
13362
- * component that reads the pointer marks itself, because the container it
13363
- * ends up in cannot know what it is;
13364
- * - a scroller between the pointer and the box with room left that way, which
13365
- * keeps the gesture until it has none;
13366
- * - another box that travels, between the pointer and this one: the innermost
13367
- * one walks the axis it walks, and leaves the others whatever axis it does
13368
- * not (see axesLeftBy).
13369
- */
13477
+ installImportMetaCssBuild(import.meta);
13370
13478
 
13371
13479
  // While a pointer is on something that travels: said on the document, because
13372
13480
  // what has to be told is the document.
@@ -13375,7 +13483,8 @@ const GESTURE_ATTRIBUTE = "data-drag-travel-gesture";
13375
13483
  // …and while one is actually travelling something, which is a later moment and
13376
13484
  // takes more away (see the CSS).
13377
13485
  const WALKING_ATTRIBUTE = "data-drag-travel-walking";
13378
- import.meta.css = [/* css */`
13486
+
13487
+ import.meta.css = /* css */ [`
13379
13488
  :root[${GESTURE_ATTRIBUTE}] {
13380
13489
  /* The bounce the browser plays when a gesture reaches the end of a page —
13381
13490
  and the swipe that goes back in history with it. Both are the browser
@@ -13535,24 +13644,42 @@ const DRAG_RESISTANCE = 0.3;
13535
13644
  // press, and nothing hands that back mid-gesture — the travel would drag a
13536
13645
  // selection along with it. With a finger the hold is the selection UI itself, so
13537
13646
  // there is nothing to take there either.
13538
- const PRESSED_INPUT_TYPES = ['[type="checkbox"]', '[type="radio"]', '[type="button"]', '[type="submit"]', '[type="reset"]', '[type="image"]', '[type="color"]', '[type="file"]'].join(",");
13539
- const NATIVE_POINTER_GESTURES = [{
13540
- // The value follows the pointer, pixel by pixel: a thumb along its rail, a
13541
- // spinner clicked up and down. There is no moment in that which could be
13542
- // given away — the gesture IS the pointer.
13543
- selector: 'input[type="range"],input[type="number"]'
13544
- }, {
13545
- // The text answers the pointer, and differently per hand: a mouse drags a
13546
- // caret through it (a selection), a finger drags the field's own content
13547
- // sideways when it overflows, and a finger held still opens the selection
13548
- // UI. Three things a field is for, against one travel that has a whole box
13549
- // to start from.
13550
- selector: [`input:not(${PRESSED_INPUT_TYPES},[type="range"],[type="number"])`, "textarea", '[contenteditable=""]', '[contenteditable="true"]'].join(",")
13551
- }, {
13552
- // A menu opens under the pointer and the browser keeps the rest of the
13553
- // gesture: dragged over, it picks an option.
13554
- selector: "select"
13555
- }];
13647
+ const PRESSED_INPUT_TYPES = [
13648
+ '[type="checkbox"]',
13649
+ '[type="radio"]',
13650
+ '[type="button"]',
13651
+ '[type="submit"]',
13652
+ '[type="reset"]',
13653
+ '[type="image"]',
13654
+ '[type="color"]',
13655
+ '[type="file"]',
13656
+ ].join(",");
13657
+ const NATIVE_POINTER_GESTURES = [
13658
+ {
13659
+ // The value follows the pointer, pixel by pixel: a thumb along its rail, a
13660
+ // spinner clicked up and down. There is no moment in that which could be
13661
+ // given away — the gesture IS the pointer.
13662
+ selector: 'input[type="range"],input[type="number"]',
13663
+ },
13664
+ {
13665
+ // The text answers the pointer, and differently per hand: a mouse drags a
13666
+ // caret through it (a selection), a finger drags the field's own content
13667
+ // sideways when it overflows, and a finger held still opens the selection
13668
+ // UI. Three things a field is for, against one travel that has a whole box
13669
+ // to start from.
13670
+ selector: [
13671
+ `input:not(${PRESSED_INPUT_TYPES},[type="range"],[type="number"])`,
13672
+ "textarea",
13673
+ '[contenteditable=""]',
13674
+ '[contenteditable="true"]',
13675
+ ].join(","),
13676
+ },
13677
+ {
13678
+ // A menu opens under the pointer and the browser keeps the rest of the
13679
+ // gesture: dragged over, it picks an option.
13680
+ selector: "select",
13681
+ },
13682
+ ];
13556
13683
 
13557
13684
  // …and what an element says against its own tag. Every line above is about what
13558
13685
  // an element DOES, and a tag only says what it IS: a picker's façade is an
@@ -13569,9 +13696,9 @@ const NATIVE_POINTER_GESTURES = [{
13569
13696
  // that can really be typed in) has not made a travel possible, it has taken the
13570
13697
  // gesture the user was making away from them.
13571
13698
  const PRESS_ONLY_ATTRIBUTE = "data-press-only";
13572
- const NATIVE_POINTER_GESTURE_SELECTOR = NATIVE_POINTER_GESTURES.map(({
13573
- selector
13574
- }) => `:is(${selector}):not([${PRESS_ONLY_ATTRIBUTE}])`).join(",");
13699
+ const NATIVE_POINTER_GESTURE_SELECTOR = NATIVE_POINTER_GESTURES.map(
13700
+ ({ selector }) => `:is(${selector}):not([${PRESS_ONLY_ATTRIBUTE}])`,
13701
+ ).join(",");
13575
13702
 
13576
13703
  // What a drag must not start on: what answers the pointer natively (above),
13577
13704
  // plus what has said the gesture is its own. A drag SOURCE is not in the list:
@@ -13583,7 +13710,15 @@ const NATIVE_POINTER_GESTURE_SELECTOR = NATIVE_POINTER_GESTURES.map(({
13583
13710
  // through (see DRAG_IGNORED_SELECTOR in drag_to.js). And so is a popover or a
13584
13711
  // dialog: a layer OVER the box, whose press only bubbles through the box because
13585
13712
  // the layer is anchored in it.
13586
- const DRAG_EXCLUDED_SELECTOR = [NATIVE_POINTER_GESTURE_SELECTOR, "[data-drag-handle]", "[data-no-drag-travel]", '[data-self-interactions~="drag"]', '[data-self-interactions~="*"]', "[popover]", "dialog"].join(",");
13713
+ const DRAG_EXCLUDED_SELECTOR = [
13714
+ NATIVE_POINTER_GESTURE_SELECTOR,
13715
+ "[data-drag-handle]",
13716
+ "[data-no-drag-travel]",
13717
+ '[data-self-interactions~="drag"]',
13718
+ '[data-self-interactions~="*"]',
13719
+ "[popover]",
13720
+ "dialog",
13721
+ ].join(",");
13587
13722
 
13588
13723
  // Which axes a box travels on, one attribute per gesture, said in the DOM by
13589
13724
  // whoever owns the box: it is what a box ABOVE another reads to know the
@@ -13603,8 +13738,12 @@ const DRAG_SOURCE_AXES_ATTRIBUTE = "data-drag-source";
13603
13738
  // covers everything. So a gesture that happened on it is not the gesture of any
13604
13739
  // box it merely sits on top of, and every walk up the tree from the pointer
13605
13740
  // ends here: the boxes above are behind, and behind is not under the finger.
13606
- const TOP_LAYER_SELECTOR = [":popover-open", "dialog:modal", ":fullscreen"].join(",");
13607
- const isTopLayer = element => {
13741
+ const TOP_LAYER_SELECTOR = [
13742
+ ":popover-open",
13743
+ "dialog:modal",
13744
+ ":fullscreen",
13745
+ ].join(",");
13746
+ const isTopLayer = (element) => {
13608
13747
  return element.matches(TOP_LAYER_SELECTOR);
13609
13748
  };
13610
13749
 
@@ -13675,12 +13814,10 @@ const scrollRoomTowards = (fromElement, stopElement, axis, sign) => {
13675
13814
  return false;
13676
13815
  }
13677
13816
  const size = axis === "x" ? element.clientWidth : element.clientHeight;
13678
- const scrollSize = axis === "x" ? element.scrollWidth : element.scrollHeight;
13817
+ const scrollSize =
13818
+ axis === "x" ? element.scrollWidth : element.scrollHeight;
13679
13819
  if (scrollSize > size + 1) {
13680
- const {
13681
- overflowX,
13682
- overflowY
13683
- } = getComputedStyle(element);
13820
+ const { overflowX, overflowY } = getComputedStyle(element);
13684
13821
  const overflow = axis === "x" ? overflowX : overflowY;
13685
13822
  if (overflow === "auto" || overflow === "scroll") {
13686
13823
  const position = axis === "x" ? element.scrollLeft : element.scrollTop;
@@ -13706,7 +13843,7 @@ const travelsAfter = ({
13706
13843
  size,
13707
13844
  velocity,
13708
13845
  towardsSomething,
13709
- commitRatio
13846
+ commitRatio,
13710
13847
  }) => {
13711
13848
  if (!towardsSomething) {
13712
13849
  return false;
@@ -13727,11 +13864,15 @@ const travelsAfter = ({
13727
13864
  // other way. Without this the picture alone decides, and a screen caught at
13728
13865
  // two thirds and thrown back still arrives: the gesture was read as the
13729
13866
  // place it was let go of rather than as a movement.
13730
- if (Math.abs(velocity) > DRAG_THROW_BACK_VELOCITY && Math.sign(velocity) !== sign) {
13867
+ if (
13868
+ Math.abs(velocity) > DRAG_THROW_BACK_VELOCITY &&
13869
+ Math.sign(velocity) !== sign
13870
+ ) {
13731
13871
  return false;
13732
13872
  }
13733
13873
  // …and going towards it travels, however slowly: see DRAG_DRIFT_VELOCITY.
13734
- const drifting = Math.abs(velocity) > DRAG_DRIFT_VELOCITY && Math.sign(velocity) === sign;
13874
+ const drifting =
13875
+ Math.abs(velocity) > DRAG_DRIFT_VELOCITY && Math.sign(velocity) === sign;
13735
13876
  if (drifting && Math.abs(pulled) > DRAG_DRIFT_DISTANCE) {
13736
13877
  return true;
13737
13878
  }
@@ -13799,17 +13940,20 @@ const travelsAfter = ({
13799
13940
  * refused it. Nothing was painted and nothing has to be put back — this is
13800
13941
  * only so the caller can forget the gesture it is holding.
13801
13942
  */
13802
- const startDragToTravel = (pointerDownEvent, {
13803
- element,
13804
- axes = "xy",
13805
- immediate = false,
13806
- commitRatio = DRAG_COMMIT_RATIO,
13807
- onStart,
13808
- onPull,
13809
- onEnd,
13810
- onEdge = () => false,
13811
- onGiveUp = () => {}
13812
- }) => {
13943
+ const startDragToTravel = (
13944
+ pointerDownEvent,
13945
+ {
13946
+ element,
13947
+ axes = "xy",
13948
+ immediate = false,
13949
+ commitRatio = DRAG_COMMIT_RATIO,
13950
+ onStart,
13951
+ onPull,
13952
+ onEnd,
13953
+ onEdge = () => false,
13954
+ onGiveUp = () => {},
13955
+ },
13956
+ ) => {
13813
13957
  const target = pointerDownEvent.target;
13814
13958
  if (!target.closest || isPressExcluded(target, element)) {
13815
13959
  return null;
@@ -13818,8 +13962,15 @@ const startDragToTravel = (pointerDownEvent, {
13818
13962
  // anything between them that is picked up and carried the same way: the
13819
13963
  // gesture is theirs, and this one is left with the axes none of them walks —
13820
13964
  // none at all, most of the time, and then there is no gesture here to read.
13821
- const axesLeftByTravels = axesLeftBy(axes, target, element, DRAG_AXES_ATTRIBUTE);
13822
- const axesLeft = axesLeftByTravels && axesLeftBy(axesLeftByTravels, target, element, DRAG_SOURCE_AXES_ATTRIBUTE);
13965
+ const axesLeftByTravels = axesLeftBy(
13966
+ axes,
13967
+ target,
13968
+ element,
13969
+ DRAG_AXES_ATTRIBUTE,
13970
+ );
13971
+ const axesLeft =
13972
+ axesLeftByTravels &&
13973
+ axesLeftBy(axesLeftByTravels, target, element, DRAG_SOURCE_AXES_ATTRIBUTE);
13823
13974
  if (!axesLeft) {
13824
13975
  return null;
13825
13976
  }
@@ -13835,6 +13986,7 @@ const startDragToTravel = (pointerDownEvent, {
13835
13986
  let travel = null;
13836
13987
  let dragGesture = null;
13837
13988
  let over = false;
13989
+
13838
13990
  const finish = () => {
13839
13991
  if (over) {
13840
13992
  return;
@@ -13848,7 +14000,7 @@ const startDragToTravel = (pointerDownEvent, {
13848
14000
  // A press that never became a travel: the intent never resolved, or the axis
13849
14001
  // it leaned on is not one this box walks. Nothing was painted and nothing has
13850
14002
  // to be put back — the caller is only told so it can forget the gesture.
13851
- const onPressOver = pointerEvent => {
14003
+ const onPressOver = (pointerEvent) => {
13852
14004
  if (pointerEvent.pointerId !== pointerDownEvent.pointerId || travel) {
13853
14005
  return;
13854
14006
  }
@@ -13869,8 +14021,11 @@ const startDragToTravel = (pointerDownEvent, {
13869
14021
  // layout the gesture computes for something being carried: nothing is being
13870
14022
  // carried here, and a scroll happening meanwhile must not read as a finger
13871
14023
  // that moved.
13872
- const coveredOn = (axis, gestureInfo) => axis === "x" ? gestureInfo.dragX - gestureInfo.grabX : gestureInfo.dragY - gestureInfo.grabY;
13873
- const pullOf = gestureInfo => {
14024
+ const coveredOn = (axis, gestureInfo) =>
14025
+ axis === "x"
14026
+ ? gestureInfo.dragX - gestureInfo.grabX
14027
+ : gestureInfo.dragY - gestureInfo.grabY;
14028
+ const pullOf = (gestureInfo) => {
13874
14029
  const covered = coveredOn(travel.axis, gestureInfo);
13875
14030
  return travel.slack + (covered - travel.origin);
13876
14031
  };
@@ -13884,7 +14039,7 @@ const startDragToTravel = (pointerDownEvent, {
13884
14039
  const next = onEdge({
13885
14040
  axis: travel.axis,
13886
14041
  sign,
13887
- event: gestureInfo.dragEvent
14042
+ event: gestureInfo.dragEvent,
13888
14043
  });
13889
14044
  if (!next || !next.size) {
13890
14045
  return null;
@@ -13902,6 +14057,7 @@ const startDragToTravel = (pointerDownEvent, {
13902
14057
  travel.origin = coveredOn(travel.axis, gestureInfo) - pulled;
13903
14058
  return pulled;
13904
14059
  };
14060
+
13905
14061
  const controller = createDragGestureController({
13906
14062
  // The threshold is left at its default and never crossed: what says this
13907
14063
  // press has become a gesture is the intent module below, which calls
@@ -13916,7 +14072,7 @@ const startDragToTravel = (pointerDownEvent, {
13916
14072
  onDragStart: () => {
13917
14073
  document.documentElement.setAttribute(GESTURE_ATTRIBUTE, "");
13918
14074
  },
13919
- onDrag: gestureInfo => {
14075
+ onDrag: (gestureInfo) => {
13920
14076
  // Releasing a gesture reports one last move, so giving one up would come
13921
14077
  // back through here and give it up again, forever.
13922
14078
  if (over) {
@@ -13982,7 +14138,7 @@ const startDragToTravel = (pointerDownEvent, {
13982
14138
  axis,
13983
14139
  sign,
13984
14140
  target,
13985
- event: gestureInfo.dragEvent
14141
+ event: gestureInfo.dragEvent,
13986
14142
  });
13987
14143
  if (!started || !started.size) {
13988
14144
  giveUp();
@@ -14003,17 +14159,21 @@ const startDragToTravel = (pointerDownEvent, {
14003
14159
  // established before the press (see immediate): there was no
14004
14160
  // threshold to cross, so every pixel since the grab is the hand's
14005
14161
  // and is owed to it.
14006
- origin: immediate ? 0 : covered > DRAG_START_THRESHOLD ? DRAG_START_THRESHOLD : covered < -DRAG_START_THRESHOLD ? -DRAG_START_THRESHOLD : covered,
14007
- pulled: started.slack || 0
14162
+ origin: immediate
14163
+ ? 0
14164
+ : covered > DRAG_START_THRESHOLD
14165
+ ? DRAG_START_THRESHOLD
14166
+ : covered < -DRAG_START_THRESHOLD
14167
+ ? -DRAG_START_THRESHOLD
14168
+ : covered,
14169
+ pulled: started.slack || 0,
14008
14170
  };
14009
14171
  document.documentElement.setAttribute(WALKING_ATTRIBUTE, axis);
14010
14172
  // The travel exists: from here the pointer is this box's, and it is
14011
14173
  // followed wherever it goes.
14012
14174
  dragGesture.capturePointer();
14013
14175
  }
14014
- const {
14015
- axis
14016
- } = travel;
14176
+ const { axis } = travel;
14017
14177
  let pulled = pullOf(gestureInfo);
14018
14178
  // Which side is being pulled in: dragging to the right brings in what is
14019
14179
  // on the left, which is what comes BEFORE.
@@ -14051,7 +14211,8 @@ const startDragToTravel = (pointerDownEvent, {
14051
14211
  // from where the finger IS once it has reached the end, so turning
14052
14212
  // around moves the picture at once instead of first walking back
14053
14213
  // over the distance the hand went too far.
14054
- travel.origin = coveredOn(axis, gestureInfo) - (pulled - travel.slack);
14214
+ travel.origin =
14215
+ coveredOn(axis, gestureInfo) - (pulled - travel.slack);
14055
14216
  } else {
14056
14217
  pulled = relayed;
14057
14218
  size = travel.size;
@@ -14064,46 +14225,42 @@ const startDragToTravel = (pointerDownEvent, {
14064
14225
  pulled,
14065
14226
  size,
14066
14227
  progress: pulled / size,
14067
- event: gestureInfo.dragEvent
14228
+ event: gestureInfo.dragEvent,
14068
14229
  });
14069
14230
  },
14070
- onRelease: gestureInfo => {
14231
+ onRelease: (gestureInfo) => {
14071
14232
  if (over || !travel) {
14072
14233
  return;
14073
14234
  }
14074
14235
  finish();
14075
- const {
14076
- axis,
14077
- size,
14078
- pulled,
14079
- slack
14080
- } = travel;
14236
+ const { axis, size, pulled, slack } = travel;
14081
14237
  const towardsSomething = pulled > 0 ? travel.travelBack : travel.travelOn;
14082
- const velocity = axis === "x" ? gestureInfo.velocityX : gestureInfo.velocityY;
14238
+ const velocity =
14239
+ axis === "x" ? gestureInfo.velocityX : gestureInfo.velocityY;
14083
14240
  // A gesture taken away rather than let go of (the browser scrolling
14084
14241
  // something else, a call coming in, another gesture taking the pointer)
14085
14242
  // said nothing: things go back.
14086
14243
  const releaseEvent = gestureInfo.releaseEvent || gestureInfo.dragEvent;
14087
- const {
14088
- cancelled
14089
- } = gestureInfo;
14244
+ const { cancelled } = gestureInfo;
14090
14245
  onEnd({
14091
14246
  axis,
14092
14247
  pulled,
14093
14248
  size,
14094
14249
  sign: pulled > 0 ? 1 : -1,
14095
- travels: !cancelled && travelsAfter({
14096
- pulled,
14097
- slack,
14098
- size,
14099
- velocity,
14100
- towardsSomething,
14101
- commitRatio
14102
- }),
14250
+ travels:
14251
+ !cancelled &&
14252
+ travelsAfter({
14253
+ pulled,
14254
+ slack,
14255
+ size,
14256
+ velocity,
14257
+ towardsSomething,
14258
+ commitRatio,
14259
+ }),
14103
14260
  cancelled,
14104
- event: releaseEvent
14261
+ event: releaseEvent,
14105
14262
  });
14106
- }
14263
+ },
14107
14264
  });
14108
14265
 
14109
14266
  // When a press becomes a gesture, and by which rule. A travel is a swipe, so
@@ -14125,7 +14282,7 @@ const startDragToTravel = (pointerDownEvent, {
14125
14282
  // would be taken away from whoever else is reading the same press for
14126
14283
  // gestures that give themselves up one event later. It is claimed once
14127
14284
  // the travel exists, in onDrag below.
14128
- pointerCaptureDeferred: true
14285
+ pointerCaptureDeferred: true,
14129
14286
  });
14130
14287
  return dragGesture;
14131
14288
  };
@@ -14138,16 +14295,17 @@ const startDragToTravel = (pointerDownEvent, {
14138
14295
  } else {
14139
14296
  dragAfterIntent(pointerDownEvent, grab, {
14140
14297
  longPress: false,
14141
- threshold: DRAG_START_THRESHOLD
14298
+ threshold: DRAG_START_THRESHOLD,
14142
14299
  });
14143
14300
  }
14144
14301
  window.addEventListener("pointerup", onPressOver);
14145
14302
  window.addEventListener("pointercancel", onPressOver);
14303
+
14146
14304
  return {
14147
14305
  stop: () => {
14148
14306
  finish();
14149
14307
  dragGesture?.release();
14150
- }
14308
+ },
14151
14309
  };
14152
14310
  };
14153
14311
 
@@ -14219,11 +14377,9 @@ const WHEEL_REGROW_RUN = 2;
14219
14377
  * scrolls, and pushing content to the right reveals its left.
14220
14378
  * @returns {() => void} stop listening.
14221
14379
  */
14222
- const watchWheelTravel = (element, {
14223
- axes = "xy",
14224
- onStep
14225
- }) => {
14380
+ const watchWheelTravel = (element, { axes = "xy", onStep }) => {
14226
14381
  let gesture = null;
14382
+
14227
14383
  const forgetGesture = () => {
14228
14384
  gesture = null;
14229
14385
  document.documentElement.removeAttribute(GESTURE_ATTRIBUTE);
@@ -14236,10 +14392,8 @@ const watchWheelTravel = (element, {
14236
14392
  // pseudo-elements are told about pointer-events. Heard on the box alone, a
14237
14393
  // gesture that sets a travel off loses every event after the first — and the
14238
14394
  // page scrolls behind the travel with everything that was not taken.
14239
- const isOverElement = wheelEvent => {
14240
- const {
14241
- target
14242
- } = wheelEvent;
14395
+ const isOverElement = (wheelEvent) => {
14396
+ const { target } = wheelEvent;
14243
14397
  if (element.contains(target)) {
14244
14398
  return true;
14245
14399
  }
@@ -14253,26 +14407,22 @@ const watchWheelTravel = (element, {
14253
14407
  if (!target.contains(element)) {
14254
14408
  return false;
14255
14409
  }
14256
- const {
14257
- left,
14258
- right,
14259
- top,
14260
- bottom
14261
- } = element.getBoundingClientRect();
14262
- const {
14263
- clientX,
14264
- clientY
14265
- } = wheelEvent;
14266
- return clientX >= left && clientX <= right && clientY >= top && clientY <= bottom;
14410
+ const { left, right, top, bottom } = element.getBoundingClientRect();
14411
+ const { clientX, clientY } = wheelEvent;
14412
+ return (
14413
+ clientX >= left && clientX <= right && clientY >= top && clientY <= bottom
14414
+ );
14267
14415
  };
14268
- const onWheel = wheelEvent => {
14416
+
14417
+ const onWheel = (wheelEvent) => {
14269
14418
  // The burst is already somebody else's — the box inside this one, a wheel
14270
14419
  // picker, whoever answered its first event. It is theirs to the end of it,
14271
14420
  // wherever the pointer has drifted since (see wheel_gesture.js).
14272
14421
  if (wheelGestureIsTakenFrom(element)) {
14273
14422
  return;
14274
14423
  }
14275
- const axis = Math.abs(wheelEvent.deltaX) > Math.abs(wheelEvent.deltaY) ? "x" : "y";
14424
+ const axis =
14425
+ Math.abs(wheelEvent.deltaX) > Math.abs(wheelEvent.deltaY) ? "x" : "y";
14276
14426
  const delta = axis === "x" ? wheelEvent.deltaX : wheelEvent.deltaY;
14277
14427
  if (!delta) {
14278
14428
  return;
@@ -14297,15 +14447,16 @@ const watchWheelTravel = (element, {
14297
14447
  // it — the same claims a press is read against (see the top of this
14298
14448
  // file), and all of them are answered by giving the gesture up whole:
14299
14449
  // nothing is prevented and the browser scrolls as it would have.
14300
- const {
14301
- target
14302
- } = wheelEvent;
14303
- if (target.closest && isPressExcluded(target, element) || scrollRoomTowards(target, element, axis, sign) ||
14304
- // …plus the third: a box below this one that travels on this axis. Its
14305
- // watcher hears the same wheel event this one does — they all listen at
14306
- // the document — so without this both step, and one push moves two
14307
- // things.
14308
- !axesLeftBy(axis, target, element, WHEEL_AXES_ATTRIBUTE)) {
14450
+ const { target } = wheelEvent;
14451
+ if (
14452
+ (target.closest && isPressExcluded(target, element)) ||
14453
+ scrollRoomTowards(target, element, axis, sign) ||
14454
+ // …plus the third: a box below this one that travels on this axis. Its
14455
+ // watcher hears the same wheel event this one does — they all listen at
14456
+ // the document — so without this both step, and one push moves two
14457
+ // things.
14458
+ !axesLeftBy(axis, target, element, WHEEL_AXES_ATTRIBUTE)
14459
+ ) {
14309
14460
  return;
14310
14461
  }
14311
14462
  gesture = {
@@ -14316,7 +14467,7 @@ const watchWheelTravel = (element, {
14316
14467
  fadeRun: 0,
14317
14468
  growRun: 0,
14318
14469
  faded: false,
14319
- stepped: false
14470
+ stepped: false,
14320
14471
  };
14321
14472
  document.documentElement.setAttribute(GESTURE_ATTRIBUTE, "");
14322
14473
  document.documentElement.setAttribute(WALKING_ATTRIBUTE, axis);
@@ -14345,9 +14496,7 @@ const watchWheelTravel = (element, {
14345
14496
  }
14346
14497
  // …and said on every event on our axis, because a claim nobody renews is a
14347
14498
  // gesture that is over.
14348
- claimWheelGesture(element, {
14349
- onEnd: forgetGesture
14350
- });
14499
+ claimWheelGesture(element, { onEnd: forgetGesture });
14351
14500
  if (sign !== gesture.sign) {
14352
14501
  // Turned around: what was adding up was going the other way.
14353
14502
  gesture.sign = sign;
@@ -14364,11 +14513,7 @@ const watchWheelTravel = (element, {
14364
14513
  // happen pushes harder rather than waiting. Everything a threshold could
14365
14514
  // buy here is bought by what a screen AFTER this one costs.
14366
14515
  gesture.stepped = true;
14367
- onStep({
14368
- axis: gesture.axis,
14369
- sign: gesture.sign,
14370
- event: wheelEvent
14371
- });
14516
+ onStep({ axis: gesture.axis, sign: gesture.sign, event: wheelEvent });
14372
14517
  return;
14373
14518
  }
14374
14519
  const magnitude = Math.abs(delta);
@@ -14393,11 +14538,7 @@ const watchWheelTravel = (element, {
14393
14538
  gesture.faded = false;
14394
14539
  gesture.growRun = 0;
14395
14540
  gesture.pushed = 0;
14396
- onStep({
14397
- axis: gesture.axis,
14398
- sign: gesture.sign,
14399
- event: wheelEvent
14400
- });
14541
+ onStep({ axis: gesture.axis, sign: gesture.sign, event: wheelEvent });
14401
14542
  return;
14402
14543
  }
14403
14544
  if (gesture.fadeRun >= WHEEL_FADE_RUN) {
@@ -14408,20 +14549,15 @@ const watchWheelTravel = (element, {
14408
14549
  return;
14409
14550
  }
14410
14551
  gesture.pushed = 0;
14411
- onStep({
14412
- axis: gesture.axis,
14413
- sign: gesture.sign,
14414
- event: wheelEvent
14415
- });
14552
+ onStep({ axis: gesture.axis, sign: gesture.sign, event: wheelEvent });
14416
14553
  };
14554
+
14417
14555
  document.addEventListener("wheel", onWheel, {
14418
14556
  passive: false,
14419
- capture: true
14557
+ capture: true,
14420
14558
  });
14421
14559
  return () => {
14422
- document.removeEventListener("wheel", onWheel, {
14423
- capture: true
14424
- });
14560
+ document.removeEventListener("wheel", onWheel, { capture: true });
14425
14561
  // Handed back rather than left to lapse: a box that is gone must not hold a
14426
14562
  // gesture the boxes still there are asking about.
14427
14563
  releaseWheelGesture(element);
@@ -14462,11 +14598,13 @@ const isPressExcluded = (target, element) => {
14462
14598
  * @param {Element} element Where the press landed.
14463
14599
  * @returns {boolean}
14464
14600
  */
14465
- const isPressDisputedByDrag = element => {
14601
+ const isPressDisputedByDrag = (element) => {
14466
14602
  if (!element || typeof element.closest !== "function") {
14467
14603
  return false;
14468
14604
  }
14469
- const dragged = element.closest(`[${DRAG_AXES_ATTRIBUTE}],[${DRAG_SOURCE_AXES_ATTRIBUTE}]`);
14605
+ const dragged = element.closest(
14606
+ `[${DRAG_AXES_ATTRIBUTE}],[${DRAG_SOURCE_AXES_ATTRIBUTE}]`,
14607
+ );
14470
14608
  if (!dragged) {
14471
14609
  return false;
14472
14610
  }
@@ -14681,36 +14819,17 @@ const getWidth = (element) => {
14681
14819
  return width;
14682
14820
  };
14683
14821
 
14684
- installImportMetaCssBuild(import.meta);/**
14685
- * Position Sticky Polyfill
14686
- *
14687
- * This module provides a workaround for position:sticky limitations when used with
14688
- * overflow:auto/hidden parent elements (see https://github.com/w3c/csswg-drafts/issues/865).
14689
- *
14690
- * How it works:
14691
- * 1. Creates a placeholder clone of the sticky element to maintain document flow
14692
- * 2. Positions the real element using fixed positioning relative to viewport
14693
- * 3. Adjusts position on scroll to emulate position:sticky behavior
14694
- * 4. Handles parent boundary detection to keep element within its container
14695
- * 5. Updates dimensions on resize and DOM changes
14696
- *
14697
- * Usage:
14698
- * ```
14699
- * const cleanup = initPositionSticky(element);
14700
- * // Later when no longer needed
14701
- * cleanup();
14702
- * ```
14703
- *
14704
- * The element should have a CSS "top" value specified (e.g., top: 10px).
14705
- */
14706
- const css = /* css */`[data-position-sticky-placeholder] {
14822
+ installImportMetaCssBuild(import.meta);
14823
+
14824
+ const css = /* css */ `[data-position-sticky-placeholder] {
14707
14825
  opacity: 0 !important;
14708
14826
  width: auto !important;
14709
14827
  height: auto !important;
14710
14828
  position: static !important;
14711
14829
  }
14712
14830
  `;
14713
- const initPositionSticky = element => {
14831
+
14832
+ const initPositionSticky = (element) => {
14714
14833
  import.meta.css = [css, "@jsenv/dom/src/position/position_sticky.js"];
14715
14834
  const computedStyle = getComputedStyle(element);
14716
14835
  const topCssValue = computedStyle.top;
@@ -14735,10 +14854,20 @@ const initPositionSticky = element => {
14735
14854
  break;
14736
14855
  }
14737
14856
  const style = getComputedStyle(scrollContainer);
14738
- if (xScrollContainer === null && (style.overflowX === "auto" || style.overflowX === "hidden" || style.overflowX === "scroll")) {
14857
+ if (
14858
+ xScrollContainer === null &&
14859
+ (style.overflowX === "auto" ||
14860
+ style.overflowX === "hidden" ||
14861
+ style.overflowX === "scroll")
14862
+ ) {
14739
14863
  xScrollContainer = scrollContainer;
14740
14864
  }
14741
- if (yScrollContainer === null && (style.overflowY === "auto" || style.overflowY === "hidden" || style.overflowY === "scroll")) {
14865
+ if (
14866
+ yScrollContainer === null &&
14867
+ (style.overflowY === "auto" ||
14868
+ style.overflowY === "hidden" ||
14869
+ style.overflowY === "scroll")
14870
+ ) {
14742
14871
  yScrollContainer = scrollContainer;
14743
14872
  }
14744
14873
  }
@@ -14747,6 +14876,7 @@ const initPositionSticky = element => {
14747
14876
  if (!needsPolyfillX && !needsPolyfillY) {
14748
14877
  return () => {}; // Native sticky will work fine on both axes
14749
14878
  }
14879
+
14750
14880
  const cleanupCallbackSet = new Set();
14751
14881
  const cleanup = () => {
14752
14882
  for (const cleanupCallback of cleanupCallbackSet) {
@@ -14754,6 +14884,7 @@ const initPositionSticky = element => {
14754
14884
  }
14755
14885
  cleanupCallbackSet.clear();
14756
14886
  };
14887
+
14757
14888
  const parentElement = element.parentElement;
14758
14889
  const createPlaceholderClone = () => {
14759
14890
  const clone = element.cloneNode(true);
@@ -14761,13 +14892,16 @@ const initPositionSticky = element => {
14761
14892
  clone.removeAttribute("data-sticky");
14762
14893
  return clone;
14763
14894
  };
14895
+
14764
14896
  let placeholder = createPlaceholderClone();
14765
14897
  parentElement.insertBefore(placeholder, element);
14766
14898
  cleanupCallbackSet.add(() => {
14767
14899
  placeholder.remove();
14768
14900
  });
14901
+
14769
14902
  let width = getWidth(element);
14770
14903
  let height = getHeight(element);
14904
+
14771
14905
  const updateSize = () => {
14772
14906
  const newPlaceholder = createPlaceholderClone();
14773
14907
  parentElement.replaceChild(newPlaceholder, placeholder);
@@ -14776,12 +14910,14 @@ const initPositionSticky = element => {
14776
14910
  height = getHeight(placeholder);
14777
14911
  updatePosition();
14778
14912
  };
14913
+
14779
14914
  const updatePosition = () => {
14780
14915
  // Ensure placeholder dimensions match element
14781
14916
  setStyles(placeholder, {
14782
14917
  width: `${width}px`,
14783
- height: `${height}px`
14918
+ height: `${height}px`,
14784
14919
  });
14920
+
14785
14921
  const placeholderRect = placeholder.getBoundingClientRect();
14786
14922
  const parentRect = parentElement.getBoundingClientRect();
14787
14923
 
@@ -14795,12 +14931,12 @@ const initPositionSticky = element => {
14795
14931
  // -420 <= 250 → stuck → element.style.left = 250px (main's left edge). ✓
14796
14932
  //
14797
14933
  // If no intermediate scroll container exists, use 0 (document/viewport edge).
14798
- const yContainerRect = yScrollContainer ? yScrollContainer.getBoundingClientRect() : {
14799
- top: 0
14800
- };
14801
- const xContainerRect = xScrollContainer ? xScrollContainer.getBoundingClientRect() : {
14802
- left: 0
14803
- };
14934
+ const yContainerRect = yScrollContainer
14935
+ ? yScrollContainer.getBoundingClientRect()
14936
+ : { top: 0 };
14937
+ const xContainerRect = xScrollContainer
14938
+ ? xScrollContainer.getBoundingClientRect()
14939
+ : { left: 0 };
14804
14940
  const topThreshold = yContainerRect.top + top;
14805
14941
  const leftThreshold = xContainerRect.left + left;
14806
14942
 
@@ -14843,6 +14979,7 @@ const initPositionSticky = element => {
14843
14979
  } else {
14844
14980
  leftPosition = placeholderRect.left;
14845
14981
  }
14982
+
14846
14983
  element.style.top = `${topPosition}px`;
14847
14984
  element.style.left = `${Math.round(leftPosition)}px`;
14848
14985
  element.style.width = `${width}px`;
@@ -14855,15 +14992,18 @@ const initPositionSticky = element => {
14855
14992
  element.removeAttribute("data-sticky");
14856
14993
  }
14857
14994
  };
14995
+
14858
14996
  {
14859
14997
  const restorePositionStyle = forceStyles(element, {
14860
14998
  "position": "fixed",
14861
14999
  "z-index": 1,
14862
- "will-change": "transform" // Hint for hardware acceleration
15000
+ "will-change": "transform", // Hint for hardware acceleration
14863
15001
  });
14864
15002
  cleanupCallbackSet.add(restorePositionStyle);
14865
15003
  }
15004
+
14866
15005
  updatePosition();
15006
+
14867
15007
  {
14868
15008
  const handleScroll = () => {
14869
15009
  updatePosition();
@@ -14874,16 +15014,15 @@ const initPositionSticky = element => {
14874
15014
  const listenTargets = new Set(scrollContainerSet);
14875
15015
  listenTargets.add(document.documentElement);
14876
15016
  for (const scrollTarget of listenTargets) {
14877
- scrollTarget.addEventListener("scroll", handleScroll, {
14878
- passive: true
14879
- });
15017
+ scrollTarget.addEventListener("scroll", handleScroll, { passive: true });
14880
15018
  cleanupCallbackSet.add(() => {
14881
15019
  scrollTarget.removeEventListener("scroll", handleScroll, {
14882
- passive: true
15020
+ passive: true,
14883
15021
  });
14884
15022
  });
14885
15023
  }
14886
15024
  }
15025
+
14887
15026
  {
14888
15027
  let animationFrame = null;
14889
15028
  const resizeObserver = new ResizeObserver(() => {
@@ -14902,6 +15041,7 @@ const initPositionSticky = element => {
14902
15041
  animationFrame = null;
14903
15042
  });
14904
15043
  }
15044
+
14905
15045
  {
14906
15046
  const mutationObserver = new MutationObserver(() => {
14907
15047
  updateSize();
@@ -14909,12 +15049,13 @@ const initPositionSticky = element => {
14909
15049
  mutationObserver.observe(element, {
14910
15050
  childList: true,
14911
15051
  subtree: true,
14912
- characterData: true
15052
+ characterData: true,
14913
15053
  });
14914
15054
  cleanupCallbackSet.add(() => {
14915
15055
  mutationObserver.disconnect();
14916
15056
  });
14917
15057
  }
15058
+
14918
15059
  return cleanup;
14919
15060
  };
14920
15061
 
@@ -15229,6 +15370,22 @@ const getVisibleViewportRect = () => {
15229
15370
  };
15230
15371
  };
15231
15372
 
15373
+ // The window is not always all the app's: an app simulating a narrower
15374
+ // screen (say a 600px column centered in a wide window) wants everything
15375
+ // pickPositionRelativeTo places against the viewport — dialogs, popovers,
15376
+ // side panels — kept to its own rectangle rather than the window's. The
15377
+ // embedder declares that rectangle here, as insets from the window's edges
15378
+ // ({ left, top, right, bottom } in px — see @jsenv/navi's
15379
+ // --navi-app-inset-*, the CSS twin of this). A callback rather than a
15380
+ // value so it stays live without a subscription; read on each placement.
15381
+ // Placement only: visibleRectEffect keeps reading the real viewport —
15382
+ // whether something is on screen is a fact about the window, not about
15383
+ // the rectangle an app confines itself to.
15384
+ let getPlacementViewportInsets = null;
15385
+ const setPlacementViewportInsets = (getInsets) => {
15386
+ getPlacementViewportInsets = getInsets;
15387
+ };
15388
+
15232
15389
  const isEditableElement = (element) => {
15233
15390
  if (!element) {
15234
15391
  return false;
@@ -16268,12 +16425,24 @@ const pickPositionRelativeTo = (
16268
16425
  } = {},
16269
16426
  ) => {
16270
16427
  // Needed before hasValidAnchor below.
16271
- const {
16428
+ let {
16272
16429
  left: viewportLeft,
16273
16430
  top: viewportTop,
16274
16431
  width: viewportWidth,
16275
16432
  height: viewportHeight,
16276
16433
  } = getVisibleViewportRect();
16434
+ // Narrowed to the embedder's own rectangle (see setPlacementViewportInsets)
16435
+ // before anything reads it: the available area, the flip decisions, the
16436
+ // clamps and the viewport-as-anchor rect below all follow, so an element
16437
+ // docked to an edge lands flush against the app's edge rather than the
16438
+ // window's.
16439
+ if (getPlacementViewportInsets) {
16440
+ const placementInsets = getPlacementViewportInsets();
16441
+ viewportLeft += placementInsets.left;
16442
+ viewportTop += placementInsets.top;
16443
+ viewportWidth -= placementInsets.left + placementInsets.right;
16444
+ viewportHeight -= placementInsets.top + placementInsets.bottom;
16445
+ }
16277
16446
 
16278
16447
  // Resolved early: everything below that would otherwise reach for
16279
16448
  // viewportLeft/Top/Width/Height instead uses these, so a "local" popover
@@ -20334,4 +20503,4 @@ const useResizeStatus = (elementRef, { as = "number" } = {}) => {
20334
20503
  };
20335
20504
  };
20336
20505
 
20337
- export { EASING, ELEMENT_SIZE_CHANGE, activeElementSignal, addActiveElementEffect, addAttributeEffect, allowWheelThrough, appendStyles, applyNewPosition, canScroll, captureScrollState, chainEvent, claimWheelGesture, clickIsSuppressed, closestOpenableAncestor, contrastColor, createBackgroundColorTransition, createBackgroundTransition, createBorderRadiusTransition, createBorderTransition, createDragGestureController, createDragToMoveGestureController, createEventGroupLogger, createGroupTransitionController, createHeightTransition, createIterableWeakSet, createOpacityTransition, createPubSub, createStyleController, createTimelineTransition, createTransition, createTranslateXTransition, createValueEffect, createWidthTransition, cubicBezier, dispatchCustomEvent, dispatchInternalCustomEvent, dispatchPublicCustomEvent, dragAfterIntent, elementIsFocusable, elementIsVisibleForFocus, elementIsVisuallyVisible, findAfter, findAncestor, findBefore, findDescendant, findEvent, findFocusDelegateTarget, findFocusable, findSelfOrAncestorFixedPosition, formatEventSideEffect, getAncestorOpenType, getAvailableHeight, getAvailableWidth, getBackground, getBackgroundColor, getBorder, getBorderRadius, getBorderSizes, getContrastRatio, getDefaultStyles, getDragCoordinates, getDropTargetInfo, getElementSignature, getFirstVisuallyVisibleAncestor, getFocusVisibilityInfo, getHeight, getHeightWithoutTransition, getInnerHeight, getInnerWidth, getKeyboardEventDefaultAction, getLuminance, getMarginSizes, getMaxHeight, getMaxWidth, getMinHeight, getMinWidth, getOpacity, getOpacityWithoutTransition, getPaddingSizes, getPositionedParent, getPositioningScrollOffset, getPreferedColorScheme, getScrollBox, getScrollContainer, getScrollContainerSet, getScrollIntoViewScopedOffsets, getScrollRelativeRect, getSelfAndAncestorScrolls, getStyle, getTranslateX, getTranslateXWithoutTransition, getTranslateY, getVirtualKeyboardOverlayHeight, getVisuallyVisibleInfo, getWidth, getWidthWithoutTransition, hasCSSSizeUnit, initFlexDetailsSet, initFocusGroup, initPositionSticky, isAncestorOpen, isDisplayedDespiteClosedAncestor, isPressDisputedByDrag, isPrimaryButtonEvent, isSameColor, isScrollable, isTouchDrivenEvent, keepTouchRefusable, markDragSource, measureLongestVisualLineWidth, measureScrollbar, measureWidestChildRow, mergeOneStyle, mergeTwoStyles, normalizeKeyboardKey, normalizeStyle, normalizeStyles, observeAncestorOpenState, onAncestorReopen, parsePositionArea, parseStyle, performTabNavigation, pickPositionRelativeTo, prefersDarkColors, prefersLightColors, preventFocusNav, preventFocusNavViaKeyboard, preventIntermediateScrollbar, releaseWheelGesture, resolveCSSColor, resolveCSSSize, resolveColorLuminance, resolveOklchLightness, scrollIntoViewScoped, scrollIntoViewThroughScrollables, scrollIntoViewWithStickyAwareness, scrollRoomTowards, setAttribute, setAttributes, setStyles, setVirtualKeyboardOverlaysContent, snapToPixel, startDragTo, startDragToResizeGesture, startDragToTravel, stickyAsRelativeCoords, stringifyStyle, subscribeVirtualKeyboardGeometryChange, subscribeVisualViewportResizeSettled, subscribeWindowResizeSettled, suppressClickAfterGesture, trapFocusInside, trapScrollInside, useActiveElement, useAvailableHeight, useAvailableWidth, useMaxHeight, useMaxWidth, useResizeStatus, visibleRectEffect, waitForPressHeld, watchWheelTravel, wheelGestureIsTakenFrom };
20506
+ export { EASING, ELEMENT_SIZE_CHANGE, activeElementSignal, addActiveElementEffect, addAttributeEffect, allowWheelThrough, appendStyles, applyNewPosition, canScroll, captureScrollState, chainEvent, claimWheelGesture, clickIsSuppressed, closestOpenableAncestor, contrastColor, createBackgroundColorTransition, createBackgroundTransition, createBorderRadiusTransition, createBorderTransition, createDragGestureController, createDragToMoveGestureController, createEventGroupLogger, createGroupTransitionController, createHeightTransition, createIterableWeakSet, createOpacityTransition, createPubSub, createStyleController, createTimelineTransition, createTransition, createTranslateXTransition, createValueEffect, createWidthTransition, cubicBezier, dispatchCustomEvent, dispatchInternalCustomEvent, dispatchPublicCustomEvent, dragAfterIntent, elementIsFocusable, elementIsVisibleForFocus, elementIsVisuallyVisible, findAfter, findAncestor, findBefore, findDescendant, findEvent, findFocusDelegateTarget, findFocusable, findSelfOrAncestorFixedPosition, formatEventSideEffect, getAncestorOpenType, getAvailableHeight, getAvailableWidth, getBackground, getBackgroundColor, getBorder, getBorderRadius, getBorderSizes, getContrastRatio, getDefaultStyles, getDragCoordinates, getDropTargetInfo, getElementSignature, getFirstVisuallyVisibleAncestor, getFocusVisibilityInfo, getHeight, getHeightWithoutTransition, getInnerHeight, getInnerWidth, getKeyboardEventDefaultAction, getLuminance, getMarginSizes, getMaxHeight, getMaxWidth, getMinHeight, getMinWidth, getOpacity, getOpacityWithoutTransition, getPaddingSizes, getPositionedParent, getPositioningScrollOffset, getPreferedColorScheme, getScrollBox, getScrollContainer, getScrollContainerSet, getScrollIntoViewScopedOffsets, getScrollRelativeRect, getSelfAndAncestorScrolls, getStyle, getTranslateX, getTranslateXWithoutTransition, getTranslateY, getVirtualKeyboardOverlayHeight, getVisuallyVisibleInfo, getWidth, getWidthWithoutTransition, hasCSSSizeUnit, initFlexDetailsSet, initFocusGroup, initPositionSticky, isAncestorOpen, isDisplayedDespiteClosedAncestor, isPressDisputedByDrag, isPrimaryButtonEvent, isSameColor, isScrollable, isTouchDrivenEvent, keepTouchRefusable, markDragSource, measureLongestVisualLineWidth, measureScrollbar, measureWidestChildRow, mergeOneStyle, mergeTwoStyles, normalizeKeyboardKey, normalizeStyle, normalizeStyles, observeAncestorOpenState, onAncestorReopen, parsePositionArea, parseStyle, performTabNavigation, pickPositionRelativeTo, prefersDarkColors, prefersLightColors, preventFocusNav, preventFocusNavViaKeyboard, preventIntermediateScrollbar, releaseWheelGesture, resolveCSSColor, resolveCSSSize, resolveColorLuminance, resolveOklchLightness, scrollIntoViewScoped, scrollIntoViewThroughScrollables, scrollIntoViewWithStickyAwareness, scrollRoomTowards, setAttribute, setAttributes, setPlacementViewportInsets, setStyles, setVirtualKeyboardOverlaysContent, snapToPixel, startDragTo, startDragToResizeGesture, startDragToTravel, stickyAsRelativeCoords, stringifyStyle, subscribeVirtualKeyboardGeometryChange, subscribeVisualViewportResizeSettled, subscribeWindowResizeSettled, suppressClickAfterGesture, trapFocusInside, trapScrollInside, useActiveElement, useAvailableHeight, useAvailableWidth, useMaxHeight, useMaxWidth, useResizeStatus, visibleRectEffect, waitForPressHeld, watchWheelTravel, wheelGestureIsTakenFrom };