@jsenv/dom 0.9.2 → 0.9.4

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