@jsenv/dom 0.9.2 → 0.9.3

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 +293 -335
  2. package/package.json +1 -1
package/dist/jsenv_dom.js CHANGED
@@ -6325,23 +6325,52 @@ const getDragCoordinates = (
6325
6325
  return [leftRelativeToScrollContainer, topRelativeToScrollContainer];
6326
6326
  };
6327
6327
 
6328
- const installImportMetaCss = (importMeta) => {
6329
- const stylesheet = new CSSStyleSheet({ baseUrl: importMeta.url });
6328
+ const installImportMetaCssBuild = (importMeta) => {
6329
+ const IMPORT_META_CSS_BUILD = "jsenv_import_meta_css_build";
6330
6330
 
6331
- let called = false;
6332
- // eslint-disable-next-line accessor-pairs
6331
+ if (importMeta.css === IMPORT_META_CSS_BUILD) {
6332
+ return;
6333
+ }
6334
+
6335
+ const stylesheetMap = new Map();
6336
+ const adopt = (url, value) => {
6337
+ const stylesheet = new CSSStyleSheet({ baseUrl: importMeta.url });
6338
+ stylesheet.replaceSync(value);
6339
+ stylesheetMap.set(url, stylesheet);
6340
+ document.adoptedStyleSheets = [...document.adoptedStyleSheets, stylesheet];
6341
+ };
6342
+ const update = (url, value) => {
6343
+ stylesheetMap.get(url).replaceSync(value);
6344
+ };
6345
+ const remove = (url) => {
6346
+ const stylesheet = stylesheetMap.get(url);
6347
+ document.adoptedStyleSheets = document.adoptedStyleSheets.filter(
6348
+ (s) => s !== stylesheet,
6349
+ );
6350
+ stylesheetMap.delete(url);
6351
+ };
6352
+
6353
+ const currentCssSourceMap = new Map();
6333
6354
  Object.defineProperty(importMeta, "css", {
6334
6355
  configurable: true,
6335
- set(value) {
6336
- if (called) {
6337
- throw new Error("import.meta.css setter can only be called once");
6338
- }
6339
- called = true;
6340
- stylesheet.replaceSync(value);
6341
- document.adoptedStyleSheets = [
6342
- ...document.adoptedStyleSheets,
6343
- stylesheet,
6344
- ];
6356
+ get() {
6357
+ return IMPORT_META_CSS_BUILD;
6358
+ },
6359
+ set([value, url]) {
6360
+ if (value === undefined) {
6361
+ if (stylesheetMap.has(url)) {
6362
+ remove(url);
6363
+ currentCssSourceMap.delete(url);
6364
+ }
6365
+ return;
6366
+ }
6367
+ if (!stylesheetMap.has(url)) {
6368
+ adopt(url, value);
6369
+ currentCssSourceMap.set(url, value);
6370
+ } else if (currentCssSourceMap.get(url) !== value) {
6371
+ update(url, value);
6372
+ currentCssSourceMap.set(url, value);
6373
+ }
6345
6374
  },
6346
6375
  });
6347
6376
  };
@@ -6505,7 +6534,16 @@ const isolateInteractions = (elements) => {
6505
6534
  };
6506
6535
  };
6507
6536
 
6508
- installImportMetaCss(import.meta);
6537
+ installImportMetaCssBuild(import.meta);/**
6538
+ * Drag Gesture System
6539
+ *
6540
+ * TODO: rename moveX/moveY en juste x/y
6541
+ * puisque move c'est perturbant sachant que c'est drag + scroll
6542
+ * et que drag c'est juste la partie mouvement de la souris
6543
+ *
6544
+ * donc juste x/y ca seras surement mieux
6545
+ *
6546
+ */
6509
6547
  const createDragGestureController = (options = {}) => {
6510
6548
  const {
6511
6549
  name,
@@ -6514,17 +6552,18 @@ const createDragGestureController = (options = {}) => {
6514
6552
  onDrag,
6515
6553
  onRelease,
6516
6554
  threshold = 5,
6517
- direction: defaultDirection = { x: true, y: true },
6555
+ direction: defaultDirection = {
6556
+ x: true,
6557
+ y: true
6558
+ },
6518
6559
  documentInteractions = "auto",
6519
6560
  backdrop = true,
6520
- backdropZIndex = 999999,
6561
+ backdropZIndex = 999999
6521
6562
  } = options;
6522
-
6523
6563
  const dragGestureController = {
6524
6564
  grab: null,
6525
- gravViaPointer: null,
6565
+ gravViaPointer: null
6526
6566
  };
6527
-
6528
6567
  const grab = ({
6529
6568
  element,
6530
6569
  direction = defaultDirection,
@@ -6534,7 +6573,7 @@ const createDragGestureController = (options = {}) => {
6534
6573
  cursor = "grabbing",
6535
6574
  scrollContainer = document.documentElement,
6536
6575
  layoutScrollableLeft: scrollableLeftAtGrab = 0,
6537
- layoutScrollableTop: scrollableTopAtGrab = 0,
6576
+ layoutScrollableTop: scrollableTopAtGrab = 0
6538
6577
  } = {}) => {
6539
6578
  if (!element) {
6540
6579
  throw new Error("element is required");
@@ -6542,7 +6581,6 @@ const createDragGestureController = (options = {}) => {
6542
6581
  if (!direction.x && !direction.y) {
6543
6582
  return null;
6544
6583
  }
6545
-
6546
6584
  const [publishBeforeDrag, addBeforeDragCallback] = createPubSub();
6547
6585
  const [publishDrag, addDragCallback] = createPubSub();
6548
6586
  const [publishRelease, addReleaseCallback] = createPubSub();
@@ -6552,13 +6590,15 @@ const createDragGestureController = (options = {}) => {
6552
6590
  if (onRelease) {
6553
6591
  addReleaseCallback(onRelease);
6554
6592
  }
6555
-
6556
6593
  const scrollLeftAtGrab = scrollContainer.scrollLeft;
6557
6594
  const scrollTopAtGrab = scrollContainer.scrollTop;
6558
6595
  const leftAtGrab = scrollLeftAtGrab + scrollableLeftAtGrab;
6559
6596
  const topAtGrab = scrollTopAtGrab + scrollableTopAtGrab;
6560
6597
  const createLayout = (x, y) => {
6561
- const { scrollLeft, scrollTop } = scrollContainer;
6598
+ const {
6599
+ scrollLeft,
6600
+ scrollTop
6601
+ } = scrollContainer;
6562
6602
  const left = scrollableLeftAtGrab + x;
6563
6603
  const top = scrollableTopAtGrab + y;
6564
6604
  const scrollableLeft = left - scrollLeft;
@@ -6578,42 +6618,38 @@ const createDragGestureController = (options = {}) => {
6578
6618
  top,
6579
6619
  // Delta since grab (number representing how much we dragged)
6580
6620
  xDelta: left - leftAtGrab,
6581
- yDelta: top - topAtGrab,
6621
+ yDelta: top - topAtGrab
6582
6622
  };
6583
6623
  return layoutProps;
6584
6624
  };
6585
-
6586
- const grabLayout = createLayout(
6587
- grabX + scrollContainer.scrollLeft,
6588
- grabY + scrollContainer.scrollTop,
6589
- );
6625
+ const grabLayout = createLayout(grabX + scrollContainer.scrollLeft, grabY + scrollContainer.scrollTop);
6590
6626
  const gestureInfo = {
6591
6627
  name,
6592
6628
  direction,
6593
6629
  started: !threshold,
6594
6630
  status: "grabbed",
6595
-
6596
6631
  element,
6597
6632
  scrollContainer,
6598
- grabX, // x grab coordinate (excluding scroll)
6599
- grabY, // y grab coordinate (excluding scroll)
6633
+ grabX,
6634
+ // x grab coordinate (excluding scroll)
6635
+ grabY,
6636
+ // y grab coordinate (excluding scroll)
6600
6637
  grabLayout,
6601
6638
  leftAtGrab,
6602
6639
  topAtGrab,
6603
-
6604
- dragX: grabX, // coordinate of the last drag (excluding scroll of the scrollContainer)
6605
- dragY: grabY, // coordinate of the last drag (excluding scroll of the scrollContainer)
6640
+ dragX: grabX,
6641
+ // coordinate of the last drag (excluding scroll of the scrollContainer)
6642
+ dragY: grabY,
6643
+ // coordinate of the last drag (excluding scroll of the scrollContainer)
6606
6644
  layout: grabLayout,
6607
-
6608
6645
  isGoingUp: undefined,
6609
6646
  isGoingDown: undefined,
6610
6647
  isGoingLeft: undefined,
6611
6648
  isGoingRight: undefined,
6612
-
6613
6649
  // metadata about interaction sources
6614
6650
  grabEvent: event,
6615
6651
  dragEvent: null,
6616
- releaseEvent: null,
6652
+ releaseEvent: null
6617
6653
  };
6618
6654
  definePropertyAsReadOnly(gestureInfo, "name");
6619
6655
  definePropertyAsReadOnly(gestureInfo, "direction");
@@ -6624,7 +6660,6 @@ const createDragGestureController = (options = {}) => {
6624
6660
  definePropertyAsReadOnly(gestureInfo, "leftAtGrab");
6625
6661
  definePropertyAsReadOnly(gestureInfo, "topAtGrab");
6626
6662
  definePropertyAsReadOnly(gestureInfo, "grabEvent");
6627
-
6628
6663
  document_interactions: {
6629
6664
  if (documentInteractions === "manual") {
6630
6665
  break document_interactions;
@@ -6637,23 +6672,18 @@ const createDragGestureController = (options = {}) => {
6637
6672
  2. Break the visual feedback (inconsistent cursors, hover states)
6638
6673
  3. Cause unwanted scrolling (keyboard shortcuts, wheel events in restricted directions)
6639
6674
  4. Create accessibility issues (focus jumping, screen reader confusion)
6640
-
6641
- STRATEGY: Create a controlled interaction environment by:
6675
+ STRATEGY: Create a controlled interaction environment by:
6642
6676
  1. VISUAL CONTROL: Use a backdrop to unify cursor appearance and block pointer events
6643
6677
  2. INTERACTION ISOLATION: Make non-dragged elements inert to prevent interference
6644
6678
  3. FOCUS MANAGEMENT: Control focus location and prevent focus changes during drag
6645
6679
  4. SELECTIVE SCROLLING: Allow scrolling only in directions supported by the drag gesture
6646
-
6647
- IMPLEMENTATION:
6680
+ IMPLEMENTATION:
6648
6681
  */
6649
6682
 
6650
6683
  // 1. INTERACTION ISOLATION: Make everything except the dragged element inert
6651
6684
  // This prevents keyboard events, pointer interactions, and screen reader navigation
6652
6685
  // on non-relevant elements during the drag operation
6653
- const cleanupInert = isolateInteractions([
6654
- element,
6655
- ...Array.from(document.querySelectorAll("[data-droppable]")),
6656
- ]);
6686
+ const cleanupInert = isolateInteractions([element, ...Array.from(document.querySelectorAll("[data-droppable]"))]);
6657
6687
  addReleaseCallback(() => {
6658
6688
  cleanupInert();
6659
6689
  });
@@ -6670,14 +6700,14 @@ const createDragGestureController = (options = {}) => {
6670
6700
  // Handle wheel events on backdrop for directionally-constrained drag gestures
6671
6701
  // (e.g., table column resize should only allow horizontal scrolling)
6672
6702
  if (!direction.x || !direction.y) {
6673
- backdropElement.onwheel = (e) => {
6703
+ backdropElement.onwheel = e => {
6674
6704
  e.preventDefault();
6675
6705
  const scrollX = direction.x ? e.deltaX : 0;
6676
6706
  const scrollY = direction.y ? e.deltaY : 0;
6677
6707
  scrollContainer.scrollBy({
6678
6708
  left: scrollX,
6679
6709
  top: scrollY,
6680
- behavior: "auto",
6710
+ behavior: "auto"
6681
6711
  });
6682
6712
  };
6683
6713
  }
@@ -6688,23 +6718,25 @@ const createDragGestureController = (options = {}) => {
6688
6718
  }
6689
6719
 
6690
6720
  // 3. FOCUS MANAGEMENT: Control and stabilize focus during drag
6691
- const { activeElement } = document;
6721
+ const {
6722
+ activeElement
6723
+ } = document;
6692
6724
  const focusableElement = findFocusable(element);
6693
6725
  // Focus the dragged element (or document.body as fallback) to establish clear focus context
6694
6726
  // This also ensure any keydown event listened by the currently focused element
6695
6727
  // won't be available during drag
6696
6728
  const elementToFocus = focusableElement || document.body;
6697
6729
  elementToFocus.focus({
6698
- preventScroll: true,
6730
+ preventScroll: true
6699
6731
  });
6700
6732
  addReleaseCallback(() => {
6701
6733
  // Restore original focus on release
6702
6734
  activeElement.focus({
6703
- preventScroll: true,
6735
+ preventScroll: true
6704
6736
  });
6705
6737
  });
6706
6738
  // Prevent Tab navigation entirely (focus should stay stable)
6707
- const onkeydown = (e) => {
6739
+ const onkeydown = e => {
6708
6740
  if (e.key === "Tab") {
6709
6741
  e.preventDefault();
6710
6742
  return;
@@ -6717,27 +6749,16 @@ const createDragGestureController = (options = {}) => {
6717
6749
 
6718
6750
  // 4. SELECTIVE SCROLLING: Allow keyboard scrolling only in supported directions
6719
6751
  {
6720
- const onDocumentKeydown = (keyboardEvent) => {
6752
+ const onDocumentKeydown = keyboardEvent => {
6721
6753
  // Vertical scrolling keys - prevent if vertical movement not supported
6722
- if (
6723
- keyboardEvent.key === "ArrowUp" ||
6724
- keyboardEvent.key === "ArrowDown" ||
6725
- keyboardEvent.key === " " ||
6726
- keyboardEvent.key === "PageUp" ||
6727
- keyboardEvent.key === "PageDown" ||
6728
- keyboardEvent.key === "Home" ||
6729
- keyboardEvent.key === "End"
6730
- ) {
6754
+ if (keyboardEvent.key === "ArrowUp" || keyboardEvent.key === "ArrowDown" || keyboardEvent.key === " " || keyboardEvent.key === "PageUp" || keyboardEvent.key === "PageDown" || keyboardEvent.key === "Home" || keyboardEvent.key === "End") {
6731
6755
  if (!direction.y) {
6732
6756
  keyboardEvent.preventDefault();
6733
6757
  }
6734
6758
  return;
6735
6759
  }
6736
6760
  // Horizontal scrolling keys - prevent if horizontal movement not supported
6737
- if (
6738
- keyboardEvent.key === "ArrowLeft" ||
6739
- keyboardEvent.key === "ArrowRight"
6740
- ) {
6761
+ if (keyboardEvent.key === "ArrowLeft" || keyboardEvent.key === "ArrowRight") {
6741
6762
  if (!direction.x) {
6742
6763
  keyboardEvent.preventDefault();
6743
6764
  }
@@ -6754,36 +6775,38 @@ const createDragGestureController = (options = {}) => {
6754
6775
  // Set up scroll event handling to adjust drag position when scrolling occurs
6755
6776
  {
6756
6777
  let isHandlingScroll = false;
6757
- const handleScroll = (scrollEvent) => {
6778
+ const handleScroll = scrollEvent => {
6758
6779
  if (isHandlingScroll) {
6759
6780
  return;
6760
6781
  }
6761
6782
  isHandlingScroll = true;
6762
- drag(gestureInfo.dragX, gestureInfo.dragY, { event: scrollEvent });
6783
+ drag(gestureInfo.dragX, gestureInfo.dragY, {
6784
+ event: scrollEvent
6785
+ });
6763
6786
  isHandlingScroll = false;
6764
6787
  };
6765
- const scrollEventReceiver =
6766
- scrollContainer === document.documentElement
6767
- ? document
6768
- : scrollContainer;
6788
+ const scrollEventReceiver = scrollContainer === document.documentElement ? document : scrollContainer;
6769
6789
  scrollEventReceiver.addEventListener("scroll", handleScroll, {
6770
- passive: true,
6790
+ passive: true
6771
6791
  });
6772
6792
  addReleaseCallback(() => {
6773
6793
  scrollEventReceiver.removeEventListener("scroll", handleScroll, {
6774
- passive: true,
6794
+ passive: true
6775
6795
  });
6776
6796
  });
6777
6797
  }
6778
-
6779
6798
  const determineDragData = ({
6780
6799
  dragX,
6781
6800
  dragY,
6782
6801
  dragEvent,
6783
- isRelease = false,
6802
+ isRelease = false
6784
6803
  }) => {
6785
6804
  // === ÉTAT INITIAL (au moment du grab) ===
6786
- const { grabX, grabY, grabLayout } = gestureInfo;
6805
+ const {
6806
+ grabX,
6807
+ grabY,
6808
+ grabLayout
6809
+ } = gestureInfo;
6787
6810
  // === CE QUI EST DEMANDÉ (où on veut aller) ===
6788
6811
  // Calcul de la direction basé sur le mouvement précédent
6789
6812
  // (ne tient pas compte du mouvement final une fois les contraintes appliquées)
@@ -6795,56 +6818,38 @@ const createDragGestureController = (options = {}) => {
6795
6818
  const isGoingRight = dragX > currentDragX;
6796
6819
  const isGoingUp = dragY < currentDragY;
6797
6820
  const isGoingDown = dragY > currentDragY;
6798
-
6799
- const layoutXRequested = direction.x
6800
- ? scrollContainer.scrollLeft + (dragX - grabX)
6801
- : grabLayout.scrollLeft;
6802
- const layoutYRequested = direction.y
6803
- ? scrollContainer.scrollTop + (dragY - grabY)
6804
- : grabLayout.scrollTop;
6821
+ const layoutXRequested = direction.x ? scrollContainer.scrollLeft + (dragX - grabX) : grabLayout.scrollLeft;
6822
+ const layoutYRequested = direction.y ? scrollContainer.scrollTop + (dragY - grabY) : grabLayout.scrollTop;
6805
6823
  const layoutRequested = createLayout(layoutXRequested, layoutYRequested);
6806
6824
  const currentLayout = gestureInfo.layout;
6807
6825
  let layout;
6808
- if (
6809
- layoutRequested.x === currentLayout.x &&
6810
- layoutRequested.y === currentLayout.y
6811
- ) {
6826
+ if (layoutRequested.x === currentLayout.x && layoutRequested.y === currentLayout.y) {
6812
6827
  layout = currentLayout;
6813
6828
  } else {
6814
6829
  // === APPLICATION DES CONTRAINTES ===
6815
6830
  let layoutConstrained = layoutRequested;
6816
6831
  const limitLayout = (left, top) => {
6817
- layoutConstrained = createLayout(
6818
- left === undefined
6819
- ? layoutConstrained.x
6820
- : left - scrollableLeftAtGrab,
6821
- top === undefined ? layoutConstrained.y : top - scrollableTopAtGrab,
6822
- );
6832
+ layoutConstrained = createLayout(left === undefined ? layoutConstrained.x : left - scrollableLeftAtGrab, top === undefined ? layoutConstrained.y : top - scrollableTopAtGrab);
6823
6833
  };
6824
-
6825
6834
  publishBeforeDrag(layoutRequested, currentLayout, limitLayout, {
6826
6835
  dragEvent,
6827
- isRelease,
6836
+ isRelease
6828
6837
  });
6829
6838
  // === ÉTAT FINAL ===
6830
6839
  layout = layoutConstrained;
6831
6840
  }
6832
-
6833
6841
  const dragData = {
6834
6842
  dragX,
6835
6843
  dragY,
6836
6844
  layout,
6837
-
6838
6845
  isGoingLeft,
6839
6846
  isGoingRight,
6840
6847
  isGoingUp,
6841
6848
  isGoingDown,
6842
-
6843
6849
  status: isRelease ? "released" : "dragging",
6844
6850
  dragEvent: isRelease ? gestureInfo.dragEvent : dragEvent,
6845
- releaseEvent: isRelease ? dragEvent : null,
6851
+ releaseEvent: isRelease ? dragEvent : null
6846
6852
  };
6847
-
6848
6853
  if (isRelease) {
6849
6854
  return dragData;
6850
6855
  }
@@ -6869,18 +6874,19 @@ const createDragGestureController = (options = {}) => {
6869
6874
  }
6870
6875
  return dragData;
6871
6876
  };
6872
-
6873
- const drag = (
6874
- dragX = gestureInfo.dragX, // Scroll container relative X coordinate
6875
- dragY = gestureInfo.dragY, // Scroll container relative Y coordinate
6876
- { event = new CustomEvent("programmatic"), isRelease = false } = {},
6877
- ) => {
6878
-
6877
+ const drag = (dragX = gestureInfo.dragX,
6878
+ // Scroll container relative X coordinate
6879
+ dragY = gestureInfo.dragY,
6880
+ // Scroll container relative Y coordinate
6881
+ {
6882
+ event = new CustomEvent("programmatic"),
6883
+ isRelease = false
6884
+ } = {}) => {
6879
6885
  const dragData = determineDragData({
6880
6886
  dragX,
6881
6887
  dragY,
6882
6888
  dragEvent: event,
6883
- isRelease,
6889
+ isRelease
6884
6890
  });
6885
6891
  const startedPrevious = gestureInfo.started;
6886
6892
  const layoutPrevious = gestureInfo.layout;
@@ -6890,25 +6896,24 @@ const createDragGestureController = (options = {}) => {
6890
6896
  onDragStart?.(gestureInfo);
6891
6897
  }
6892
6898
  const someLayoutChange = gestureInfo.layout !== layoutPrevious;
6893
- publishDrag(
6894
- gestureInfo,
6895
- // we still publish drag event even when unchanged
6896
- // because UI might need to adjust when document scrolls
6897
- // even if nothing truly changes visually the element
6898
- // can decide to stick to the scroll for example
6899
- someLayoutChange,
6900
- );
6899
+ publishDrag(gestureInfo,
6900
+ // we still publish drag event even when unchanged
6901
+ // because UI might need to adjust when document scrolls
6902
+ // even if nothing truly changes visually the element
6903
+ // can decide to stick to the scroll for example
6904
+ someLayoutChange);
6901
6905
  };
6902
-
6903
6906
  const release = ({
6904
6907
  event = new CustomEvent("programmatic"),
6905
6908
  releaseX = gestureInfo.dragX,
6906
- releaseY = gestureInfo.dragY,
6909
+ releaseY = gestureInfo.dragY
6907
6910
  } = {}) => {
6908
- drag(releaseX, releaseY, { event, isRelease: true });
6911
+ drag(releaseX, releaseY, {
6912
+ event,
6913
+ isRelease: true
6914
+ });
6909
6915
  publishRelease(gestureInfo);
6910
6916
  };
6911
-
6912
6917
  onGrab?.(gestureInfo);
6913
6918
  const dragGesture = {
6914
6919
  gestureInfo,
@@ -6916,12 +6921,11 @@ const createDragGestureController = (options = {}) => {
6916
6921
  addDragCallback,
6917
6922
  addReleaseCallback,
6918
6923
  drag,
6919
- release,
6924
+ release
6920
6925
  };
6921
6926
  return dragGesture;
6922
6927
  };
6923
6928
  dragGestureController.grab = grab;
6924
-
6925
6929
  const initDragByPointer = (grabEvent, dragOptions, initializer) => {
6926
6930
  if (grabEvent.button !== undefined && grabEvent.button !== 0) {
6927
6931
  return null;
@@ -6931,8 +6935,11 @@ const createDragGestureController = (options = {}) => {
6931
6935
  // target is a text node
6932
6936
  return null;
6933
6937
  }
6934
- const mouseEventCoords = (mouseEvent) => {
6935
- const { clientX, clientY } = mouseEvent;
6938
+ const mouseEventCoords = mouseEvent => {
6939
+ const {
6940
+ clientX,
6941
+ clientY
6942
+ } = mouseEvent;
6936
6943
  return [clientX, clientY];
6937
6944
  };
6938
6945
  const [grabX, grabY] = mouseEventCoords(grabEvent);
@@ -6940,37 +6947,39 @@ const createDragGestureController = (options = {}) => {
6940
6947
  grabX,
6941
6948
  grabY,
6942
6949
  event: grabEvent,
6943
- ...dragOptions,
6950
+ ...dragOptions
6944
6951
  });
6945
- const dragViaPointer = (dragEvent) => {
6952
+ const dragViaPointer = dragEvent => {
6946
6953
  const [mouseDragX, mouseDragY] = mouseEventCoords(dragEvent);
6947
6954
  dragGesture.drag(mouseDragX, mouseDragY, {
6948
- event: dragEvent,
6955
+ event: dragEvent
6949
6956
  });
6950
6957
  };
6951
- const releaseViaPointer = (mouseupEvent) => {
6958
+ const releaseViaPointer = mouseupEvent => {
6952
6959
  const [mouseReleaseX, mouseReleaseY] = mouseEventCoords(mouseupEvent);
6953
6960
  dragGesture.release({
6954
6961
  event: mouseupEvent,
6955
6962
  releaseX: mouseReleaseX,
6956
- releaseY: mouseReleaseY,
6963
+ releaseY: mouseReleaseY
6957
6964
  });
6958
6965
  };
6959
6966
  dragGesture.dragViaPointer = dragViaPointer;
6960
6967
  dragGesture.releaseViaPointer = releaseViaPointer;
6961
6968
  const cleanup = initializer({
6962
6969
  onMove: dragViaPointer,
6963
- onRelease: releaseViaPointer,
6970
+ onRelease: releaseViaPointer
6964
6971
  });
6965
6972
  dragGesture.addReleaseCallback(() => {
6966
6973
  cleanup();
6967
6974
  });
6968
6975
  return dragGesture;
6969
6976
  };
6970
-
6971
6977
  const grabViaPointer = (grabEvent, options) => {
6972
6978
  if (grabEvent.type === "pointerdown") {
6973
- return initDragByPointer(grabEvent, options, ({ onMove, onRelease }) => {
6979
+ return initDragByPointer(grabEvent, options, ({
6980
+ onMove,
6981
+ onRelease
6982
+ }) => {
6974
6983
  const target = grabEvent.target;
6975
6984
  target.setPointerCapture(grabEvent.pointerId);
6976
6985
  target.addEventListener("lostpointercapture", onRelease);
@@ -6987,11 +6996,12 @@ const createDragGestureController = (options = {}) => {
6987
6996
  });
6988
6997
  }
6989
6998
  if (grabEvent.type === "mousedown") {
6990
- console.warn(
6991
- `Received "mousedown" event, "pointerdown" events are recommended to perform drag gestures.`,
6992
- );
6993
- return initDragByPointer(grabEvent, options, ({ onMove, onRelease }) => {
6994
- const onPointerUp = (pointerEvent) => {
6999
+ console.warn(`Received "mousedown" event, "pointerdown" events are recommended to perform drag gestures.`);
7000
+ return initDragByPointer(grabEvent, options, ({
7001
+ onMove,
7002
+ onRelease
7003
+ }) => {
7004
+ const onPointerUp = pointerEvent => {
6995
7005
  // <button disabled> for example does not emit mouseup if we release mouse over it
6996
7006
  // -> we add "pointerup" to catch mouseup occuring on disabled element
6997
7007
  if (pointerEvent.pointerType === "mouse") {
@@ -7008,54 +7018,43 @@ const createDragGestureController = (options = {}) => {
7008
7018
  };
7009
7019
  });
7010
7020
  }
7011
- throw new Error(
7012
- `Unsupported "${grabEvent.type}" evenet passed to grabViaPointer. "pointerdown" was expected.`,
7013
- );
7021
+ throw new Error(`Unsupported "${grabEvent.type}" evenet passed to grabViaPointer. "pointerdown" was expected.`);
7014
7022
  };
7015
7023
  dragGestureController.grabViaPointer = grabViaPointer;
7016
-
7017
7024
  return dragGestureController;
7018
7025
  };
7019
-
7020
- const dragAfterThreshold = (
7021
- grabEvent,
7022
- dragGestureInitializer,
7023
- threshold,
7024
- ) => {
7026
+ const dragAfterThreshold = (grabEvent, dragGestureInitializer, threshold) => {
7025
7027
  const significantDragGestureController = createDragGestureController({
7026
7028
  threshold,
7027
7029
  // allow interaction for this intermediate gesture:
7028
7030
  // user should still be able to scroll or interact with the document
7029
7031
  // only once the gesture is significant we take control
7030
7032
  documentInteractions: "manual",
7031
- onDragStart: (gestureInfo) => {
7033
+ onDragStart: gestureInfo => {
7032
7034
  significantDragGesture.release(); // kill that gesture
7033
7035
  const dragGesture = dragGestureInitializer();
7034
7036
  dragGesture.dragViaPointer(gestureInfo.dragEvent);
7035
- },
7037
+ }
7038
+ });
7039
+ const significantDragGesture = significantDragGestureController.grabViaPointer(grabEvent, {
7040
+ element: grabEvent.target
7036
7041
  });
7037
- const significantDragGesture =
7038
- significantDragGestureController.grabViaPointer(grabEvent, {
7039
- element: grabEvent.target,
7040
- });
7041
7042
  };
7042
-
7043
7043
  const definePropertyAsReadOnly = (object, propertyName) => {
7044
7044
  Object.defineProperty(object, propertyName, {
7045
7045
  writable: false,
7046
- value: object[propertyName],
7046
+ value: object[propertyName]
7047
7047
  });
7048
7048
  };
7049
-
7050
- import.meta.css = /* css */ `
7049
+ import.meta.css = [/* css */`
7051
7050
  .navi_drag_gesture_backdrop {
7052
7051
  position: fixed;
7053
7052
  inset: 0;
7054
7053
  user-select: none;
7055
7054
  }
7056
- `;
7055
+ `, "/src/interaction/drag/drag_gesture.js"];
7057
7056
 
7058
- installImportMetaCss(import.meta);const setupConstraintFeedbackLine = () => {
7057
+ installImportMetaCssBuild(import.meta);const setupConstraintFeedbackLine = () => {
7059
7058
  const constraintFeedbackLine = createConstraintFeedbackLine();
7060
7059
 
7061
7060
  // Track last known mouse position for constraint feedback line during scroll
@@ -7063,18 +7062,17 @@ installImportMetaCss(import.meta);const setupConstraintFeedbackLine = () => {
7063
7062
  let lastMouseY = null;
7064
7063
 
7065
7064
  // Internal function to update constraint feedback line
7066
- const onDrag = (gestureInfo) => {
7067
- const { grabEvent, dragEvent } = gestureInfo;
7068
- if (
7069
- grabEvent.type === "programmatic" ||
7070
- // dragEvent can be null when only mousedown without yet any mousemove
7071
- !dragEvent ||
7072
- dragEvent.type === "programmatic"
7073
- ) {
7065
+ const onDrag = gestureInfo => {
7066
+ const {
7067
+ grabEvent,
7068
+ dragEvent
7069
+ } = gestureInfo;
7070
+ if (grabEvent.type === "programmatic" ||
7071
+ // dragEvent can be null when only mousedown without yet any mousemove
7072
+ !dragEvent || dragEvent.type === "programmatic") {
7074
7073
  // programmatic drag
7075
7074
  return;
7076
7075
  }
7077
-
7078
7076
  const mouseX = dragEvent.clientX;
7079
7077
  const mouseY = dragEvent.clientY;
7080
7078
  // Use last known position if current position not available (e.g., during scroll)
@@ -7087,7 +7085,6 @@ installImportMetaCss(import.meta);const setupConstraintFeedbackLine = () => {
7087
7085
  // Store current mouse position for potential use during scroll
7088
7086
  lastMouseX = mouseX;
7089
7087
  lastMouseY = mouseY;
7090
-
7091
7088
  const grabClientX = grabEvent.clientX;
7092
7089
  const grabClientY = grabEvent.clientY;
7093
7090
 
@@ -7116,25 +7113,21 @@ installImportMetaCss(import.meta);const setupConstraintFeedbackLine = () => {
7116
7113
  constraintFeedbackLine.style.opacity = `${maxOpacity * opacityFactor}`;
7117
7114
  constraintFeedbackLine.setAttribute("data-visible", "");
7118
7115
  };
7119
-
7120
7116
  return {
7121
7117
  onDrag,
7122
7118
  onRelease: () => {
7123
7119
  constraintFeedbackLine.remove();
7124
- },
7120
+ }
7125
7121
  };
7126
7122
  };
7127
-
7128
7123
  const createConstraintFeedbackLine = () => {
7129
7124
  const line = document.createElement("div");
7130
7125
  line.className = "navi_constraint_feedback_line";
7131
- line.title =
7132
- "Constraint feedback - shows distance between mouse and moving grab point";
7126
+ line.title = "Constraint feedback - shows distance between mouse and moving grab point";
7133
7127
  document.body.appendChild(line);
7134
7128
  return line;
7135
7129
  };
7136
-
7137
- import.meta.css = /* css */ `
7130
+ import.meta.css = [/* css */`
7138
7131
  .navi_constraint_feedback_line {
7139
7132
  position: fixed;
7140
7133
  z-index: 9998;
@@ -7148,16 +7141,17 @@ import.meta.css = /* css */ `
7148
7141
  .navi_constraint_feedback_line[data-visible] {
7149
7142
  visibility: visible;
7150
7143
  }
7151
- `;
7152
-
7153
- installImportMetaCss(import.meta);const MARKER_SIZE = 12;
7144
+ `, "/src/interaction/drag/constraint_feedback_line.js"];
7154
7145
 
7146
+ installImportMetaCssBuild(import.meta);// Keep visual markers (debug markers, obstacle markers, constraint feedback line) in DOM after drag ends
7147
+ const MARKER_SIZE = 12;
7155
7148
  let currentDebugMarkers = [];
7156
7149
  let currentConstraintMarkers = [];
7157
7150
  let currentReferenceElementMarker = null;
7158
7151
  let currentElementMarker = null;
7159
-
7160
- const setupDragDebugMarkers = (dragGesture, { referenceElement }) => {
7152
+ const setupDragDebugMarkers = (dragGesture, {
7153
+ referenceElement
7154
+ }) => {
7161
7155
  // Clean up any existing persistent markers from previous drag gestures
7162
7156
  {
7163
7157
  // Remove any existing markers from previous gestures
@@ -7166,29 +7160,27 @@ const setupDragDebugMarkers = (dragGesture, { referenceElement }) => {
7166
7160
  container.innerHTML = ""; // Clear all markers efficiently
7167
7161
  }
7168
7162
  }
7169
-
7170
- const { direction, scrollContainer } = dragGesture.gestureInfo;
7171
-
7163
+ const {
7164
+ direction,
7165
+ scrollContainer
7166
+ } = dragGesture.gestureInfo;
7172
7167
  return {
7173
- onConstraints: (
7174
- constraints,
7175
- { left, top, right, bottom, autoScrollArea },
7176
- ) => {
7168
+ onConstraints: (constraints, {
7169
+ left,
7170
+ top,
7171
+ right,
7172
+ bottom,
7173
+ autoScrollArea
7174
+ }) => {
7177
7175
  // Schedule removal of previous markers if they exist
7178
7176
  const previousDebugMarkers = [...currentDebugMarkers];
7179
7177
  const previousConstraintMarkers = [...currentConstraintMarkers];
7180
7178
  const previousReferenceElementMarker = currentReferenceElementMarker;
7181
7179
  const previousElementMarker = currentElementMarker;
7182
-
7183
- if (
7184
- previousDebugMarkers.length > 0 ||
7185
- previousConstraintMarkers.length > 0 ||
7186
- previousReferenceElementMarker ||
7187
- previousElementMarker
7188
- ) {
7180
+ if (previousDebugMarkers.length > 0 || previousConstraintMarkers.length > 0 || previousReferenceElementMarker || previousElementMarker) {
7189
7181
  setTimeout(() => {
7190
- previousDebugMarkers.forEach((marker) => marker.remove());
7191
- previousConstraintMarkers.forEach((marker) => marker.remove());
7182
+ previousDebugMarkers.forEach(marker => marker.remove());
7183
+ previousConstraintMarkers.forEach(marker => marker.remove());
7192
7184
  if (previousReferenceElementMarker) {
7193
7185
  previousReferenceElementMarker.remove();
7194
7186
  }
@@ -7217,7 +7209,7 @@ const setupDragDebugMarkers = (dragGesture, { referenceElement }) => {
7217
7209
  bottom,
7218
7210
  scrollContainer,
7219
7211
  label: elementLabel,
7220
- color: elementColor,
7212
+ color: elementColor
7221
7213
  });
7222
7214
 
7223
7215
  // Create reference element marker if reference element exists
@@ -7227,52 +7219,47 @@ const setupDragDebugMarkers = (dragGesture, { referenceElement }) => {
7227
7219
  top,
7228
7220
  right,
7229
7221
  bottom,
7230
- scrollContainer,
7222
+ scrollContainer
7231
7223
  });
7232
7224
  }
7233
7225
 
7234
7226
  // Collect all markers to be created, then merge duplicates
7235
7227
  const markersToCreate = [];
7236
-
7237
7228
  {
7238
7229
  if (direction.x) {
7239
7230
  markersToCreate.push({
7240
- name: autoScrollArea.paddingLeft
7241
- ? `autoscroll.left + padding(${autoScrollArea.paddingLeft})`
7242
- : "autoscroll.left",
7231
+ name: autoScrollArea.paddingLeft ? `autoscroll.left + padding(${autoScrollArea.paddingLeft})` : "autoscroll.left",
7243
7232
  x: autoScrollArea.left,
7244
7233
  y: 0,
7245
- color: "0 128 0", // green
7246
- side: "left",
7234
+ color: "0 128 0",
7235
+ // green
7236
+ side: "left"
7247
7237
  });
7248
7238
  markersToCreate.push({
7249
- name: autoScrollArea.paddingRight
7250
- ? `autoscroll.right + padding(${autoScrollArea.paddingRight})`
7251
- : "autoscroll.right",
7239
+ name: autoScrollArea.paddingRight ? `autoscroll.right + padding(${autoScrollArea.paddingRight})` : "autoscroll.right",
7252
7240
  x: autoScrollArea.right,
7253
7241
  y: 0,
7254
- color: "0 128 0", // green
7255
- side: "right",
7242
+ color: "0 128 0",
7243
+ // green
7244
+ side: "right"
7256
7245
  });
7257
7246
  }
7258
7247
  if (direction.y) {
7259
7248
  markersToCreate.push({
7260
- name: autoScrollArea.paddingTop
7261
- ? `autoscroll.top + padding(${autoScrollArea.paddingTop})`
7262
- : "autoscroll.top",
7249
+ name: autoScrollArea.paddingTop ? `autoscroll.top + padding(${autoScrollArea.paddingTop})` : "autoscroll.top",
7263
7250
  x: 0,
7264
7251
  y: autoScrollArea.top,
7265
- color: "255 0 0", // red
7266
- side: "top",
7252
+ color: "255 0 0",
7253
+ // red
7254
+ side: "top"
7267
7255
  });
7268
7256
  markersToCreate.push({
7269
- name: autoScrollArea.paddingBottom
7270
- ? `autoscroll.bottom + padding(${autoScrollArea.paddingBottom})`
7271
- : "autoscroll.bottom",
7257
+ name: autoScrollArea.paddingBottom ? `autoscroll.bottom + padding(${autoScrollArea.paddingBottom})` : "autoscroll.bottom",
7272
7258
  x: 0,
7273
7259
  y: autoScrollArea.bottom,
7274
- color: "255 165 0", // orange
7275
- side: "bottom",
7260
+ color: "255 165 0",
7261
+ // orange
7262
+ side: "bottom"
7276
7263
  });
7277
7264
  }
7278
7265
  }
@@ -7280,7 +7267,9 @@ const setupDragDebugMarkers = (dragGesture, { referenceElement }) => {
7280
7267
  // Process each constraint individually to preserve names
7281
7268
  for (const constraint of constraints) {
7282
7269
  if (constraint.type === "bounds") {
7283
- const { bounds } = constraint;
7270
+ const {
7271
+ bounds
7272
+ } = constraint;
7284
7273
 
7285
7274
  // Create individual markers for each bound with constraint name
7286
7275
  if (direction.x) {
@@ -7289,8 +7278,9 @@ const setupDragDebugMarkers = (dragGesture, { referenceElement }) => {
7289
7278
  name: `${constraint.name}.left`,
7290
7279
  x: bounds.left,
7291
7280
  y: 0,
7292
- color: "128 0 128", // purple
7293
- side: "left",
7281
+ color: "128 0 128",
7282
+ // purple
7283
+ side: "left"
7294
7284
  });
7295
7285
  }
7296
7286
  if (bounds.right !== undefined) {
@@ -7300,8 +7290,9 @@ const setupDragDebugMarkers = (dragGesture, { referenceElement }) => {
7300
7290
  name: `${constraint.name}.right`,
7301
7291
  x: bounds.right,
7302
7292
  y: 0,
7303
- color: "128 0 128", // purple
7304
- side: "right",
7293
+ color: "128 0 128",
7294
+ // purple
7295
+ side: "right"
7305
7296
  });
7306
7297
  }
7307
7298
  }
@@ -7311,8 +7302,9 @@ const setupDragDebugMarkers = (dragGesture, { referenceElement }) => {
7311
7302
  name: `${constraint.name}.top`,
7312
7303
  x: 0,
7313
7304
  y: bounds.top,
7314
- color: "128 0 128", // purple
7315
- side: "top",
7305
+ color: "128 0 128",
7306
+ // purple
7307
+ side: "top"
7316
7308
  });
7317
7309
  }
7318
7310
  if (bounds.bottom !== undefined) {
@@ -7322,37 +7314,28 @@ const setupDragDebugMarkers = (dragGesture, { referenceElement }) => {
7322
7314
  name: `${constraint.name}.bottom`,
7323
7315
  x: 0,
7324
7316
  y: bounds.bottom,
7325
- color: "128 0 128", // purple
7326
- side: "bottom",
7317
+ color: "128 0 128",
7318
+ // purple
7319
+ side: "bottom"
7327
7320
  });
7328
7321
  }
7329
7322
  }
7330
7323
  } else if (constraint.type === "obstacle") {
7331
- const obstacleMarker = createObstacleMarker(
7332
- constraint,
7333
- scrollContainer,
7334
- );
7324
+ const obstacleMarker = createObstacleMarker(constraint, scrollContainer);
7335
7325
  currentConstraintMarkers.push(obstacleMarker);
7336
7326
  }
7337
7327
  }
7338
7328
 
7339
7329
  // Create markers with merging for overlapping positions
7340
- const createdMarkers = createMergedMarkers(
7341
- markersToCreate,
7342
- scrollContainer,
7343
- );
7344
- currentDebugMarkers.push(
7345
- ...createdMarkers.filter((m) => m.type !== "constraint"),
7346
- );
7347
- currentConstraintMarkers.push(
7348
- ...createdMarkers.filter((m) => m.type === "constraint"),
7349
- );
7330
+ const createdMarkers = createMergedMarkers(markersToCreate, scrollContainer);
7331
+ currentDebugMarkers.push(...createdMarkers.filter(m => m.type !== "constraint"));
7332
+ currentConstraintMarkers.push(...createdMarkers.filter(m => m.type === "constraint"));
7350
7333
  },
7351
7334
  onRelease: () => {
7352
7335
  {
7353
7336
  return;
7354
7337
  }
7355
- },
7338
+ }
7356
7339
  };
7357
7340
  };
7358
7341
 
@@ -7371,8 +7354,9 @@ const getMarkersContainer = () => {
7371
7354
  // Convert document-relative coordinates to viewport coordinates for marker positioning
7372
7355
  // Takes the scroll container into account for proper positioning relative to the container
7373
7356
  const getDebugMarkerPos = (x, y, scrollContainer, side = null) => {
7374
- const { documentElement } = document;
7375
-
7357
+ const {
7358
+ documentElement
7359
+ } = document;
7376
7360
  const leftWithoutScroll = x - scrollContainer.scrollLeft;
7377
7361
  const topWithoutScroll = y - scrollContainer.scrollTop;
7378
7362
  let baseX;
@@ -7404,7 +7388,6 @@ const getDebugMarkerPos = (x, y, scrollContainer, side = null) => {
7404
7388
  // For obstacles and other markers: use converted coordinates directly
7405
7389
  return [baseX, baseY];
7406
7390
  };
7407
-
7408
7391
  const createMergedMarkers = (markersToCreate, scrollContainer) => {
7409
7392
  const mergedMarkers = [];
7410
7393
  const positionMap = new Map();
@@ -7412,7 +7395,6 @@ const createMergedMarkers = (markersToCreate, scrollContainer) => {
7412
7395
  // Group markers by position and side
7413
7396
  for (const marker of markersToCreate) {
7414
7397
  const key = `${marker.x},${marker.y},${marker.side}`;
7415
-
7416
7398
  if (!positionMap.has(key)) {
7417
7399
  positionMap.set(key, []);
7418
7400
  }
@@ -7430,43 +7412,36 @@ const createMergedMarkers = (markersToCreate, scrollContainer) => {
7430
7412
  } else {
7431
7413
  // Multiple markers at same position - merge labels
7432
7414
  const firstMarker = markers[0];
7433
- const combinedName = markers.map((m) => m.name).join(" + ");
7415
+ const combinedName = markers.map(m => m.name).join(" + ");
7434
7416
 
7435
7417
  // Use the first marker's color, or mix colors if needed
7436
- const domMarker = createDebugMarker(
7437
- {
7438
- ...firstMarker,
7439
- name: combinedName,
7440
- },
7441
- scrollContainer,
7442
- );
7443
- domMarker.type = markers.some((m) => m.name.includes("Bound"))
7444
- ? "constraint"
7445
- : "visible";
7418
+ const domMarker = createDebugMarker({
7419
+ ...firstMarker,
7420
+ name: combinedName
7421
+ }, scrollContainer);
7422
+ domMarker.type = markers.some(m => m.name.includes("Bound")) ? "constraint" : "visible";
7446
7423
  mergedMarkers.push(domMarker);
7447
7424
  }
7448
7425
  }
7449
-
7450
7426
  return mergedMarkers;
7451
7427
  };
7452
-
7453
- const createDebugMarker = (
7454
- { name, x, y, color = "255 0 0", side },
7455
- scrollContainer,
7456
- ) => {
7428
+ const createDebugMarker = ({
7429
+ name,
7430
+ x,
7431
+ y,
7432
+ color = "255 0 0",
7433
+ side
7434
+ }, scrollContainer) => {
7457
7435
  // Convert coordinates from document-relative to viewport
7458
7436
  const [viewportX, viewportY] = getDebugMarkerPos(x, y, scrollContainer, side);
7459
-
7460
7437
  const marker = document.createElement("div");
7461
7438
  marker.className = `navi_debug_marker`;
7462
7439
  marker.setAttribute(`data-${side}`, "");
7463
7440
  // Set the color as a CSS custom property
7464
7441
  marker.style.setProperty("--marker-color", `rgb(${color})`);
7465
7442
  // Position markers exactly at the boundary coordinates
7466
- marker.style.left =
7467
- side === "right" ? `${viewportX - MARKER_SIZE}px` : `${viewportX}px`;
7468
- marker.style.top =
7469
- side === "bottom" ? `${viewportY - MARKER_SIZE}px` : `${viewportY}px`;
7443
+ marker.style.left = side === "right" ? `${viewportX - MARKER_SIZE}px` : `${viewportX}px`;
7444
+ marker.style.top = side === "bottom" ? `${viewportY - MARKER_SIZE}px` : `${viewportY}px`;
7470
7445
  marker.title = name;
7471
7446
 
7472
7447
  // Add label
@@ -7474,7 +7449,6 @@ const createDebugMarker = (
7474
7449
  label.className = `navi_debug_marker_label`;
7475
7450
  label.textContent = name;
7476
7451
  marker.appendChild(label);
7477
-
7478
7452
  const container = getMarkersContainer();
7479
7453
  container.appendChild(marker);
7480
7454
  return marker;
@@ -7484,13 +7458,7 @@ const createObstacleMarker = (obstacleObj, scrollContainer) => {
7484
7458
  const height = obstacleObj.bounds.bottom - obstacleObj.bounds.top;
7485
7459
 
7486
7460
  // Convert document-relative coordinates to viewport coordinates
7487
- const [x, y] = getDebugMarkerPos(
7488
- obstacleObj.bounds.left,
7489
- obstacleObj.bounds.top,
7490
- scrollContainer,
7491
- "obstacle",
7492
- );
7493
-
7461
+ const [x, y] = getDebugMarkerPos(obstacleObj.bounds.left, obstacleObj.bounds.top, scrollContainer, "obstacle");
7494
7462
  const marker = document.createElement("div");
7495
7463
  marker.className = "navi_obstacle_marker";
7496
7464
  marker.style.left = `${x}px`;
@@ -7504,12 +7472,10 @@ const createObstacleMarker = (obstacleObj, scrollContainer) => {
7504
7472
  label.className = "navi_obstacle_marker_label";
7505
7473
  label.textContent = obstacleObj.name;
7506
7474
  marker.appendChild(label);
7507
-
7508
7475
  const container = getMarkersContainer();
7509
7476
  container.appendChild(marker);
7510
7477
  return marker;
7511
7478
  };
7512
-
7513
7479
  const createElementMarker = ({
7514
7480
  left,
7515
7481
  top,
@@ -7517,13 +7483,12 @@ const createElementMarker = ({
7517
7483
  bottom,
7518
7484
  scrollContainer,
7519
7485
  label = "Element",
7520
- color = "0, 200, 0", // Default green color
7486
+ color = "0, 200, 0" // Default green color
7521
7487
  }) => {
7522
7488
  const width = right - left;
7523
7489
  const height = bottom - top;
7524
7490
  // Convert document-relative coordinates to viewport coordinates
7525
7491
  const [x, y] = getDebugMarkerPos(left, top, scrollContainer, "element");
7526
-
7527
7492
  const marker = document.createElement("div");
7528
7493
  marker.className = "navi_element_marker";
7529
7494
  marker.style.left = `${x}px`;
@@ -7541,24 +7506,21 @@ const createElementMarker = ({
7541
7506
  labelEl.className = "navi_element_marker_label";
7542
7507
  labelEl.textContent = label;
7543
7508
  marker.appendChild(labelEl);
7544
-
7545
7509
  const container = getMarkersContainer();
7546
7510
  container.appendChild(marker);
7547
7511
  return marker;
7548
7512
  };
7549
-
7550
7513
  const createReferenceElementMarker = ({
7551
7514
  left,
7552
7515
  top,
7553
7516
  right,
7554
7517
  bottom,
7555
- scrollContainer,
7518
+ scrollContainer
7556
7519
  }) => {
7557
7520
  const width = right - left;
7558
7521
  const height = bottom - top;
7559
7522
  // Convert document-relative coordinates to viewport coordinates
7560
7523
  const [x, y] = getDebugMarkerPos(left, top, scrollContainer, "reference");
7561
-
7562
7524
  const marker = document.createElement("div");
7563
7525
  marker.className = "navi_reference_element_marker";
7564
7526
  marker.style.left = `${x}px`;
@@ -7572,13 +7534,11 @@ const createReferenceElementMarker = ({
7572
7534
  label.className = "navi_reference_element_marker_label";
7573
7535
  label.textContent = "Reference Element";
7574
7536
  marker.appendChild(label);
7575
-
7576
7537
  const container = getMarkersContainer();
7577
7538
  container.appendChild(marker);
7578
7539
  return marker;
7579
7540
  };
7580
-
7581
- import.meta.css = /* css */ `
7541
+ import.meta.css = [/* css */`
7582
7542
  .navi_debug_markers_container {
7583
7543
  position: fixed;
7584
7544
  top: 0;
@@ -7763,7 +7723,7 @@ import.meta.css = /* css */ `
7763
7723
  border-radius: 3px;
7764
7724
  pointer-events: none;
7765
7725
  }
7766
- `;
7726
+ `, "/src/interaction/drag/drag_debug_markers.js"];
7767
7727
 
7768
7728
  const initDragConstraints = (
7769
7729
  dragGesture,
@@ -8927,17 +8887,37 @@ const getWidth = (element) => {
8927
8887
  return width;
8928
8888
  };
8929
8889
 
8930
- installImportMetaCss(import.meta);
8931
- import.meta.css = /* css */ `
8890
+ installImportMetaCssBuild(import.meta);/**
8891
+ * Position Sticky Polyfill
8892
+ *
8893
+ * This module provides a workaround for position:sticky limitations when used with
8894
+ * overflow:auto/hidden parent elements (see https://github.com/w3c/csswg-drafts/issues/865).
8895
+ *
8896
+ * How it works:
8897
+ * 1. Creates a placeholder clone of the sticky element to maintain document flow
8898
+ * 2. Positions the real element using fixed positioning relative to viewport
8899
+ * 3. Adjusts position on scroll to emulate position:sticky behavior
8900
+ * 4. Handles parent boundary detection to keep element within its container
8901
+ * 5. Updates dimensions on resize and DOM changes
8902
+ *
8903
+ * Usage:
8904
+ * ```
8905
+ * const cleanup = initPositionSticky(element);
8906
+ * // Later when no longer needed
8907
+ * cleanup();
8908
+ * ```
8909
+ *
8910
+ * The element should have a CSS "top" value specified (e.g., top: 10px).
8911
+ */
8912
+ import.meta.css = [/* css */`
8932
8913
  [data-position-sticky-placeholder] {
8933
8914
  position: static !important;
8934
8915
  width: auto !important;
8935
8916
  height: auto !important;
8936
8917
  opacity: 0 !important;
8937
8918
  }
8938
- `;
8939
-
8940
- const initPositionSticky = (element) => {
8919
+ `, "/src/position/position_sticky.js"];
8920
+ const initPositionSticky = element => {
8941
8921
  const computedStyle = getComputedStyle(element);
8942
8922
  const topCssValue = computedStyle.top;
8943
8923
  const top = parseFloat(topCssValue);
@@ -8961,20 +8941,10 @@ const initPositionSticky = (element) => {
8961
8941
  break;
8962
8942
  }
8963
8943
  const style = getComputedStyle(scrollContainer);
8964
- if (
8965
- xScrollContainer === null &&
8966
- (style.overflowX === "auto" ||
8967
- style.overflowX === "hidden" ||
8968
- style.overflowX === "scroll")
8969
- ) {
8944
+ if (xScrollContainer === null && (style.overflowX === "auto" || style.overflowX === "hidden" || style.overflowX === "scroll")) {
8970
8945
  xScrollContainer = scrollContainer;
8971
8946
  }
8972
- if (
8973
- yScrollContainer === null &&
8974
- (style.overflowY === "auto" ||
8975
- style.overflowY === "hidden" ||
8976
- style.overflowY === "scroll")
8977
- ) {
8947
+ if (yScrollContainer === null && (style.overflowY === "auto" || style.overflowY === "hidden" || style.overflowY === "scroll")) {
8978
8948
  yScrollContainer = scrollContainer;
8979
8949
  }
8980
8950
  }
@@ -8983,7 +8953,6 @@ const initPositionSticky = (element) => {
8983
8953
  if (!needsPolyfillX && !needsPolyfillY) {
8984
8954
  return () => {}; // Native sticky will work fine on both axes
8985
8955
  }
8986
-
8987
8956
  const cleanupCallbackSet = new Set();
8988
8957
  const cleanup = () => {
8989
8958
  for (const cleanupCallback of cleanupCallbackSet) {
@@ -8991,7 +8960,6 @@ const initPositionSticky = (element) => {
8991
8960
  }
8992
8961
  cleanupCallbackSet.clear();
8993
8962
  };
8994
-
8995
8963
  const parentElement = element.parentElement;
8996
8964
  const createPlaceholderClone = () => {
8997
8965
  const clone = element.cloneNode(true);
@@ -8999,16 +8967,13 @@ const initPositionSticky = (element) => {
8999
8967
  clone.removeAttribute("data-sticky");
9000
8968
  return clone;
9001
8969
  };
9002
-
9003
8970
  let placeholder = createPlaceholderClone();
9004
8971
  parentElement.insertBefore(placeholder, element);
9005
8972
  cleanupCallbackSet.add(() => {
9006
8973
  placeholder.remove();
9007
8974
  });
9008
-
9009
8975
  let width = getWidth(element);
9010
8976
  let height = getHeight(element);
9011
-
9012
8977
  const updateSize = () => {
9013
8978
  const newPlaceholder = createPlaceholderClone();
9014
8979
  parentElement.replaceChild(newPlaceholder, placeholder);
@@ -9017,14 +8982,12 @@ const initPositionSticky = (element) => {
9017
8982
  height = getHeight(placeholder);
9018
8983
  updatePosition();
9019
8984
  };
9020
-
9021
8985
  const updatePosition = () => {
9022
8986
  // Ensure placeholder dimensions match element
9023
8987
  setStyles(placeholder, {
9024
8988
  width: `${width}px`,
9025
- height: `${height}px`,
8989
+ height: `${height}px`
9026
8990
  });
9027
-
9028
8991
  const placeholderRect = placeholder.getBoundingClientRect();
9029
8992
  const parentRect = parentElement.getBoundingClientRect();
9030
8993
 
@@ -9038,12 +9001,12 @@ const initPositionSticky = (element) => {
9038
9001
  // -420 <= 250 → stuck → element.style.left = 250px (main's left edge). ✓
9039
9002
  //
9040
9003
  // If no intermediate scroll container exists, use 0 (document/viewport edge).
9041
- const yContainerRect = yScrollContainer
9042
- ? yScrollContainer.getBoundingClientRect()
9043
- : { top: 0 };
9044
- const xContainerRect = xScrollContainer
9045
- ? xScrollContainer.getBoundingClientRect()
9046
- : { left: 0 };
9004
+ const yContainerRect = yScrollContainer ? yScrollContainer.getBoundingClientRect() : {
9005
+ top: 0
9006
+ };
9007
+ const xContainerRect = xScrollContainer ? xScrollContainer.getBoundingClientRect() : {
9008
+ left: 0
9009
+ };
9047
9010
  const topThreshold = yContainerRect.top + top;
9048
9011
  const leftThreshold = xContainerRect.left + left;
9049
9012
 
@@ -9086,7 +9049,6 @@ const initPositionSticky = (element) => {
9086
9049
  } else {
9087
9050
  leftPosition = placeholderRect.left;
9088
9051
  }
9089
-
9090
9052
  element.style.top = `${topPosition}px`;
9091
9053
  element.style.left = `${Math.round(leftPosition)}px`;
9092
9054
  element.style.width = `${width}px`;
@@ -9099,18 +9061,15 @@ const initPositionSticky = (element) => {
9099
9061
  element.removeAttribute("data-sticky");
9100
9062
  }
9101
9063
  };
9102
-
9103
9064
  {
9104
9065
  const restorePositionStyle = forceStyles(element, {
9105
9066
  "position": "fixed",
9106
9067
  "z-index": 1,
9107
- "will-change": "transform", // Hint for hardware acceleration
9068
+ "will-change": "transform" // Hint for hardware acceleration
9108
9069
  });
9109
9070
  cleanupCallbackSet.add(restorePositionStyle);
9110
9071
  }
9111
-
9112
9072
  updatePosition();
9113
-
9114
9073
  {
9115
9074
  const handleScroll = () => {
9116
9075
  updatePosition();
@@ -9121,15 +9080,16 @@ const initPositionSticky = (element) => {
9121
9080
  const listenTargets = new Set(scrollContainerSet);
9122
9081
  listenTargets.add(document.documentElement);
9123
9082
  for (const scrollTarget of listenTargets) {
9124
- scrollTarget.addEventListener("scroll", handleScroll, { passive: true });
9083
+ scrollTarget.addEventListener("scroll", handleScroll, {
9084
+ passive: true
9085
+ });
9125
9086
  cleanupCallbackSet.add(() => {
9126
9087
  scrollTarget.removeEventListener("scroll", handleScroll, {
9127
- passive: true,
9088
+ passive: true
9128
9089
  });
9129
9090
  });
9130
9091
  }
9131
9092
  }
9132
-
9133
9093
  {
9134
9094
  let animationFrame = null;
9135
9095
  const resizeObserver = new ResizeObserver(() => {
@@ -9148,7 +9108,6 @@ const initPositionSticky = (element) => {
9148
9108
  animationFrame = null;
9149
9109
  });
9150
9110
  }
9151
-
9152
9111
  {
9153
9112
  const mutationObserver = new MutationObserver(() => {
9154
9113
  updateSize();
@@ -9156,13 +9115,12 @@ const initPositionSticky = (element) => {
9156
9115
  mutationObserver.observe(element, {
9157
9116
  childList: true,
9158
9117
  subtree: true,
9159
- characterData: true,
9118
+ characterData: true
9160
9119
  });
9161
9120
  cleanupCallbackSet.add(() => {
9162
9121
  mutationObserver.disconnect();
9163
9122
  });
9164
9123
  }
9165
-
9166
9124
  return cleanup;
9167
9125
  };
9168
9126
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/dom",
3
- "version": "0.9.2",
3
+ "version": "0.9.3",
4
4
  "description": "DOM utilities for writing frontend code",
5
5
  "repository": {
6
6
  "type": "git",