svelte-dnd-action 0.9.65 → 0.9.67

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.
package/dist/index.js CHANGED
@@ -509,52 +509,91 @@
509
509
  var rect = getAbsoluteRect(el);
510
510
  return rect.right < 0 || rect.left > document.documentElement.scrollWidth || rect.bottom < 0 || rect.top > document.documentElement.scrollHeight;
511
511
  }
512
+
513
+ /**
514
+ * Computes the portion of an element that is actually visible inside its scrollable
515
+ * ancestor containers. If the element is clipped by any scrollable ancestor (overflow: auto|scroll),
516
+ * the returned rect is the clipped one. If it is not clipped by any scrollable ancestor but is
517
+ * partially or fully outside the viewport, the function returns the element's full bounding rect
518
+ * (i.e. it does NOT clip to the viewport).
519
+ *
520
+ * This is useful for distinguishing "hidden because parent scrolls" from
521
+ * "hidden because off-screen".
522
+ *
523
+ * @param {HTMLElement} element - The DOM element to measure.
524
+ * @returns {{top: number, bottom: number, left: number, right: number, width: number, height: number}}
525
+ * An object describing the visible rectangle in viewport coordinates.
526
+ */
512
527
  function getVisibleRectRecursive(element) {
528
+ // original rect of the element (can be off-screen)
513
529
  var rect = element.getBoundingClientRect();
530
+
531
+ // this will be our "clipped by scroll containers" rect
514
532
  var visibleRect = {
515
533
  top: rect.top,
516
534
  bottom: rect.bottom,
517
535
  left: rect.left,
518
536
  right: rect.right
519
537
  };
538
+ var wasClippedByScrollY = false;
539
+ var wasClippedByScrollX = false;
520
540
 
521
- // Traverse up the DOM hierarchy, checking for scrollable ancestors
541
+ // walk up and clip ONLY by scrollable ancestors
522
542
  var parent = element.parentElement;
523
543
  while (parent && parent !== document.body) {
524
- var parentRect = parent.getBoundingClientRect();
525
-
526
- // Check if the parent has a scrollable overflow
527
- var overflowY = window.getComputedStyle(parent).overflowY;
528
- var overflowX = window.getComputedStyle(parent).overflowX;
544
+ var style = window.getComputedStyle(parent);
545
+ var overflowY = style.overflowY;
546
+ var overflowX = style.overflowX;
529
547
  var isScrollableY = overflowY === "scroll" || overflowY === "auto";
530
548
  var isScrollableX = overflowX === "scroll" || overflowX === "auto";
531
-
532
- // Constrain the visible area to the parent's visible area
533
- if (isScrollableY) {
534
- visibleRect.top = Math.max(visibleRect.top, parentRect.top);
535
- visibleRect.bottom = Math.min(visibleRect.bottom, parentRect.bottom);
536
- }
537
- if (isScrollableX) {
538
- visibleRect.left = Math.max(visibleRect.left, parentRect.left);
539
- visibleRect.right = Math.min(visibleRect.right, parentRect.right);
549
+ if (isScrollableY || isScrollableX) {
550
+ var parentRect = parent.getBoundingClientRect();
551
+ if (isScrollableY) {
552
+ // if we actually shrink, mark it
553
+ var newTop = Math.max(visibleRect.top, parentRect.top);
554
+ var newBottom = Math.min(visibleRect.bottom, parentRect.bottom);
555
+ if (newTop !== visibleRect.top || newBottom !== visibleRect.bottom) {
556
+ wasClippedByScrollY = true;
557
+ }
558
+ visibleRect.top = newTop;
559
+ visibleRect.bottom = newBottom;
560
+ }
561
+ if (isScrollableX) {
562
+ var newLeft = Math.max(visibleRect.left, parentRect.left);
563
+ var newRight = Math.min(visibleRect.right, parentRect.right);
564
+ if (newLeft !== visibleRect.left || newRight !== visibleRect.right) {
565
+ wasClippedByScrollX = true;
566
+ }
567
+ visibleRect.left = newLeft;
568
+ visibleRect.right = newRight;
569
+ }
540
570
  }
541
571
  parent = parent.parentElement;
542
572
  }
543
573
 
544
- // Finally, constrain the visible rect to the viewport
545
- visibleRect.top = Math.max(visibleRect.top, 0);
546
- visibleRect.bottom = Math.min(visibleRect.bottom, window.innerHeight);
547
- visibleRect.left = Math.max(visibleRect.left, 0);
548
- visibleRect.right = Math.min(visibleRect.right, window.innerWidth);
574
+ // CASE 1: element was clipped by a scrollable container
575
+ // → return the clipped rect (this is your 500px content inside 250px scroller case)
576
+ if (wasClippedByScrollY || wasClippedByScrollX) {
577
+ return {
578
+ top: visibleRect.top,
579
+ bottom: visibleRect.bottom,
580
+ left: visibleRect.left,
581
+ right: visibleRect.right,
582
+ width: Math.max(0, visibleRect.right - visibleRect.left),
583
+ height: Math.max(0, visibleRect.bottom - visibleRect.top)
584
+ };
585
+ }
549
586
 
550
- // Return the visible rectangle, ensuring that all values are valid
587
+ // CASE 2: not clipped by scroll containers
588
+ // → we want the element’s FULL rect, even if it’s off-screen
589
+ // i.e. do NOT clip to viewport
551
590
  return {
552
- top: visibleRect.top,
553
- bottom: visibleRect.bottom,
554
- left: visibleRect.left,
555
- right: visibleRect.right,
556
- width: Math.max(0, visibleRect.right - visibleRect.left),
557
- height: Math.max(0, visibleRect.bottom - visibleRect.top)
591
+ top: rect.top,
592
+ bottom: rect.bottom,
593
+ left: rect.left,
594
+ right: rect.right,
595
+ width: Math.max(0, rect.right - rect.left),
596
+ height: Math.max(0, rect.bottom - rect.top)
558
597
  };
559
598
  }
560
599
 
@@ -1493,9 +1532,9 @@
1493
1532
  return;
1494
1533
  }
1495
1534
  isDraggedOutsideOfAnyDz = false;
1496
- // this deals with another race condition. in rare occasions (super rapid operations) the list hasn't updated yet
1535
+ // this deals with another race condition. on some occasions (super rapid operations) the list hasn't updated yet
1497
1536
  items = items.filter(function (item) {
1498
- return item[ITEM_ID_KEY] !== shadowElData[ITEM_ID_KEY];
1537
+ return item[ITEM_ID_KEY] !== shadowElData[ITEM_ID_KEY] && item[ITEM_ID_KEY] !== SHADOW_PLACEHOLDER_ITEM_ID;
1499
1538
  });
1500
1539
  printDebug(function () {
1501
1540
  return "dragged entered items ".concat(toString(items));
package/dist/index.mjs CHANGED
@@ -503,52 +503,91 @@ function isElementOffDocument(el) {
503
503
  var rect = getAbsoluteRect(el);
504
504
  return rect.right < 0 || rect.left > document.documentElement.scrollWidth || rect.bottom < 0 || rect.top > document.documentElement.scrollHeight;
505
505
  }
506
+
507
+ /**
508
+ * Computes the portion of an element that is actually visible inside its scrollable
509
+ * ancestor containers. If the element is clipped by any scrollable ancestor (overflow: auto|scroll),
510
+ * the returned rect is the clipped one. If it is not clipped by any scrollable ancestor but is
511
+ * partially or fully outside the viewport, the function returns the element's full bounding rect
512
+ * (i.e. it does NOT clip to the viewport).
513
+ *
514
+ * This is useful for distinguishing "hidden because parent scrolls" from
515
+ * "hidden because off-screen".
516
+ *
517
+ * @param {HTMLElement} element - The DOM element to measure.
518
+ * @returns {{top: number, bottom: number, left: number, right: number, width: number, height: number}}
519
+ * An object describing the visible rectangle in viewport coordinates.
520
+ */
506
521
  function getVisibleRectRecursive(element) {
522
+ // original rect of the element (can be off-screen)
507
523
  var rect = element.getBoundingClientRect();
524
+
525
+ // this will be our "clipped by scroll containers" rect
508
526
  var visibleRect = {
509
527
  top: rect.top,
510
528
  bottom: rect.bottom,
511
529
  left: rect.left,
512
530
  right: rect.right
513
531
  };
532
+ var wasClippedByScrollY = false;
533
+ var wasClippedByScrollX = false;
514
534
 
515
- // Traverse up the DOM hierarchy, checking for scrollable ancestors
535
+ // walk up and clip ONLY by scrollable ancestors
516
536
  var parent = element.parentElement;
517
537
  while (parent && parent !== document.body) {
518
- var parentRect = parent.getBoundingClientRect();
519
-
520
- // Check if the parent has a scrollable overflow
521
- var overflowY = window.getComputedStyle(parent).overflowY;
522
- var overflowX = window.getComputedStyle(parent).overflowX;
538
+ var style = window.getComputedStyle(parent);
539
+ var overflowY = style.overflowY;
540
+ var overflowX = style.overflowX;
523
541
  var isScrollableY = overflowY === "scroll" || overflowY === "auto";
524
542
  var isScrollableX = overflowX === "scroll" || overflowX === "auto";
525
-
526
- // Constrain the visible area to the parent's visible area
527
- if (isScrollableY) {
528
- visibleRect.top = Math.max(visibleRect.top, parentRect.top);
529
- visibleRect.bottom = Math.min(visibleRect.bottom, parentRect.bottom);
530
- }
531
- if (isScrollableX) {
532
- visibleRect.left = Math.max(visibleRect.left, parentRect.left);
533
- visibleRect.right = Math.min(visibleRect.right, parentRect.right);
543
+ if (isScrollableY || isScrollableX) {
544
+ var parentRect = parent.getBoundingClientRect();
545
+ if (isScrollableY) {
546
+ // if we actually shrink, mark it
547
+ var newTop = Math.max(visibleRect.top, parentRect.top);
548
+ var newBottom = Math.min(visibleRect.bottom, parentRect.bottom);
549
+ if (newTop !== visibleRect.top || newBottom !== visibleRect.bottom) {
550
+ wasClippedByScrollY = true;
551
+ }
552
+ visibleRect.top = newTop;
553
+ visibleRect.bottom = newBottom;
554
+ }
555
+ if (isScrollableX) {
556
+ var newLeft = Math.max(visibleRect.left, parentRect.left);
557
+ var newRight = Math.min(visibleRect.right, parentRect.right);
558
+ if (newLeft !== visibleRect.left || newRight !== visibleRect.right) {
559
+ wasClippedByScrollX = true;
560
+ }
561
+ visibleRect.left = newLeft;
562
+ visibleRect.right = newRight;
563
+ }
534
564
  }
535
565
  parent = parent.parentElement;
536
566
  }
537
567
 
538
- // Finally, constrain the visible rect to the viewport
539
- visibleRect.top = Math.max(visibleRect.top, 0);
540
- visibleRect.bottom = Math.min(visibleRect.bottom, window.innerHeight);
541
- visibleRect.left = Math.max(visibleRect.left, 0);
542
- visibleRect.right = Math.min(visibleRect.right, window.innerWidth);
568
+ // CASE 1: element was clipped by a scrollable container
569
+ // → return the clipped rect (this is your 500px content inside 250px scroller case)
570
+ if (wasClippedByScrollY || wasClippedByScrollX) {
571
+ return {
572
+ top: visibleRect.top,
573
+ bottom: visibleRect.bottom,
574
+ left: visibleRect.left,
575
+ right: visibleRect.right,
576
+ width: Math.max(0, visibleRect.right - visibleRect.left),
577
+ height: Math.max(0, visibleRect.bottom - visibleRect.top)
578
+ };
579
+ }
543
580
 
544
- // Return the visible rectangle, ensuring that all values are valid
581
+ // CASE 2: not clipped by scroll containers
582
+ // → we want the element’s FULL rect, even if it’s off-screen
583
+ // i.e. do NOT clip to viewport
545
584
  return {
546
- top: visibleRect.top,
547
- bottom: visibleRect.bottom,
548
- left: visibleRect.left,
549
- right: visibleRect.right,
550
- width: Math.max(0, visibleRect.right - visibleRect.left),
551
- height: Math.max(0, visibleRect.bottom - visibleRect.top)
585
+ top: rect.top,
586
+ bottom: rect.bottom,
587
+ left: rect.left,
588
+ right: rect.right,
589
+ width: Math.max(0, rect.right - rect.left),
590
+ height: Math.max(0, rect.bottom - rect.top)
552
591
  };
553
592
  }
554
593
 
@@ -1487,9 +1526,9 @@ function handleDraggedEntered(e) {
1487
1526
  return;
1488
1527
  }
1489
1528
  isDraggedOutsideOfAnyDz = false;
1490
- // this deals with another race condition. in rare occasions (super rapid operations) the list hasn't updated yet
1529
+ // this deals with another race condition. on some occasions (super rapid operations) the list hasn't updated yet
1491
1530
  items = items.filter(function (item) {
1492
- return item[ITEM_ID_KEY] !== shadowElData[ITEM_ID_KEY];
1531
+ return item[ITEM_ID_KEY] !== shadowElData[ITEM_ID_KEY] && item[ITEM_ID_KEY] !== SHADOW_PLACEHOLDER_ITEM_ID;
1493
1532
  });
1494
1533
  printDebug(function () {
1495
1534
  return "dragged entered items ".concat(toString(items));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "svelte-dnd-action",
3
3
  "description": "*An awesome drag and drop library for Svelte 3 and 4 (not using the browser's built-in dnd, thanks god): Rich animations, nested containers, touch support and more *",
4
- "version": "0.9.65",
4
+ "version": "0.9.67",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/isaacHagoel/svelte-dnd-action.git"
@@ -158,8 +158,25 @@ export function isElementOffDocument(el) {
158
158
  return rect.right < 0 || rect.left > document.documentElement.scrollWidth || rect.bottom < 0 || rect.top > document.documentElement.scrollHeight;
159
159
  }
160
160
 
161
+ /**
162
+ * Computes the portion of an element that is actually visible inside its scrollable
163
+ * ancestor containers. If the element is clipped by any scrollable ancestor (overflow: auto|scroll),
164
+ * the returned rect is the clipped one. If it is not clipped by any scrollable ancestor but is
165
+ * partially or fully outside the viewport, the function returns the element's full bounding rect
166
+ * (i.e. it does NOT clip to the viewport).
167
+ *
168
+ * This is useful for distinguishing "hidden because parent scrolls" from
169
+ * "hidden because off-screen".
170
+ *
171
+ * @param {HTMLElement} element - The DOM element to measure.
172
+ * @returns {{top: number, bottom: number, left: number, right: number, width: number, height: number}}
173
+ * An object describing the visible rectangle in viewport coordinates.
174
+ */
161
175
  function getVisibleRectRecursive(element) {
162
- let rect = element.getBoundingClientRect();
176
+ // original rect of the element (can be off-screen)
177
+ const rect = element.getBoundingClientRect();
178
+
179
+ // this will be our "clipped by scroll containers" rect
163
180
  let visibleRect = {
164
181
  top: rect.top,
165
182
  bottom: rect.bottom,
@@ -167,43 +184,68 @@ function getVisibleRectRecursive(element) {
167
184
  right: rect.right
168
185
  };
169
186
 
170
- // Traverse up the DOM hierarchy, checking for scrollable ancestors
187
+ let wasClippedByScrollY = false;
188
+ let wasClippedByScrollX = false;
189
+
190
+ // walk up and clip ONLY by scrollable ancestors
171
191
  let parent = element.parentElement;
172
192
  while (parent && parent !== document.body) {
173
- let parentRect = parent.getBoundingClientRect();
174
-
175
- // Check if the parent has a scrollable overflow
176
- const overflowY = window.getComputedStyle(parent).overflowY;
177
- const overflowX = window.getComputedStyle(parent).overflowX;
193
+ const style = window.getComputedStyle(parent);
194
+ const overflowY = style.overflowY;
195
+ const overflowX = style.overflowX;
178
196
  const isScrollableY = overflowY === "scroll" || overflowY === "auto";
179
197
  const isScrollableX = overflowX === "scroll" || overflowX === "auto";
180
198
 
181
- // Constrain the visible area to the parent's visible area
182
- if (isScrollableY) {
183
- visibleRect.top = Math.max(visibleRect.top, parentRect.top);
184
- visibleRect.bottom = Math.min(visibleRect.bottom, parentRect.bottom);
185
- }
186
- if (isScrollableX) {
187
- visibleRect.left = Math.max(visibleRect.left, parentRect.left);
188
- visibleRect.right = Math.min(visibleRect.right, parentRect.right);
199
+ if (isScrollableY || isScrollableX) {
200
+ const parentRect = parent.getBoundingClientRect();
201
+
202
+ if (isScrollableY) {
203
+ // if we actually shrink, mark it
204
+ const newTop = Math.max(visibleRect.top, parentRect.top);
205
+ const newBottom = Math.min(visibleRect.bottom, parentRect.bottom);
206
+ if (newTop !== visibleRect.top || newBottom !== visibleRect.bottom) {
207
+ wasClippedByScrollY = true;
208
+ }
209
+ visibleRect.top = newTop;
210
+ visibleRect.bottom = newBottom;
211
+ }
212
+
213
+ if (isScrollableX) {
214
+ const newLeft = Math.max(visibleRect.left, parentRect.left);
215
+ const newRight = Math.min(visibleRect.right, parentRect.right);
216
+ if (newLeft !== visibleRect.left || newRight !== visibleRect.right) {
217
+ wasClippedByScrollX = true;
218
+ }
219
+ visibleRect.left = newLeft;
220
+ visibleRect.right = newRight;
221
+ }
189
222
  }
190
223
 
191
224
  parent = parent.parentElement;
192
225
  }
193
226
 
194
- // Finally, constrain the visible rect to the viewport
195
- visibleRect.top = Math.max(visibleRect.top, 0);
196
- visibleRect.bottom = Math.min(visibleRect.bottom, window.innerHeight);
197
- visibleRect.left = Math.max(visibleRect.left, 0);
198
- visibleRect.right = Math.min(visibleRect.right, window.innerWidth);
227
+ // CASE 1: element was clipped by a scrollable container
228
+ // → return the clipped rect (this is your 500px content inside 250px scroller case)
229
+ if (wasClippedByScrollY || wasClippedByScrollX) {
230
+ return {
231
+ top: visibleRect.top,
232
+ bottom: visibleRect.bottom,
233
+ left: visibleRect.left,
234
+ right: visibleRect.right,
235
+ width: Math.max(0, visibleRect.right - visibleRect.left),
236
+ height: Math.max(0, visibleRect.bottom - visibleRect.top)
237
+ };
238
+ }
199
239
 
200
- // Return the visible rectangle, ensuring that all values are valid
240
+ // CASE 2: not clipped by scroll containers
241
+ // → we want the element’s FULL rect, even if it’s off-screen
242
+ // i.e. do NOT clip to viewport
201
243
  return {
202
- top: visibleRect.top,
203
- bottom: visibleRect.bottom,
204
- left: visibleRect.left,
205
- right: visibleRect.right,
206
- width: Math.max(0, visibleRect.right - visibleRect.left),
207
- height: Math.max(0, visibleRect.bottom - visibleRect.top)
244
+ top: rect.top,
245
+ bottom: rect.bottom,
246
+ left: rect.left,
247
+ right: rect.right,
248
+ width: Math.max(0, rect.right - rect.left),
249
+ height: Math.max(0, rect.bottom - rect.top)
208
250
  };
209
251
  }
@@ -141,8 +141,8 @@ function handleDraggedEntered(e) {
141
141
  return;
142
142
  }
143
143
  isDraggedOutsideOfAnyDz = false;
144
- // this deals with another race condition. in rare occasions (super rapid operations) the list hasn't updated yet
145
- items = items.filter(item => item[ITEM_ID_KEY] !== shadowElData[ITEM_ID_KEY]);
144
+ // this deals with another race condition. on some occasions (super rapid operations) the list hasn't updated yet
145
+ items = items.filter(item => item[ITEM_ID_KEY] !== shadowElData[ITEM_ID_KEY] && item[ITEM_ID_KEY] !== SHADOW_PLACEHOLDER_ITEM_ID);
146
146
  printDebug(() => `dragged entered items ${toString(items)}`);
147
147
 
148
148
  if (originDropZone !== e.currentTarget) {