svelte-dnd-action 0.9.78 → 0.9.80

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
@@ -184,6 +184,105 @@
184
184
  };
185
185
  }
186
186
 
187
+ /**
188
+ * The window an element lives in. A drop zone can live in another window than the one that
189
+ * loaded this module, for example a same-origin iframe or a popup opened with window.open.
190
+ * @param {Node} el
191
+ * @return {Window}
192
+ */
193
+ function getWindowOf(el) {
194
+ // eslint-disable-next-line no-restricted-globals -- for a document without a window, e.g. after its window closed
195
+ return el.ownerDocument.defaultView || window;
196
+ }
197
+
198
+ /**
199
+ * Schedules a timeout on the given window. Work for a zone runs on the zone's window: the browser throttles
200
+ * or stops the timers of a hidden window, and the loading window can be hidden while the zone's is not.
201
+ * @param {Window} win
202
+ * @param {function} callback
203
+ * @param {number} ms
204
+ * @return {{win: Window, id: number}} - pass it to clearTimeoutIn
205
+ */
206
+ function setTimeoutIn(win, callback, ms) {
207
+ return {
208
+ win: win,
209
+ id: win.setTimeout(callback, ms)
210
+ };
211
+ }
212
+
213
+ /**
214
+ * Cancels a timeout through the window that scheduled it
215
+ * @param {{win: Window, id: number}} [timeout] - from setTimeoutIn
216
+ */
217
+ function clearTimeoutIn(timeout) {
218
+ if (timeout) timeout.win.clearTimeout(timeout.id);
219
+ }
220
+
221
+ /**
222
+ * @param {Object} object
223
+ * @return {string}
224
+ */
225
+ function toString(object) {
226
+ return JSON.stringify(object, null, 2);
227
+ }
228
+
229
+ /**
230
+ * Finds the depth of the given node in the DOM tree
231
+ * @param {HTMLElement} node
232
+ * @return {number} - the depth of the node
233
+ */
234
+ function getDepth(node) {
235
+ if (!node) {
236
+ throw new Error("cannot get depth of a falsy node");
237
+ }
238
+ return _getDepth(node, 0);
239
+ }
240
+ function _getDepth(node) {
241
+ var countSoFar = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
242
+ if (!node.parentElement) {
243
+ return countSoFar - 1;
244
+ }
245
+ return _getDepth(node.parentElement, countSoFar + 1);
246
+ }
247
+
248
+ /**
249
+ * A simple util to shallow compare objects quickly, it doesn't validate the arguments so pass objects in
250
+ * @param {Object} objA
251
+ * @param {Object} objB
252
+ * @return {boolean} - true if objA and objB are shallow equal
253
+ */
254
+ function areObjectsShallowEqual(objA, objB) {
255
+ if (Object.keys(objA).length !== Object.keys(objB).length) {
256
+ return false;
257
+ }
258
+ for (var keyA in objA) {
259
+ if (!{}.hasOwnProperty.call(objB, keyA) || objB[keyA] !== objA[keyA]) {
260
+ return false;
261
+ }
262
+ }
263
+ return true;
264
+ }
265
+
266
+ /**
267
+ * Shallow compares two arrays
268
+ * @param arrA
269
+ * @param arrB
270
+ * @return {boolean} - whether the arrays are shallow equal
271
+ */
272
+ function areArraysShallowEqualSameOrder(arrA, arrB) {
273
+ if (arrA.length !== arrB.length) {
274
+ return false;
275
+ }
276
+ for (var i = 0; i < arrA.length; i++) {
277
+ if (arrA[i] !== arrB[i]) {
278
+ return false;
279
+ }
280
+ }
281
+ return true;
282
+ }
283
+
284
+ // Events are created in the window of the element they are dispatched on, so a listener in that window sees an
285
+ // instance of its own CustomEvent.
187
286
  // external events
188
287
  var FINALIZE_EVENT_NAME = "finalize";
189
288
  var CONSIDER_EVENT_NAME = "consider";
@@ -198,7 +297,7 @@
198
297
  * @param {Info} info
199
298
  */
200
299
  function dispatchFinalizeEvent(el, items, info) {
201
- el.dispatchEvent(new CustomEvent(FINALIZE_EVENT_NAME, {
300
+ el.dispatchEvent(new (getWindowOf(el).CustomEvent)(FINALIZE_EVENT_NAME, {
202
301
  detail: {
203
302
  items: items,
204
303
  info: info
@@ -213,7 +312,7 @@
213
312
  * @param {Info} info
214
313
  */
215
314
  function dispatchConsiderEvent(el, items, info) {
216
- el.dispatchEvent(new CustomEvent(CONSIDER_EVENT_NAME, {
315
+ el.dispatchEvent(new (getWindowOf(el).CustomEvent)(CONSIDER_EVENT_NAME, {
217
316
  detail: {
218
317
  items: items,
219
318
  info: info
@@ -231,7 +330,7 @@
231
330
  OUTSIDE_OF_ANY: "outsideOfAny"
232
331
  };
233
332
  function dispatchDraggedElementEnteredContainer(containerEl, indexObj, draggedEl) {
234
- containerEl.dispatchEvent(new CustomEvent(DRAGGED_ENTERED_EVENT_NAME, {
333
+ containerEl.dispatchEvent(new (getWindowOf(containerEl).CustomEvent)(DRAGGED_ENTERED_EVENT_NAME, {
235
334
  detail: {
236
335
  indexObj: indexObj,
237
336
  draggedEl: draggedEl
@@ -245,7 +344,7 @@
245
344
  * @param theOtherDz - the new dropzone the element entered
246
345
  */
247
346
  function dispatchDraggedElementLeftContainerForAnother(containerEl, draggedEl, theOtherDz) {
248
- containerEl.dispatchEvent(new CustomEvent(DRAGGED_LEFT_EVENT_NAME, {
347
+ containerEl.dispatchEvent(new (getWindowOf(containerEl).CustomEvent)(DRAGGED_LEFT_EVENT_NAME, {
249
348
  detail: {
250
349
  draggedEl: draggedEl,
251
350
  type: DRAGGED_LEFT_TYPES.LEFT_FOR_ANOTHER,
@@ -254,7 +353,7 @@
254
353
  }));
255
354
  }
256
355
  function dispatchDraggedElementLeftContainerForNone(containerEl, draggedEl) {
257
- containerEl.dispatchEvent(new CustomEvent(DRAGGED_LEFT_EVENT_NAME, {
356
+ containerEl.dispatchEvent(new (getWindowOf(containerEl).CustomEvent)(DRAGGED_LEFT_EVENT_NAME, {
258
357
  detail: {
259
358
  draggedEl: draggedEl,
260
359
  type: DRAGGED_LEFT_TYPES.OUTSIDE_OF_ANY
@@ -262,7 +361,7 @@
262
361
  }));
263
362
  }
264
363
  function dispatchDraggedElementIsOverIndex(containerEl, indexObj, draggedEl) {
265
- containerEl.dispatchEvent(new CustomEvent(DRAGGED_OVER_INDEX_EVENT_NAME, {
364
+ containerEl.dispatchEvent(new (getWindowOf(containerEl).CustomEvent)(DRAGGED_OVER_INDEX_EVENT_NAME, {
266
365
  detail: {
267
366
  indexObj: indexObj,
268
367
  draggedEl: draggedEl
@@ -270,7 +369,8 @@
270
369
  }));
271
370
  }
272
371
  function dispatchDraggedLeftDocument(draggedEl) {
273
- window.dispatchEvent(new CustomEvent(DRAGGED_LEFT_DOCUMENT_EVENT_NAME, {
372
+ var win = getWindowOf(draggedEl);
373
+ win.dispatchEvent(new win.CustomEvent(DRAGGED_LEFT_DOCUMENT_EVENT_NAME, {
274
374
  detail: {
275
375
  draggedEl: draggedEl
276
376
  }
@@ -346,6 +446,8 @@
346
446
  });
347
447
  ITEM_ID_KEY = newKeyName;
348
448
  }
449
+
450
+ // eslint-disable-next-line no-restricted-globals -- detects server-side rendering
349
451
  var isOnServer = typeof window === "undefined";
350
452
  var printDebug = function printDebug() {};
351
453
 
@@ -381,7 +483,7 @@
381
483
  var onlyVisible = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
382
484
  var ta;
383
485
  var rect = onlyVisible ? getVisibleRectRecursive(el) : el.getBoundingClientRect();
384
- var style = getComputedStyle(el);
486
+ var style = getWindowOf(el).getComputedStyle(el);
385
487
  var tx = style.transform;
386
488
  if (tx) {
387
489
  var sx, sy, dx, dy;
@@ -401,8 +503,8 @@
401
503
  return rect;
402
504
  }
403
505
  var to = style.transformOrigin;
404
- var x = rect.x - dx - (1 - sx) * parseFloat(to);
405
- var y = rect.y - dy - (1 - sy) * parseFloat(to.slice(to.indexOf(" ") + 1));
506
+ var x = rect.left - dx - (1 - sx) * parseFloat(to);
507
+ var y = rect.top - dy - (1 - sy) * parseFloat(to.slice(to.indexOf(" ") + 1));
406
508
  var w = sx ? rect.width / sx : el.offsetWidth;
407
509
  var h = sy ? rect.height / sy : el.offsetHeight;
408
510
  return {
@@ -427,11 +529,12 @@
427
529
  */
428
530
  function getAbsoluteRectNoTransforms(el) {
429
531
  var rect = getBoundingRectNoTransforms(el);
532
+ var win = getWindowOf(el);
430
533
  return {
431
- top: rect.top + window.scrollY,
432
- bottom: rect.bottom + window.scrollY,
433
- left: rect.left + window.scrollX,
434
- right: rect.right + window.scrollX
534
+ top: rect.top + win.scrollY,
535
+ bottom: rect.bottom + win.scrollY,
536
+ left: rect.left + win.scrollX,
537
+ right: rect.right + win.scrollX
435
538
  };
436
539
  }
437
540
 
@@ -442,11 +545,12 @@
442
545
  */
443
546
  function getAbsoluteRect(el) {
444
547
  var rect = el.getBoundingClientRect();
548
+ var win = getWindowOf(el);
445
549
  return {
446
- top: rect.top + window.scrollY,
447
- bottom: rect.bottom + window.scrollY,
448
- left: rect.left + window.scrollX,
449
- right: rect.right + window.scrollX
550
+ top: rect.top + win.scrollY,
551
+ bottom: rect.bottom + win.scrollY,
552
+ left: rect.left + win.scrollX,
553
+ right: rect.right + win.scrollX
450
554
  };
451
555
  }
452
556
 
@@ -514,7 +618,8 @@
514
618
  */
515
619
  function isElementOffDocument(el) {
516
620
  var rect = getAbsoluteRect(el);
517
- return rect.right < 0 || rect.left > document.documentElement.scrollWidth || rect.bottom < 0 || rect.top > document.documentElement.scrollHeight;
621
+ var documentElement = el.ownerDocument.documentElement;
622
+ return rect.right < 0 || rect.left > documentElement.scrollWidth || rect.bottom < 0 || rect.top > documentElement.scrollHeight;
518
623
  }
519
624
 
520
625
  /**
@@ -547,8 +652,8 @@
547
652
 
548
653
  // walk up and clip ONLY by scrollable ancestors
549
654
  var parent = element.parentElement;
550
- while (parent && parent !== document.body) {
551
- var style = window.getComputedStyle(parent);
655
+ while (parent && parent !== element.ownerDocument.body) {
656
+ var style = getWindowOf(parent).getComputedStyle(parent);
552
657
  var overflowY = style.overflowY;
553
658
  var overflowX = style.overflowX;
554
659
  var isScrollableY = overflowY === "scroll" || overflowY === "auto";
@@ -727,72 +832,12 @@
727
832
  };
728
833
  }
729
834
 
730
- /**
731
- * @param {Object} object
732
- * @return {string}
733
- */
734
- function toString(object) {
735
- return JSON.stringify(object, null, 2);
736
- }
737
-
738
- /**
739
- * Finds the depth of the given node in the DOM tree
740
- * @param {HTMLElement} node
741
- * @return {number} - the depth of the node
742
- */
743
- function getDepth(node) {
744
- if (!node) {
745
- throw new Error("cannot get depth of a falsy node");
746
- }
747
- return _getDepth(node, 0);
748
- }
749
- function _getDepth(node) {
750
- var countSoFar = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
751
- if (!node.parentElement) {
752
- return countSoFar - 1;
753
- }
754
- return _getDepth(node.parentElement, countSoFar + 1);
755
- }
756
-
757
- /**
758
- * A simple util to shallow compare objects quickly, it doesn't validate the arguments so pass objects in
759
- * @param {Object} objA
760
- * @param {Object} objB
761
- * @return {boolean} - true if objA and objB are shallow equal
762
- */
763
- function areObjectsShallowEqual(objA, objB) {
764
- if (Object.keys(objA).length !== Object.keys(objB).length) {
765
- return false;
766
- }
767
- for (var keyA in objA) {
768
- if (!{}.hasOwnProperty.call(objB, keyA) || objB[keyA] !== objA[keyA]) {
769
- return false;
770
- }
771
- }
772
- return true;
773
- }
774
-
775
- /**
776
- * Shallow compares two arrays
777
- * @param arrA
778
- * @param arrB
779
- * @return {boolean} - whether the arrays are shallow equal
780
- */
781
- function areArraysShallowEqualSameOrder(arrA, arrB) {
782
- if (arrA.length !== arrB.length) {
783
- return false;
784
- }
785
- for (var i = 0; i < arrA.length; i++) {
786
- if (arrA[i] !== arrB[i]) {
787
- return false;
788
- }
789
- }
790
- return true;
791
- }
792
-
793
835
  var INTERVAL_MS = 200;
794
836
  var TOLERANCE_PX = 10;
795
837
  var next;
838
+ // The observation in progress. A consider handler can end the drag in the middle of an observation tick, for example by
839
+ // removing the drag's window, and the tick must not carry on with a drag that has ended.
840
+ var currentObservation;
796
841
 
797
842
  /**
798
843
  * Tracks the dragged elements and performs the side effects when it is dragged over a drop zone (basically dispatching custom-events scrolling)
@@ -811,6 +856,9 @@
811
856
  var lastIndexFound;
812
857
  var lastIsDraggedInADropZone = false;
813
858
  var lastCentrePositionOfDragged;
859
+ var win = getWindowOf(draggedEl);
860
+ var observation = {};
861
+ currentObservation = observation;
814
862
  // We are sorting to make sure that in case of nested zones of the same type the one "on top" is considered first
815
863
  var dropZonesFromDeepToShallow = Array.from(dropZones).sort(function (dz1, dz2) {
816
864
  return getDepth(dz2) - getDepth(dz1);
@@ -824,7 +872,7 @@
824
872
  var scrolled = multiScroller.multiScrollIfNeeded();
825
873
  // we only want to make a new decision after the element was moved a bit to prevent flickering
826
874
  if (!scrolled && lastCentrePositionOfDragged && Math.abs(lastCentrePositionOfDragged.x - referencePoint.x) < TOLERANCE_PX && Math.abs(lastCentrePositionOfDragged.y - referencePoint.y) < TOLERANCE_PX) {
827
- next = window.setTimeout(andNow, intervalMs);
875
+ next = setTimeoutIn(win, andNow, intervalMs);
828
876
  return;
829
877
  }
830
878
  if (isElementOffDocument(draggedEl)) {
@@ -853,6 +901,7 @@
853
901
  // the element is over a container
854
902
  if (dz !== lastDropZoneFound) {
855
903
  lastDropZoneFound && dispatchDraggedElementLeftContainerForAnother(lastDropZoneFound, draggedEl, dz);
904
+ if (currentObservation !== observation) return;
856
905
  dispatchDraggedElementEnteredContainer(dz, indexObj, draggedEl);
857
906
  lastDropZoneFound = dz;
858
907
  } else if (index !== lastIndexFound) {
@@ -876,7 +925,8 @@
876
925
  } else {
877
926
  lastIsDraggedInADropZone = true;
878
927
  }
879
- next = window.setTimeout(andNow, intervalMs);
928
+ if (currentObservation !== observation) return;
929
+ next = setTimeoutIn(win, andNow, intervalMs);
880
930
  }
881
931
  andNow();
882
932
  }
@@ -886,7 +936,9 @@
886
936
  printDebug(function () {
887
937
  return "unobserving";
888
938
  });
889
- clearTimeout(next);
939
+ currentObservation = undefined;
940
+ clearTimeoutIn(next);
941
+ next = undefined;
890
942
  resetIndexesCache();
891
943
  }
892
944
 
@@ -912,7 +964,7 @@
912
964
  stepPx = _scrollingInfo.stepPx;
913
965
  if (directionObj) {
914
966
  containerEl.scrollBy(directionObj.x * stepPx, directionObj.y * stepPx);
915
- window.requestAnimationFrame(function () {
967
+ getWindowOf(containerEl).requestAnimationFrame(function () {
916
968
  return scrollContainer(containerEl);
917
969
  });
918
970
  }
@@ -1001,11 +1053,11 @@
1001
1053
  */
1002
1054
  function calcInnerDistancesBetweenPointAndSidesOfElement(point, el) {
1003
1055
  // Even if the scrolling element is small it acts as a scroller for the viewport
1004
- var rect = el === document.scrollingElement ? {
1056
+ var rect = el === el.ownerDocument.scrollingElement ? {
1005
1057
  top: 0,
1006
- bottom: window.innerHeight,
1058
+ bottom: getWindowOf(el).innerHeight,
1007
1059
  left: 0,
1008
- right: window.innerWidth
1060
+ right: getWindowOf(el).innerWidth
1009
1061
  } : el.getBoundingClientRect();
1010
1062
  if (!isPointInsideRect(point, rect)) {
1011
1063
  return null;
@@ -1050,7 +1102,7 @@
1050
1102
  return false;
1051
1103
  }
1052
1104
  var scrollContainersUnderCursor = scrollingContainersDeepToShallow.filter(function (el) {
1053
- return isPointInsideRect(mousePosition, el.getBoundingClientRect()) || el === document.scrollingElement;
1105
+ return isPointInsideRect(mousePosition, el.getBoundingClientRect()) || el === el.ownerDocument.scrollingElement;
1054
1106
  });
1055
1107
  for (var i = 0; i < scrollContainersUnderCursor.length; i++) {
1056
1108
  var scrolled = scrollIfNeeded(mousePosition, scrollContainersUnderCursor[i]);
@@ -1078,8 +1130,8 @@
1078
1130
  var scrollableContainers = [];
1079
1131
  var parent = element;
1080
1132
  while (parent) {
1081
- var _window$getComputedSt = window.getComputedStyle(parent),
1082
- overflow = _window$getComputedSt.overflow;
1133
+ var _getWindowOf$getCompu = getWindowOf(parent).getComputedStyle(parent),
1134
+ overflow = _getWindowOf$getCompu.overflow;
1083
1135
  if (overflow.split(" ").some(function (o) {
1084
1136
  return o.includes("auto") || o.includes("scroll");
1085
1137
  })) {
@@ -1099,16 +1151,17 @@
1099
1151
  findScrollableParents(dz).forEach(function (container) {
1100
1152
  return scrollingContainers.add(container);
1101
1153
  });
1154
+ // The scrolling element might have overflow visible and still be scrollable
1155
+ var scrollingElement = dz.ownerDocument.scrollingElement;
1156
+ if (scrollingElement.scrollHeight > scrollingElement.clientHeight || scrollingElement.scrollWidth > scrollingElement.clientHeight) {
1157
+ scrollingContainers.add(scrollingElement);
1158
+ }
1102
1159
  }
1103
- // The scrolling element might have overflow visible and still be scrollable
1104
1160
  } catch (err) {
1105
1161
  _iterator.e(err);
1106
1162
  } finally {
1107
1163
  _iterator.f();
1108
1164
  }
1109
- if (document.scrollingElement.scrollHeight > document.scrollingElement.clientHeight || document.scrollingElement.scrollWidth > document.scrollingElement.clientHeight) {
1110
- scrollingContainers.add(document.scrollingElement);
1111
- }
1112
1165
  return scrollingContainers;
1113
1166
  }
1114
1167
 
@@ -1227,6 +1280,7 @@
1227
1280
  */
1228
1281
  function createDraggedElementFrom(originalElement, positionCenterOnXY) {
1229
1282
  var rect = originalElement.getBoundingClientRect();
1283
+ var win = getWindowOf(originalElement);
1230
1284
  var draggedEl = svelteNodeClone(originalElement);
1231
1285
  copyStylesFromTo(originalElement, draggedEl);
1232
1286
  draggedEl.id = DRAGGED_ELEMENT_ID;
@@ -1239,7 +1293,7 @@
1239
1293
  var center = findCenter(rect);
1240
1294
  elTopPx -= center.y - positionCenterOnXY.y;
1241
1295
  elLeftPx -= center.x - positionCenterOnXY.x;
1242
- window.setTimeout(function () {
1296
+ win.setTimeout(function () {
1243
1297
  draggedEl.style.top = "".concat(elTopPx, "px");
1244
1298
  draggedEl.style.left = "".concat(elLeftPx, "px");
1245
1299
  }, 0);
@@ -1251,7 +1305,7 @@
1251
1305
  draggedEl.style.width = "".concat(rect.width, "px");
1252
1306
  draggedEl.style.transition = "".concat(trs("top"), ", ").concat(trs("left"), ", ").concat(trs("background-color"), ", ").concat(trs("opacity"), ", ").concat(trs("color"), " ");
1253
1307
  // this is a workaround for a strange browser bug that causes the right border to disappear when all the transitions are added at the same time
1254
- window.setTimeout(function () {
1308
+ win.setTimeout(function () {
1255
1309
  return ensureMorphSizeTransitions(draggedEl);
1256
1310
  }, 0);
1257
1311
  draggedEl.style.zIndex = "9999";
@@ -1279,7 +1333,7 @@
1279
1333
  copyStylesFromTo(copyFromEl, draggedEl);
1280
1334
  var newRect = copyFromEl.getBoundingClientRect();
1281
1335
  var draggedElRect = draggedEl.getBoundingClientRect();
1282
- var draggedElComputedStyle = window.getComputedStyle(draggedEl);
1336
+ var draggedElComputedStyle = getWindowOf(draggedEl).getComputedStyle(draggedEl);
1283
1337
  var currentLeft = parseFloat(draggedElComputedStyle.left);
1284
1338
  var currentTop = parseFloat(draggedElComputedStyle.top);
1285
1339
  var widthChange = newRect.width - draggedElRect.width;
@@ -1303,7 +1357,7 @@
1303
1357
  * @param {HTMLElement} copyToEl
1304
1358
  */
1305
1359
  function copyStylesFromTo(copyFromEl, copyToEl) {
1306
- var computedStyle = window.getComputedStyle(copyFromEl);
1360
+ var computedStyle = getWindowOf(copyFromEl).getComputedStyle(copyFromEl);
1307
1361
  Array.from(computedStyle).filter(function (s) {
1308
1362
  return s.startsWith("background") || s.startsWith("padding") || s.startsWith("font") || s.startsWith("text") || s.startsWith("align") || s.startsWith("justify") || s.startsWith("display") || s.startsWith("flex") || s.startsWith("border") || s === "opacity" || s === "color" || s === "list-style-type" ||
1309
1363
  // copying with and height to make up for rect update timing issues in some browsers
@@ -1413,9 +1467,10 @@
1413
1467
  */
1414
1468
  function preventShrinking(el) {
1415
1469
  var originalMinHeight = el.style.minHeight;
1416
- el.style.minHeight = window.getComputedStyle(el).getPropertyValue("height");
1470
+ var computedStyle = getWindowOf(el).getComputedStyle(el);
1471
+ el.style.minHeight = computedStyle.getPropertyValue("height");
1417
1472
  var originalMinWidth = el.style.minWidth;
1418
- el.style.minWidth = window.getComputedStyle(el).getPropertyValue("width");
1473
+ el.style.minWidth = computedStyle.getPropertyValue("width");
1419
1474
  return function undo() {
1420
1475
  el.style.minHeight = originalMinHeight;
1421
1476
  el.style.minWidth = originalMinWidth;
@@ -1452,6 +1507,11 @@
1452
1507
  var useCursorForDetectionActive = false;
1453
1508
  var pendingDragOwner;
1454
1509
  var watchedDropZones = new Set();
1510
+ // The window of the drag in progress: the dragged item's own window, which is not the one that
1511
+ // loaded this module when the zone lives in a same-origin iframe or a popup.
1512
+ var dragWindow;
1513
+ // While the drop animation runs: its timeout and the function that finalizes the drop
1514
+ var dropFinalization;
1455
1515
 
1456
1516
  // a map from type to a set of drop-zones
1457
1517
  var typeToDropZones$1 = new Map();
@@ -1481,14 +1541,21 @@
1481
1541
  typeToDropZones$1["delete"](type);
1482
1542
  }
1483
1543
  }
1544
+ // The pointer is only tracked in the drag's window and each window has its own coordinates, so zones in other windows
1545
+ // are not candidates, and neither are zones whose window is gone
1546
+ function getDropZonesInDragWindow(type) {
1547
+ return Array.from(typeToDropZones$1.get(type) || []).filter(function (dz) {
1548
+ return dz.ownerDocument.defaultView === dragWindow;
1549
+ });
1550
+ }
1484
1551
 
1485
1552
  /* functions to manage observing the dragged element and trigger custom drag-events */
1486
1553
  function watchDraggedElement() {
1487
1554
  printDebug(function () {
1488
1555
  return "watching dragged element";
1489
1556
  });
1490
- var dropZones = typeToDropZones$1.get(draggedElType);
1491
- if (!dropZones || dropZones.size === 0) return;
1557
+ var dropZones = getDropZonesInDragWindow(draggedElType);
1558
+ if (dropZones.length === 0) return;
1492
1559
  if (watchedDropZones.size > 0) unWatchDraggedElement();
1493
1560
  watchedDropZones = new Set(dropZones);
1494
1561
  var _iterator = _createForOfIteratorHelper(watchedDropZones),
@@ -1505,7 +1572,7 @@
1505
1572
  } finally {
1506
1573
  _iterator.f();
1507
1574
  }
1508
- window.addEventListener(DRAGGED_LEFT_DOCUMENT_EVENT_NAME, handleDrop$1);
1575
+ dragWindow.addEventListener(DRAGGED_LEFT_DOCUMENT_EVENT_NAME, handleDrop$1);
1509
1576
 
1510
1577
  // it is important that we don't have an interval that is faster than the flip duration because it can cause elements to jump bach and forth
1511
1578
  var setIntervalMs = Math.max.apply(Math, _toConsumableArray(Array.from(watchedDropZones).map(function (dz) {
@@ -1518,8 +1585,8 @@
1518
1585
  // Returns reference point in document coordinates - either cursor position or element center
1519
1586
  var getReferencePoint = useCursorForDetectionActive ? function () {
1520
1587
  return {
1521
- x: currentMousePosition.x + window.scrollX,
1522
- y: currentMousePosition.y + window.scrollY
1588
+ x: currentMousePosition.x + dragWindow.scrollX,
1589
+ y: currentMousePosition.y + dragWindow.scrollY
1523
1590
  };
1524
1591
  } : function () {
1525
1592
  return findCenterOfElement(draggedEl);
@@ -1545,7 +1612,7 @@
1545
1612
  _iterator2.f();
1546
1613
  }
1547
1614
  watchedDropZones = new Set();
1548
- window.removeEventListener(DRAGGED_LEFT_DOCUMENT_EVENT_NAME, handleDrop$1);
1615
+ dragWindow.removeEventListener(DRAGGED_LEFT_DOCUMENT_EVENT_NAME, handleDrop$1);
1549
1616
  // ensuring multiScroller is not already destroyed before destroying
1550
1617
  if (multiScroller) {
1551
1618
  multiScroller.destroy();
@@ -1595,6 +1662,8 @@
1595
1662
  id: draggedElData[ITEM_ID_KEY],
1596
1663
  source: SOURCES.POINTER
1597
1664
  });
1665
+ // the handler can end the drag, for example by removing its window
1666
+ if (!isWorkingOnPreviousDrag) return;
1598
1667
  }
1599
1668
  var shadowElIdx = e.detail.indexObj.index;
1600
1669
  shadowElDropZone = e.currentTarget;
@@ -1644,6 +1713,8 @@
1644
1713
  id: draggedElData[ITEM_ID_KEY],
1645
1714
  source: SOURCES.POINTER
1646
1715
  });
1716
+ // the handler can end the drag, for example by removing its window
1717
+ if (!isWorkingOnPreviousDrag) return;
1647
1718
  }
1648
1719
  // for the origin dz, when the dragged is outside of any, this will be fired in addition to the previous. this is for simplicity
1649
1720
  dispatchConsiderEvent(e.currentTarget, items, {
@@ -1696,10 +1767,10 @@
1696
1767
  });
1697
1768
  finalizingPreviousDrag = true;
1698
1769
  // cleanup
1699
- window.removeEventListener("mousemove", handleMouseMove);
1700
- window.removeEventListener("touchmove", handleMouseMove);
1701
- window.removeEventListener("mouseup", handleDrop$1);
1702
- window.removeEventListener("touchend", handleDrop$1);
1770
+ dragWindow.removeEventListener("mousemove", handleMouseMove);
1771
+ dragWindow.removeEventListener("touchmove", handleMouseMove);
1772
+ dragWindow.removeEventListener("mouseup", handleDrop$1);
1773
+ dragWindow.removeEventListener("touchend", handleDrop$1);
1703
1774
  unWatchDraggedElement();
1704
1775
  moveDraggedElementToWasDroppedState(draggedEl);
1705
1776
  if (!shadowElDropZone) {
@@ -1714,7 +1785,7 @@
1714
1785
  var _dzToConfig$get4 = dzToConfig$1.get(shadowElDropZone),
1715
1786
  items = _dzToConfig$get4.items,
1716
1787
  type = _dzToConfig$get4.type;
1717
- styleInactiveDropZones(typeToDropZones$1.get(type), function (dz) {
1788
+ styleInactiveDropZones(getDropZonesInDragWindow(type), function (dz) {
1718
1789
  return dzToConfig$1.get(dz).dropTargetStyle;
1719
1790
  }, function (dz) {
1720
1791
  return dzToConfig$1.get(dz).dropTargetClasses;
@@ -1773,7 +1844,32 @@
1773
1844
  var transition = "transform ".concat(dropAnimationDurationMs, "ms ease");
1774
1845
  draggedEl.style.transition = draggedEl.style.transition ? draggedEl.style.transition + "," + transition : transition;
1775
1846
  draggedEl.style.transform = "translate3d(".concat(newTransform.x, "px, ").concat(newTransform.y, "px, 0)");
1776
- window.setTimeout(callback, dropAnimationDurationMs);
1847
+ dropFinalization = {
1848
+ timeout: setTimeoutIn(dragWindow, finishDropAnimation, dropAnimationDurationMs),
1849
+ finalize: callback
1850
+ };
1851
+ }
1852
+ function finishDropAnimation() {
1853
+ var _dropFinalization = dropFinalization,
1854
+ finalize = _dropFinalization.finalize;
1855
+ // cleared first, because a finalize handler can remove the drag's window
1856
+ dropFinalization = undefined;
1857
+ finalize();
1858
+ }
1859
+
1860
+ // The drag's window is going away: it was closed or navigated, or its iframe was removed. Its events, timers and animation
1861
+ // frames stop, so finish the drag now, without the drop animation, and let the other windows start new drags.
1862
+ function handleDragWindowHidden() {
1863
+ printDebug(function () {
1864
+ return "the drag's window is going away";
1865
+ });
1866
+ if (!finalizingPreviousDrag) {
1867
+ handleDrop$1();
1868
+ }
1869
+ if (dropFinalization) {
1870
+ clearTimeoutIn(dropFinalization.timeout);
1871
+ finishDropAnimation();
1872
+ }
1777
1873
  }
1778
1874
  function scheduleDZForRemovalAfterDrop(dz, destroy) {
1779
1875
  var scheduledRemoval = {
@@ -1781,10 +1877,10 @@
1781
1877
  destroy: destroy
1782
1878
  };
1783
1879
  scheduledForRemovalAfterDrop.push(scheduledRemoval);
1784
- window.requestAnimationFrame(function () {
1880
+ getWindowOf(dz).requestAnimationFrame(function () {
1785
1881
  if (!scheduledForRemovalAfterDrop.includes(scheduledRemoval)) return;
1786
1882
  hideElement(dz);
1787
- document.body.appendChild(dz);
1883
+ dz.ownerDocument.body.appendChild(dz);
1788
1884
  });
1789
1885
  }
1790
1886
  /* cleanup */
@@ -1810,9 +1906,9 @@
1810
1906
  finalizingPreviousDrag = false;
1811
1907
  unlockOriginDzMinDimensions = undefined;
1812
1908
  isDraggedOutsideOfAnyDz = false;
1813
- if (touchDragHoldTimer) {
1814
- clearTimeout(touchDragHoldTimer);
1815
- }
1909
+ dragWindow.removeEventListener("pagehide", handleDragWindowHidden);
1910
+ dragWindow = undefined;
1911
+ clearTimeoutIn(touchDragHoldTimer);
1816
1912
  touchDragHoldTimer = undefined;
1817
1913
  touchHoldElapsed = false;
1818
1914
  useCursorForDetectionActive = false;
@@ -1855,27 +1951,29 @@
1855
1951
  });
1856
1952
  var elToIdx = new Map();
1857
1953
  function addMaybeListeners() {
1858
- window.addEventListener("mousemove", handleMouseMoveMaybeDragStart, {
1954
+ dragWindow.addEventListener("mousemove", handleMouseMoveMaybeDragStart, {
1859
1955
  passive: false
1860
1956
  });
1861
- window.addEventListener("touchmove", handleMouseMoveMaybeDragStart, {
1957
+ dragWindow.addEventListener("touchmove", handleMouseMoveMaybeDragStart, {
1862
1958
  passive: false,
1863
1959
  capture: false
1864
1960
  });
1865
- window.addEventListener("mouseup", handleFalseAlarm, {
1961
+ dragWindow.addEventListener("mouseup", handleFalseAlarm, {
1866
1962
  passive: false
1867
1963
  });
1868
- window.addEventListener("touchend", handleFalseAlarm, {
1964
+ dragWindow.addEventListener("touchend", handleFalseAlarm, {
1869
1965
  passive: false
1870
1966
  });
1967
+ dragWindow.addEventListener("pagehide", cancelPendingDrag);
1871
1968
  }
1872
1969
  function removeMaybeListeners() {
1873
- window.removeEventListener("mousemove", handleMouseMoveMaybeDragStart);
1874
- window.removeEventListener("touchmove", handleMouseMoveMaybeDragStart);
1875
- window.removeEventListener("mouseup", handleFalseAlarm);
1876
- window.removeEventListener("touchend", handleFalseAlarm);
1970
+ dragWindow.removeEventListener("mousemove", handleMouseMoveMaybeDragStart);
1971
+ dragWindow.removeEventListener("touchmove", handleMouseMoveMaybeDragStart);
1972
+ dragWindow.removeEventListener("mouseup", handleFalseAlarm);
1973
+ dragWindow.removeEventListener("touchend", handleFalseAlarm);
1974
+ dragWindow.removeEventListener("pagehide", cancelPendingDrag);
1877
1975
  if (touchDragHoldTimer) {
1878
- clearTimeout(touchDragHoldTimer);
1976
+ clearTimeoutIn(touchDragHoldTimer);
1879
1977
  touchDragHoldTimer = undefined;
1880
1978
  touchHoldElapsed = false;
1881
1979
  }
@@ -1883,6 +1981,7 @@
1883
1981
  function cancelPendingDrag() {
1884
1982
  if (pendingDragOwner !== node) return;
1885
1983
  removeMaybeListeners();
1984
+ dragWindow = undefined;
1886
1985
  pendingDragOwner = undefined;
1887
1986
  originalDragTarget = undefined;
1888
1987
  dragStartMousePosition = undefined;
@@ -1894,7 +1993,7 @@
1894
1993
 
1895
1994
  // dragging initiated by touch events prevents onclick from initially firing
1896
1995
  if (e.type === "touchend") {
1897
- var clickEvent = new Event("click", {
1996
+ var clickEvent = new (getWindowOf(e.target).Event)("click", {
1898
1997
  bubbles: true,
1899
1998
  cancelable: true
1900
1999
  });
@@ -1916,10 +2015,8 @@
1916
2015
  };
1917
2016
  if (Math.abs(currentMousePosition.x - dragStartMousePosition.x) >= MIN_MOVEMENT_BEFORE_DRAG_START_PX || Math.abs(currentMousePosition.y - dragStartMousePosition.y) >= MIN_MOVEMENT_BEFORE_DRAG_START_PX) {
1918
2017
  // User started scrolling, cancel drag attempt.
1919
- if (touchDragHoldTimer) {
1920
- clearTimeout(touchDragHoldTimer);
1921
- touchDragHoldTimer = undefined;
1922
- }
2018
+ clearTimeoutIn(touchDragHoldTimer);
2019
+ touchDragHoldTimer = undefined;
1923
2020
  handleFalseAlarm(e);
1924
2021
  }
1925
2022
  return; // Do not preventDefault so scrolling works.
@@ -1970,10 +2067,11 @@
1970
2067
  };
1971
2068
  currentMousePosition = _objectSpread2({}, dragStartMousePosition);
1972
2069
  originalDragTarget = e.currentTarget;
2070
+ dragWindow = getWindowOf(originalDragTarget);
1973
2071
  if (useDelay) {
1974
2072
  var pendingTarget = originalDragTarget;
1975
2073
  touchHoldElapsed = false;
1976
- touchDragHoldTimer = window.setTimeout(function () {
2074
+ touchDragHoldTimer = setTimeoutIn(dragWindow, function () {
1977
2075
  // If this action still owns the same pending gesture, transition it to a drag.
1978
2076
  if (destroyed || pendingDragOwner !== node || originalDragTarget !== pendingTarget) return;
1979
2077
  touchHoldElapsed = true;
@@ -2026,17 +2124,19 @@
2026
2124
  originDropZoneRoot.appendChild(originalDragTarget);
2027
2125
  // have to watch before we hide, otherwise Svelte 5 $state gets confused
2028
2126
  watchDraggedElement();
2127
+ // a consider handler during the first observation can end the drag, for example by removing its window
2128
+ if (!draggedEl) return;
2029
2129
  hideElement(originalDragTarget);
2030
2130
  // after the removal of the original element we can give the shadow element the original item id so that the host zone can find it and render it correctly if it does lookups by id
2031
2131
  shadowElData[ITEM_ID_KEY] = draggedElData[ITEM_ID_KEY];
2032
2132
  // to prevent the outline from disappearing
2033
2133
  draggedEl.focus();
2034
2134
  } else {
2035
- window.requestAnimationFrame(keepOriginalElementInDom);
2135
+ dragWindow.requestAnimationFrame(keepOriginalElementInDom);
2036
2136
  }
2037
2137
  }
2038
- window.requestAnimationFrame(keepOriginalElementInDom);
2039
- styleActiveDropZones(Array.from(typeToDropZones$1.get(config.type)).filter(function (dz) {
2138
+ dragWindow.requestAnimationFrame(keepOriginalElementInDom);
2139
+ styleActiveDropZones(getDropZonesInDragWindow(config.type).filter(function (dz) {
2040
2140
  return dz === originDropZone || !dzToConfig$1.get(dz).dropFromOthersDisabled;
2041
2141
  }), function (dz) {
2042
2142
  return dzToConfig$1.get(dz).dropTargetStyle;
@@ -2047,26 +2147,28 @@
2047
2147
  // removing the original element by removing its data entry
2048
2148
  items.splice(currentIdx, 1, shadowElData);
2049
2149
  unlockOriginDzMinDimensions = preventShrinking(originDropZone);
2050
- dispatchConsiderEvent(originDropZone, items, {
2051
- trigger: TRIGGERS.DRAG_STARTED,
2052
- id: draggedElData[ITEM_ID_KEY],
2053
- source: SOURCES.POINTER
2054
- });
2055
2150
 
2056
- // handing over to global handlers - starting to watch the element
2057
- window.addEventListener("mousemove", handleMouseMove, {
2151
+ // handing over to global handlers - starting to watch the element. This happens before the consider event because
2152
+ // its handler can remove the drag's window, which ends the drag.
2153
+ dragWindow.addEventListener("mousemove", handleMouseMove, {
2058
2154
  passive: false
2059
2155
  });
2060
- window.addEventListener("touchmove", handleMouseMove, {
2156
+ dragWindow.addEventListener("touchmove", handleMouseMove, {
2061
2157
  passive: false,
2062
2158
  capture: false
2063
2159
  });
2064
- window.addEventListener("mouseup", handleDrop$1, {
2160
+ dragWindow.addEventListener("mouseup", handleDrop$1, {
2065
2161
  passive: false
2066
2162
  });
2067
- window.addEventListener("touchend", handleDrop$1, {
2163
+ dragWindow.addEventListener("touchend", handleDrop$1, {
2068
2164
  passive: false
2069
2165
  });
2166
+ dragWindow.addEventListener("pagehide", handleDragWindowHidden);
2167
+ dispatchConsiderEvent(originDropZone, items, {
2168
+ trigger: TRIGGERS.DRAG_STARTED,
2169
+ id: draggedElData[ITEM_ID_KEY],
2170
+ source: SOURCES.POINTER
2171
+ });
2070
2172
  }
2071
2173
  function configure(_ref2) {
2072
2174
  var _ref2$items = _ref2.items,
@@ -2095,6 +2197,9 @@
2095
2197
  dropAnimationDisabled = _ref2$dropAnimationDi === void 0 ? false : _ref2$dropAnimationDi,
2096
2198
  _ref2$delayTouchStart = _ref2.delayTouchStart,
2097
2199
  delayTouchStartOpt = _ref2$delayTouchStart === void 0 ? false : _ref2$delayTouchStart;
2200
+ var isInDragWindow = isWorkingOnPreviousDrag && node.ownerDocument.defaultView === dragWindow;
2201
+ var wasDropTarget = node === originDropZone || config.type === draggedElType && !config.dropFromOthersDisabled;
2202
+ var isDropTarget = node === originDropZone || newType === draggedElType && !dropFromOthersDisabled;
2098
2203
  config.dropAnimationDurationMs = dropAnimationDurationMs;
2099
2204
  var effectiveDelayMs = 0;
2100
2205
  if (delayTouchStartOpt === true) {
@@ -2115,41 +2220,26 @@
2115
2220
  config.useCursorForDetection = useCursorForDetection;
2116
2221
  config.dropAnimationDisabled = dropAnimationDisabled;
2117
2222
 
2118
- // realtime update for dropTargetStyle
2119
- if (initialized && isWorkingOnPreviousDrag && !finalizingPreviousDrag && (!areObjectsShallowEqual(dropTargetStyle, config.dropTargetStyle) || !areArraysShallowEqualSameOrder(dropTargetClasses, config.dropTargetClasses))) {
2120
- styleInactiveDropZones([node], function () {
2121
- return config.dropTargetStyle;
2122
- }, function () {
2123
- return dropTargetClasses;
2124
- });
2125
- styleActiveDropZones([node], function () {
2126
- return dropTargetStyle;
2127
- }, function () {
2128
- return dropTargetClasses;
2129
- });
2130
- }
2131
- config.dropTargetStyle = dropTargetStyle;
2132
- config.dropTargetClasses = _toConsumableArray(dropTargetClasses);
2133
-
2134
- // realtime update for dropFromOthersDisabled
2135
- function getConfigProp(dz, propName) {
2136
- return dzToConfig$1.get(dz) ? dzToConfig$1.get(dz)[propName] : config[propName];
2137
- }
2138
- if (initialized && isWorkingOnPreviousDrag && config.dropFromOthersDisabled !== dropFromOthersDisabled) {
2139
- if (dropFromOthersDisabled) {
2140
- styleInactiveDropZones([node], function (dz) {
2141
- return getConfigProp(dz, "dropTargetStyle");
2142
- }, function (dz) {
2143
- return getConfigProp(dz, "dropTargetClasses");
2223
+ // Realtime styling follows the same eligibility as drag start. Remove the previously applied styles and
2224
+ // classes before storing the new options, including when disabling incoming drops and changing styles together.
2225
+ if (initialized && isInDragWindow && !finalizingPreviousDrag && (wasDropTarget !== isDropTarget || !areObjectsShallowEqual(dropTargetStyle, config.dropTargetStyle) || !areArraysShallowEqualSameOrder(dropTargetClasses, config.dropTargetClasses))) {
2226
+ if (wasDropTarget) {
2227
+ styleInactiveDropZones([node], function () {
2228
+ return config.dropTargetStyle;
2229
+ }, function () {
2230
+ return config.dropTargetClasses;
2144
2231
  });
2145
- } else {
2146
- styleActiveDropZones([node], function (dz) {
2147
- return getConfigProp(dz, "dropTargetStyle");
2148
- }, function (dz) {
2149
- return getConfigProp(dz, "dropTargetClasses");
2232
+ }
2233
+ if (isDropTarget) {
2234
+ styleActiveDropZones([node], function () {
2235
+ return dropTargetStyle;
2236
+ }, function () {
2237
+ return dropTargetClasses;
2150
2238
  });
2151
2239
  }
2152
2240
  }
2241
+ config.dropTargetStyle = dropTargetStyle;
2242
+ config.dropTargetClasses = _toConsumableArray(dropTargetClasses);
2153
2243
  config.dropFromOthersDisabled = dropFromOthersDisabled;
2154
2244
  dzToConfig$1.set(node, config);
2155
2245
  registerDropZone$1(node, newType);
@@ -2174,10 +2264,9 @@
2174
2264
  }
2175
2265
  // updating the idx
2176
2266
  elToIdx.set(draggableEl, idx);
2177
- if (!initialized) {
2178
- initialized = true;
2179
- }
2180
2267
  }
2268
+ // Empty zones are initialized too: they can receive active-target styling before they have any children.
2269
+ initialized = true;
2181
2270
  }
2182
2271
  configure(options);
2183
2272
  return {
@@ -2274,14 +2363,17 @@
2274
2363
  keyboardDragTrigger: DEFAULT_KEYBOARD_DRAG_TRIGGER
2275
2364
  };
2276
2365
  var ALERT_DIV_ID = "dnd-action-aria-alert";
2277
- var alertsDiv;
2278
- function initAriaOnBrowser() {
2279
- if (alertsDiv) {
2366
+ // A map from a document to its alerts div. A zone can live in another window than the one that loaded this module, so each
2367
+ // document with zones gets its own alerts and instructions. It is a Map rather than a WeakMap so the instructions in every
2368
+ // document can be re-rendered; destroyAria removes the entry.
2369
+ var docToAlertsDiv = new Map();
2370
+ function initAriaOnBrowser(doc) {
2371
+ if (docToAlertsDiv.has(doc)) {
2280
2372
  // it is already initialized
2281
2373
  return;
2282
2374
  }
2283
2375
  // setting the dynamic alerts
2284
- alertsDiv = document.createElement("div");
2376
+ var alertsDiv = doc.createElement("div");
2285
2377
  (function initAlertsDiv() {
2286
2378
  alertsDiv.id = ALERT_DIV_ID;
2287
2379
  // tab index -1 makes the alert be read twice on chrome for some reason
@@ -2295,45 +2387,60 @@
2295
2387
  alertsDiv.style.width = "0";
2296
2388
  alertsDiv.setAttribute("role", "alert");
2297
2389
  })();
2298
- document.body.prepend(alertsDiv);
2390
+ doc.body.prepend(alertsDiv);
2391
+ // forget the documents whose window is gone
2392
+ docToAlertsDiv.forEach(function (_, knownDoc) {
2393
+ return !knownDoc.defaultView && docToAlertsDiv["delete"](knownDoc);
2394
+ });
2395
+ docToAlertsDiv.set(doc, alertsDiv);
2299
2396
 
2300
2397
  // setting the instructions
2301
2398
  Object.entries(INSTRUCTION_ID_TO_STRING_KEY).forEach(function (_ref7) {
2302
2399
  var _ref8 = _slicedToArray(_ref7, 2),
2303
2400
  id = _ref8[0],
2304
2401
  key = _ref8[1];
2305
- return document.body.prepend(instructionToHiddenDiv(id, formatWithFallback(key, instructionCtx)));
2402
+ return doc.body.prepend(instructionToHiddenDiv(doc, id, formatWithFallback(key, instructionCtx)));
2306
2403
  });
2307
2404
  }
2308
2405
 
2309
2406
  /**
2310
2407
  * Initializes the static aria instructions so they can be attached to zones
2408
+ * @param {Document} [doc] - the document of the zones, defaults to the one that loaded this module
2311
2409
  * @return {{DND_ZONE_ACTIVE: string, DND_ZONE_DRAG_DISABLED: string} | null} - the IDs for static aria instruction (to be used via aria-describedby) or null on the server
2312
2410
  */
2313
- function initAria() {
2411
+ function initAria(doc) {
2314
2412
  if (isOnServer) return null;
2315
- if (document.readyState === "complete") {
2316
- initAriaOnBrowser();
2413
+ // eslint-disable-next-line no-restricted-globals -- the documented default
2414
+ doc = doc || document;
2415
+ if (doc.readyState === "complete") {
2416
+ initAriaOnBrowser(doc);
2317
2417
  } else {
2318
- window.addEventListener("DOMContentLoaded", initAriaOnBrowser);
2418
+ doc.addEventListener("DOMContentLoaded", function () {
2419
+ return initAriaOnBrowser(doc);
2420
+ });
2319
2421
  }
2320
2422
  return _objectSpread2({}, INSTRUCTION_IDs$1);
2321
2423
  }
2322
2424
 
2323
2425
  /**
2324
- * Removes all the artifacts (dom elements) added by this module
2426
+ * Removes all the artifacts (dom elements) added by this module to the given document
2427
+ * @param {Document} [doc] - defaults to the document that loaded this module
2325
2428
  */
2326
- function destroyAria() {
2327
- if (isOnServer || !alertsDiv) return;
2429
+ function destroyAria(doc) {
2430
+ if (isOnServer) return;
2431
+ // eslint-disable-next-line no-restricted-globals -- the documented default
2432
+ doc = doc || document;
2433
+ var alertsDiv = docToAlertsDiv.get(doc);
2434
+ if (!alertsDiv) return;
2328
2435
  Object.keys(INSTRUCTION_ID_TO_STRING_KEY).forEach(function (id) {
2329
- var _document$getElementB;
2330
- return (_document$getElementB = document.getElementById(id)) === null || _document$getElementB === void 0 ? void 0 : _document$getElementB.remove();
2436
+ var _doc$getElementById;
2437
+ return (_doc$getElementById = doc.getElementById(id)) === null || _doc$getElementById === void 0 ? void 0 : _doc$getElementById.remove();
2331
2438
  });
2332
2439
  alertsDiv.remove();
2333
- alertsDiv = undefined;
2440
+ docToAlertsDiv["delete"](doc);
2334
2441
  }
2335
- function instructionToHiddenDiv(id, txt) {
2336
- var div = document.createElement("div");
2442
+ function instructionToHiddenDiv(doc, id, txt) {
2443
+ var div = doc.createElement("div");
2337
2444
  div.id = id;
2338
2445
  renderInstruction(div, txt);
2339
2446
  div.style.display = "none";
@@ -2343,7 +2450,7 @@
2343
2450
  }
2344
2451
  function renderInstruction(div, txt) {
2345
2452
  div.replaceChildren();
2346
- var paragraph = document.createElement("p");
2453
+ var paragraph = div.ownerDocument.createElement("p");
2347
2454
  paragraph.textContent = txt;
2348
2455
  div.appendChild(paragraph);
2349
2456
  }
@@ -2351,14 +2458,19 @@
2351
2458
  /**
2352
2459
  * Will make the screen reader alert the provided text to the user
2353
2460
  * @param {string} txt
2461
+ * @param {Document} [doc] - the document to announce in, for a zone in another window such as an iframe or a popup.
2462
+ * Defaults to the document that loaded this module.
2354
2463
  */
2355
- function alertToScreenReader(txt) {
2464
+ function alertToScreenReader(txt, doc) {
2356
2465
  if (isOnServer) return;
2357
- if (!alertsDiv) {
2358
- initAriaOnBrowser();
2466
+ // eslint-disable-next-line no-restricted-globals -- the documented default
2467
+ doc = doc || document;
2468
+ if (!docToAlertsDiv.has(doc)) {
2469
+ initAriaOnBrowser(doc);
2359
2470
  }
2471
+ var alertsDiv = docToAlertsDiv.get(doc);
2360
2472
  alertsDiv.innerHTML = "";
2361
- var alertText = document.createTextNode(txt);
2473
+ var alertText = doc.createTextNode(txt);
2362
2474
  alertsDiv.appendChild(alertText);
2363
2475
  // this is needed for Safari
2364
2476
  alertsDiv.style.display = "none";
@@ -2428,12 +2540,14 @@
2428
2540
  }
2429
2541
  function refreshInstructions() {
2430
2542
  if (isOnServer) return;
2431
- Object.entries(INSTRUCTION_ID_TO_STRING_KEY).forEach(function (_ref9) {
2432
- var _ref10 = _slicedToArray(_ref9, 2),
2433
- id = _ref10[0],
2434
- key = _ref10[1];
2435
- var div = document.getElementById(id);
2436
- if (div) renderInstruction(div, formatWithFallback(key, instructionCtx));
2543
+ docToAlertsDiv.forEach(function (alertsDiv, doc) {
2544
+ return Object.entries(INSTRUCTION_ID_TO_STRING_KEY).forEach(function (_ref9) {
2545
+ var _ref10 = _slicedToArray(_ref9, 2),
2546
+ id = _ref10[0],
2547
+ key = _ref10[1];
2548
+ var div = doc.getElementById(id);
2549
+ if (div) renderInstruction(div, formatWithFallback(key, instructionCtx));
2550
+ });
2437
2551
  });
2438
2552
  }
2439
2553
 
@@ -2453,9 +2567,10 @@
2453
2567
  * escape because this function runs during the drag lifecycle.
2454
2568
  * @param {string} key - one of the keys accepted by setAriaStrings
2455
2569
  * @param {Object} [ctx] - the interpolation context for that key
2570
+ * @param {Document} [doc] - the document of the zone the message is about, defaults to the one that loaded this module
2456
2571
  */
2457
- function announceToScreenReader(key, ctx) {
2458
- alertToScreenReader(formatWithFallback(key, ctx));
2572
+ function announceToScreenReader(key, ctx, doc) {
2573
+ alertToScreenReader(formatWithFallback(key, ctx), doc);
2459
2574
  }
2460
2575
 
2461
2576
  var activeTrigger = DEFAULT_KEYBOARD_DRAG_TRIGGER;
@@ -2503,12 +2618,20 @@
2503
2618
  var focusedItem;
2504
2619
  var focusedItemId;
2505
2620
  var focusedItemLabel = "";
2621
+ // Like pointer dragging, a keyboard drag only has destinations in the document it started in.
2622
+ var dragDocument;
2506
2623
  var allDragTargets = new WeakSet();
2507
2624
  var elToKeyDownListeners = new WeakMap();
2508
2625
  var elToFocusListeners = new WeakMap();
2509
2626
  var dzToHandles = new Map();
2510
2627
  var dzToConfig = new Map();
2511
2628
  var typeToDropZones = new Map();
2629
+ // Each document with zones gets its own aria elements, because a zone can live in another window than the one that loaded
2630
+ // this module. That document and the same-origin documents around it, up to the loading document, get keydown, click and
2631
+ // pagehide handlers, so that Escape and a click outside end a drag from any of them. A zone is released from the documents
2632
+ // it registered with, even if it has moved to another document since.
2633
+ var dzToDocuments = new Map();
2634
+ var documentToRegistration = new Map();
2512
2635
 
2513
2636
  /* TODO (potentially)
2514
2637
  * what's the deal with the black border of voice-reader not following focus?
@@ -2518,18 +2641,83 @@
2518
2641
  var INSTRUCTION_IDs;
2519
2642
 
2520
2643
  /* drop-zones registration management */
2644
+ function getDocumentsAround(doc) {
2645
+ var docs = [doc];
2646
+ // eslint-disable-next-line no-restricted-globals -- the documents above the loading one belong to whatever hosts the app
2647
+ for (var frame = (_doc$defaultView = doc.defaultView) === null || _doc$defaultView === void 0 ? void 0 : _doc$defaultView.frameElement; frame && docs[docs.length - 1] !== document; frame = getWindowOf(frame).frameElement) {
2648
+ var _doc$defaultView;
2649
+ docs.push(frame.ownerDocument);
2650
+ }
2651
+ return docs;
2652
+ }
2653
+ function retainDocuments(dropZoneEl) {
2654
+ var docs = getDocumentsAround(dropZoneEl.ownerDocument);
2655
+ docs.forEach(function (doc, idx) {
2656
+ var registration = documentToRegistration.get(doc);
2657
+ if (!registration) {
2658
+ printDebug(function () {
2659
+ return "adding global keydown and click handlers";
2660
+ });
2661
+ // Handlers of its own, so that removing them through a window that has since navigated to another document
2662
+ // leaves the handlers of that document alone
2663
+ var handlers = {
2664
+ keydown: globalKeyDownHandler,
2665
+ click: globalClickHandler,
2666
+ pagehide: handleWindowHidden
2667
+ };
2668
+ registration = {
2669
+ win: doc.defaultView,
2670
+ zoneCount: 0,
2671
+ documentCount: 0,
2672
+ handlers: {}
2673
+ };
2674
+ Object.entries(handlers).forEach(function (_ref) {
2675
+ var _registration$win;
2676
+ var _ref2 = _slicedToArray(_ref, 2),
2677
+ type = _ref2[0],
2678
+ handler = _ref2[1];
2679
+ registration.handlers[type] = function (e) {
2680
+ return handler(e);
2681
+ };
2682
+ (_registration$win = registration.win) === null || _registration$win === void 0 ? void 0 : _registration$win.addEventListener(type, registration.handlers[type]);
2683
+ });
2684
+ documentToRegistration.set(doc, registration);
2685
+ }
2686
+ registration.documentCount++;
2687
+ if (idx === 0 && registration.zoneCount++ === 0) {
2688
+ INSTRUCTION_IDs = initAria(doc);
2689
+ }
2690
+ });
2691
+ dzToDocuments.set(dropZoneEl, docs);
2692
+ }
2693
+ function releaseDocuments(dropZoneEl) {
2694
+ var docs = dzToDocuments.get(dropZoneEl);
2695
+ if (!docs) return;
2696
+ dzToDocuments["delete"](dropZoneEl);
2697
+ docs.forEach(function (doc, idx) {
2698
+ var registration = documentToRegistration.get(doc);
2699
+ if (idx === 0 && --registration.zoneCount === 0) {
2700
+ destroyAria(doc);
2701
+ }
2702
+ if (--registration.documentCount > 0) return;
2703
+ printDebug(function () {
2704
+ return "removing global keydown and click handlers";
2705
+ });
2706
+ documentToRegistration["delete"](doc);
2707
+ Object.entries(registration.handlers).forEach(function (_ref3) {
2708
+ var _registration$win2;
2709
+ var _ref4 = _slicedToArray(_ref3, 2),
2710
+ type = _ref4[0],
2711
+ handler = _ref4[1];
2712
+ return (_registration$win2 = registration.win) === null || _registration$win2 === void 0 ? void 0 : _registration$win2.removeEventListener(type, handler);
2713
+ });
2714
+ });
2715
+ }
2521
2716
  function registerDropZone(dropZoneEl, type) {
2717
+ var _dzToDocuments$get;
2522
2718
  printDebug(function () {
2523
2719
  return "registering drop-zone if absent";
2524
2720
  });
2525
- if (typeToDropZones.size === 0) {
2526
- printDebug(function () {
2527
- return "adding global keydown and click handlers";
2528
- });
2529
- INSTRUCTION_IDs = initAria();
2530
- window.addEventListener("keydown", globalKeyDownHandler);
2531
- window.addEventListener("click", globalClickHandler);
2532
- }
2533
2721
  if (!typeToDropZones.has(type)) {
2534
2722
  typeToDropZones.set(type, new Set());
2535
2723
  }
@@ -2537,6 +2725,11 @@
2537
2725
  typeToDropZones.get(type).add(dropZoneEl);
2538
2726
  incrementActiveDropZoneCount();
2539
2727
  }
2728
+ // a zone that moved to another document since it registered takes its registration along
2729
+ if (((_dzToDocuments$get = dzToDocuments.get(dropZoneEl)) === null || _dzToDocuments$get === void 0 ? void 0 : _dzToDocuments$get[0]) !== dropZoneEl.ownerDocument) {
2730
+ releaseDocuments(dropZoneEl);
2731
+ retainDocuments(dropZoneEl);
2732
+ }
2540
2733
  }
2541
2734
  function unregisterDropZone(dropZoneEl, type) {
2542
2735
  printDebug(function () {
@@ -2551,15 +2744,7 @@
2551
2744
  if (dropZones.size === 0) {
2552
2745
  typeToDropZones["delete"](type);
2553
2746
  }
2554
- if (typeToDropZones.size === 0) {
2555
- printDebug(function () {
2556
- return "removing global keydown and click handlers";
2557
- });
2558
- window.removeEventListener("keydown", globalKeyDownHandler);
2559
- window.removeEventListener("click", globalClickHandler);
2560
- INSTRUCTION_IDs = undefined;
2561
- destroyAria();
2562
- }
2747
+ releaseDocuments(dropZoneEl);
2563
2748
  }
2564
2749
  function globalKeyDownHandler(e) {
2565
2750
  if (!isDragging) return;
@@ -2571,21 +2756,50 @@
2571
2756
  }
2572
2757
  }
2573
2758
  }
2574
- function globalClickHandler() {
2759
+ function globalClickHandler(e) {
2575
2760
  if (!isDragging) return;
2576
- if (!allDragTargets.has(document.activeElement)) {
2761
+ if (!allDragTargets.has(e.currentTarget.document.activeElement)) {
2577
2762
  printDebug(function () {
2578
2763
  return "clicked outside of any draggable";
2579
2764
  });
2580
2765
  handleDrop();
2581
2766
  }
2582
2767
  }
2583
- function getActiveDragTabIndex(dropZoneEl, config) {
2584
- return dropZoneEl === focusedDz || focusedItem.contains(dropZoneEl) || config.dropFromOthersDisabled || config.type !== draggedItemType ? -1 : 0;
2768
+
2769
+ // The focused zone's window is going away: it was closed or navigated, or its iframe was removed. End the drag so the
2770
+ // zones in the other windows become tab stops again.
2771
+ function handleWindowHidden(e) {
2772
+ if (isDragging && focusedDz && getWindowOf(focusedDz) === e.currentTarget) {
2773
+ printDebug(function () {
2774
+ return "the focused zone's window is going away";
2775
+ });
2776
+ handleDrop();
2777
+ }
2778
+ }
2779
+
2780
+ // Tab can cross iframe boundaries, but a destination must still belong to the drag's own document.
2781
+ function isInDragDocument(dropZoneEl) {
2782
+ var doc = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : dragDocument;
2783
+ return dropZoneEl.ownerDocument === doc;
2784
+ }
2785
+ function isAboveOrLeftOf(el, otherEl) {
2786
+ var position = el.getBoundingClientRect();
2787
+ var otherPosition = otherEl.getBoundingClientRect();
2788
+ return position.top < otherPosition.top || position.left < otherPosition.left;
2789
+ }
2790
+
2791
+ // Tab order, focus-driven transfers, styling and announcements must agree about which zones can receive the item.
2792
+ // tabindex=-1 alone is not a restriction: a click or consumer code can still focus a zone.
2793
+ function isKeyboardDropTarget(dropZoneEl) {
2794
+ var config = dzToConfig.get(dropZoneEl);
2795
+ return config.type === draggedItemType && !config.dropFromOthersDisabled && !focusedItem.contains(dropZoneEl) && isInDragDocument(dropZoneEl);
2796
+ }
2797
+ function getActiveDragTabIndex(dropZoneEl) {
2798
+ return dropZoneEl !== focusedDz && isKeyboardDropTarget(dropZoneEl) ? 0 : -1;
2585
2799
  }
2586
2800
  function refreshActiveDragTabIndices() {
2587
- dzToConfig.forEach(function (config, dropZoneEl) {
2588
- dropZoneEl.tabIndex = getActiveDragTabIndex(dropZoneEl, config);
2801
+ dzToConfig.forEach(function (_, dropZoneEl) {
2802
+ dropZoneEl.tabIndex = getActiveDragTabIndex(dropZoneEl);
2589
2803
  });
2590
2804
  }
2591
2805
  function grabIsAlive() {
@@ -2605,8 +2819,10 @@
2605
2819
  });
2606
2820
  if (!isDragging) return;
2607
2821
  var newlyFocusedDz = e.currentTarget;
2608
- if (newlyFocusedDz === focusedDz) return;
2822
+ if (newlyFocusedDz === focusedDz || !isKeyboardDropTarget(newlyFocusedDz)) return;
2609
2823
  if (!grabIsAlive()) return;
2824
+ var dzFrom = focusedDz;
2825
+ var movedItemId = focusedItemId;
2610
2826
  focusedDzLabel = newlyFocusedDz.getAttribute("aria-label") || "";
2611
2827
  var _dzToConfig$get = dzToConfig.get(focusedDz),
2612
2828
  originItems = _dzToConfig$get.items;
@@ -2618,7 +2834,7 @@
2618
2834
  var _dzToConfig$get2 = dzToConfig.get(newlyFocusedDz),
2619
2835
  targetItems = _dzToConfig$get2.items,
2620
2836
  autoAriaDisabled = _dzToConfig$get2.autoAriaDisabled;
2621
- if (newlyFocusedDz.getBoundingClientRect().top < focusedDz.getBoundingClientRect().top || newlyFocusedDz.getBoundingClientRect().left < focusedDz.getBoundingClientRect().left) {
2837
+ if (isAboveOrLeftOf(newlyFocusedDz, focusedDz)) {
2622
2838
  targetItems.push(itemToMove);
2623
2839
  if (!autoAriaDisabled) {
2624
2840
  announceToScreenReader("movedToZoneEnd", {
@@ -2626,7 +2842,7 @@
2626
2842
  zoneLabel: focusedDzLabel,
2627
2843
  position: targetItems.length,
2628
2844
  count: targetItems.length
2629
- });
2845
+ }, newlyFocusedDz.ownerDocument);
2630
2846
  }
2631
2847
  } else {
2632
2848
  targetItems.unshift(itemToMove);
@@ -2636,12 +2852,11 @@
2636
2852
  zoneLabel: focusedDzLabel,
2637
2853
  position: 1,
2638
2854
  count: targetItems.length
2639
- });
2855
+ }, newlyFocusedDz.ownerDocument);
2640
2856
  }
2641
2857
  }
2642
- var dzFrom = focusedDz;
2643
- var movedItemId = focusedItemId;
2644
- focusedDz = newlyFocusedDz;
2858
+ // An announcement formatter can end the drag, for example by removing its window. The item still moves.
2859
+ if (isDragging) focusedDz = newlyFocusedDz;
2645
2860
  dispatchFinalizeEvent(dzFrom, originItems, {
2646
2861
  trigger: TRIGGERS.DROPPED_INTO_ANOTHER,
2647
2862
  id: movedItemId,
@@ -2656,8 +2871,8 @@
2656
2871
  }
2657
2872
  }
2658
2873
  function triggerAllDzsUpdate() {
2659
- dzToHandles.forEach(function (_ref, dz) {
2660
- var update = _ref.update;
2874
+ dzToHandles.forEach(function (_ref5, dz) {
2875
+ var update = _ref5.update;
2661
2876
  return update(dzToConfig.get(dz));
2662
2877
  });
2663
2878
  }
@@ -2672,6 +2887,20 @@
2672
2887
  var droppedItemId = focusedItemId;
2673
2888
  var droppedItemType = draggedItemType;
2674
2889
  if (!droppedConfig) return;
2890
+ var droppedItemLabel = focusedItemLabel;
2891
+ var droppedDzLabel = focusedDzLabel;
2892
+ var droppedDocument = dragDocument;
2893
+ // Clear global drag state before running any consumer code: an announcement formatter, a blur handler or a consider
2894
+ // handler. A synchronous handler may destroy the focused zone or remove its window, and neither must recursively
2895
+ // enter handleDrop.
2896
+ focusedItem = null;
2897
+ focusedItemId = null;
2898
+ focusedItemLabel = "";
2899
+ draggedItemType = null;
2900
+ focusedDz = null;
2901
+ focusedDzLabel = "";
2902
+ dragDocument = undefined;
2903
+ isDragging = false;
2675
2904
  if (!droppedConfig.autoAriaDisabled) {
2676
2905
  // Include the destination and final position so localized messages can describe the completed drop.
2677
2906
  var droppedItems = droppedConfig.items;
@@ -2679,24 +2908,16 @@
2679
2908
  return item[ITEM_ID_KEY] === droppedItemId;
2680
2909
  });
2681
2910
  announceToScreenReader("dropped", {
2682
- itemLabel: focusedItemLabel,
2683
- zoneLabel: focusedDzLabel,
2911
+ itemLabel: droppedItemLabel,
2912
+ zoneLabel: droppedDzLabel,
2684
2913
  position: (droppedIdx < 0 ? 0 : droppedIdx) + 1,
2685
2914
  count: droppedItems.length
2686
- });
2915
+ }, droppedDz.ownerDocument);
2687
2916
  }
2688
- if (allDragTargets.has(document.activeElement)) {
2689
- document.activeElement.blur();
2917
+ var activeElement = droppedDz.ownerDocument.activeElement;
2918
+ if (allDragTargets.has(activeElement)) {
2919
+ activeElement.blur();
2690
2920
  }
2691
- // Clear global drag state before dispatching. A synchronous handler may destroy the
2692
- // focused zone, and unregisterDropZone must not recursively enter handleDrop.
2693
- focusedItem = null;
2694
- focusedItemId = null;
2695
- focusedItemLabel = "";
2696
- draggedItemType = null;
2697
- focusedDz = null;
2698
- focusedDzLabel = "";
2699
- isDragging = false;
2700
2921
  if (dispatchConsider) {
2701
2922
  dispatchConsiderEvent(droppedDz, droppedConfig.items, {
2702
2923
  trigger: TRIGGERS.DRAG_STOPPED,
@@ -2706,7 +2927,9 @@
2706
2927
  }
2707
2928
  var dropZones = typeToDropZones.get(droppedItemType);
2708
2929
  if (dropZones) {
2709
- styleInactiveDropZones(dropZones, function (dz) {
2930
+ styleInactiveDropZones(Array.from(dropZones).filter(function (dz) {
2931
+ return isInDragDocument(dz, droppedDocument);
2932
+ }), function (dz) {
2710
2933
  return dzToConfig.get(dz).dropTargetStyle;
2711
2934
  }, function (dz) {
2712
2935
  return dzToConfig.get(dz).dropTargetClasses;
@@ -2732,6 +2955,10 @@
2732
2955
  if (arr.length <= 1) return;
2733
2956
  arr.splice(j, 1, arr.splice(i, 1, arr[j])[0]);
2734
2957
  }
2958
+ function ownsArrowKey(e) {
2959
+ // Only the grabbed item owns reorder keys. Nested controls and other items keep their own key behaviour.
2960
+ return isDragging && node === focusedDz && isInDragDocument(node) && e.currentTarget === focusedItem && e.target === focusedItem;
2961
+ }
2735
2962
  function handleKeyDown(e) {
2736
2963
  printDebug(function () {
2737
2964
  return ["handling key down", e.key];
@@ -2762,7 +2989,7 @@
2762
2989
  case "ArrowDown":
2763
2990
  case "ArrowRight":
2764
2991
  {
2765
- if (!isDragging) return;
2992
+ if (!ownsArrowKey(e)) return;
2766
2993
  e.preventDefault(); // prevent scrolling
2767
2994
  e.stopPropagation();
2768
2995
  var _dzToConfig$get3 = dzToConfig.get(node),
@@ -2779,8 +3006,10 @@
2779
3006
  zoneLabel: focusedDzLabel,
2780
3007
  position: idx + 2,
2781
3008
  count: items.length
2782
- });
3009
+ }, node.ownerDocument);
2783
3010
  }
3011
+ // An announcement formatter can end or move the drag, for example by removing its window.
3012
+ if (!ownsArrowKey(e)) break;
2784
3013
  swap(items, idx, idx + 1);
2785
3014
  dispatchFinalizeEvent(node, items, {
2786
3015
  trigger: TRIGGERS.DROPPED_INTO_ZONE,
@@ -2793,7 +3022,7 @@
2793
3022
  case "ArrowUp":
2794
3023
  case "ArrowLeft":
2795
3024
  {
2796
- if (!isDragging) return;
3025
+ if (!ownsArrowKey(e)) return;
2797
3026
  e.preventDefault(); // prevent scrolling
2798
3027
  e.stopPropagation();
2799
3028
  var _dzToConfig$get4 = dzToConfig.get(node),
@@ -2810,8 +3039,10 @@
2810
3039
  zoneLabel: focusedDzLabel,
2811
3040
  position: _idx,
2812
3041
  count: _items.length
2813
- });
3042
+ }, node.ownerDocument);
2814
3043
  }
3044
+ // An announcement formatter can end or move the drag, for example by removing its window.
3045
+ if (!ownsArrowKey(e)) break;
2815
3046
  swap(_items, _idx, _idx - 1);
2816
3047
  dispatchFinalizeEvent(node, _items, {
2817
3048
  trigger: TRIGGERS.DROPPED_INTO_ZONE,
@@ -2832,8 +3063,9 @@
2832
3063
  focusedDzLabel = node.getAttribute("aria-label") || "";
2833
3064
  draggedItemType = config.type;
2834
3065
  isDragging = true;
3066
+ dragDocument = node.ownerDocument;
2835
3067
  var dropTargets = Array.from(typeToDropZones.get(config.type)).filter(function (dz) {
2836
- return dz === focusedDz || !dzToConfig.get(dz).dropFromOthersDisabled;
3068
+ return dz === focusedDz || isKeyboardDropTarget(dz);
2837
3069
  });
2838
3070
  styleActiveDropZones(dropTargets, function (dz) {
2839
3071
  return dzToConfig.get(dz).dropTargetStyle;
@@ -2852,8 +3084,10 @@
2852
3084
  position: (startIdx < 0 ? 0 : startIdx) + 1,
2853
3085
  count: startItems.length,
2854
3086
  canMoveBetweenZones: dropTargets.length > 1
2855
- });
3087
+ }, node.ownerDocument);
2856
3088
  }
3089
+ // an announcement formatter can end the drag, for example by removing its window
3090
+ if (!isDragging) return;
2857
3091
  dispatchConsiderEvent(node, dzToConfig.get(node).items, {
2858
3092
  trigger: TRIGGERS.DRAG_STARTED,
2859
3093
  id: focusedItemId,
@@ -2878,25 +3112,25 @@
2878
3112
  focusedItemId = items[focusedItemIdx][ITEM_ID_KEY];
2879
3113
  focusedItemLabel = children[focusedItemIdx].getAttribute("aria-label") || "";
2880
3114
  }
2881
- function configure(_ref2) {
2882
- var _ref2$items = _ref2.items,
2883
- items = _ref2$items === void 0 ? [] : _ref2$items,
2884
- _ref2$type = _ref2.type,
2885
- newType = _ref2$type === void 0 ? DEFAULT_DROP_ZONE_TYPE : _ref2$type,
2886
- _ref2$dragDisabled = _ref2.dragDisabled,
2887
- dragDisabled = _ref2$dragDisabled === void 0 ? false : _ref2$dragDisabled,
2888
- _ref2$zoneTabIndex = _ref2.zoneTabIndex,
2889
- zoneTabIndex = _ref2$zoneTabIndex === void 0 ? 0 : _ref2$zoneTabIndex,
2890
- _ref2$zoneItemTabInde = _ref2.zoneItemTabIndex,
2891
- zoneItemTabIndex = _ref2$zoneItemTabInde === void 0 ? 0 : _ref2$zoneItemTabInde,
2892
- _ref2$dropFromOthersD = _ref2.dropFromOthersDisabled,
2893
- dropFromOthersDisabled = _ref2$dropFromOthersD === void 0 ? false : _ref2$dropFromOthersD,
2894
- _ref2$dropTargetStyle = _ref2.dropTargetStyle,
2895
- dropTargetStyle = _ref2$dropTargetStyle === void 0 ? DEFAULT_DROP_TARGET_STYLE : _ref2$dropTargetStyle,
2896
- _ref2$dropTargetClass = _ref2.dropTargetClasses,
2897
- dropTargetClasses = _ref2$dropTargetClass === void 0 ? [] : _ref2$dropTargetClass,
2898
- _ref2$autoAriaDisable = _ref2.autoAriaDisabled,
2899
- autoAriaDisabled = _ref2$autoAriaDisable === void 0 ? false : _ref2$autoAriaDisable;
3115
+ function configure(_ref6) {
3116
+ var _ref6$items = _ref6.items,
3117
+ items = _ref6$items === void 0 ? [] : _ref6$items,
3118
+ _ref6$type = _ref6.type,
3119
+ newType = _ref6$type === void 0 ? DEFAULT_DROP_ZONE_TYPE : _ref6$type,
3120
+ _ref6$dragDisabled = _ref6.dragDisabled,
3121
+ dragDisabled = _ref6$dragDisabled === void 0 ? false : _ref6$dragDisabled,
3122
+ _ref6$zoneTabIndex = _ref6.zoneTabIndex,
3123
+ zoneTabIndex = _ref6$zoneTabIndex === void 0 ? 0 : _ref6$zoneTabIndex,
3124
+ _ref6$zoneItemTabInde = _ref6.zoneItemTabIndex,
3125
+ zoneItemTabIndex = _ref6$zoneItemTabInde === void 0 ? 0 : _ref6$zoneItemTabInde,
3126
+ _ref6$dropFromOthersD = _ref6.dropFromOthersDisabled,
3127
+ dropFromOthersDisabled = _ref6$dropFromOthersD === void 0 ? false : _ref6$dropFromOthersD,
3128
+ _ref6$dropTargetStyle = _ref6.dropTargetStyle,
3129
+ dropTargetStyle = _ref6$dropTargetStyle === void 0 ? DEFAULT_DROP_TARGET_STYLE : _ref6$dropTargetStyle,
3130
+ _ref6$dropTargetClass = _ref6.dropTargetClasses,
3131
+ dropTargetClasses = _ref6$dropTargetClass === void 0 ? [] : _ref6$dropTargetClass,
3132
+ _ref6$autoAriaDisable = _ref6.autoAriaDisabled,
3133
+ autoAriaDisabled = _ref6$autoAriaDisable === void 0 ? false : _ref6$autoAriaDisable;
2900
3134
  config.items = _toConsumableArray(items);
2901
3135
  config.dragDisabled = dragDisabled;
2902
3136
  config.dropFromOthersDisabled = dropFromOthersDisabled;
@@ -2917,14 +3151,14 @@
2917
3151
  dzToConfig.set(node, config);
2918
3152
  var itemMovedToThisZone = false;
2919
3153
  if (isDragging) {
2920
- itemMovedToThisZone = config.type === draggedItemType && config.items.some(function (item) {
3154
+ itemMovedToThisZone = isInDragDocument(node) && config.type === draggedItemType && config.items.some(function (item) {
2921
3155
  return item[ITEM_ID_KEY] === focusedItemId;
2922
3156
  }) && node !== focusedDz;
2923
3157
  if (itemMovedToThisZone) {
2924
3158
  focusedDz = node;
2925
3159
  focusedDzLabel = node.getAttribute("aria-label") || "";
2926
3160
  }
2927
- node.tabIndex = getActiveDragTabIndex(node, config);
3161
+ node.tabIndex = getActiveDragTabIndex(node);
2928
3162
  } else {
2929
3163
  node.tabIndex = config.zoneTabIndex;
2930
3164
  }
@@ -2945,7 +3179,7 @@
2945
3179
  draggableEl.addEventListener("click", handleClick);
2946
3180
  elToFocusListeners.set(draggableEl, handleClick);
2947
3181
  }
2948
- if (isDragging && config.type === draggedItemType && ((_config$items$i = config.items[i]) === null || _config$items$i === void 0 ? void 0 : _config$items$i[ITEM_ID_KEY]) === focusedItemId) {
3182
+ if (isDragging && isInDragDocument(node) && config.type === draggedItemType && ((_config$items$i = config.items[i]) === null || _config$items$i === void 0 ? void 0 : _config$items$i[ITEM_ID_KEY]) === focusedItemId) {
2949
3183
  printDebug(function () {
2950
3184
  return ["focusing on", {
2951
3185
  i: i,
@@ -2962,7 +3196,8 @@
2962
3196
  for (var i = 0; i < node.children.length; i++) {
2963
3197
  _loop(i);
2964
3198
  }
2965
- if (itemMovedToThisZone) {
3199
+ // Focusing the replacement item runs its focus handler, which can end the drag, for example by removing its window
3200
+ if (itemMovedToThisZone && isDragging) {
2966
3201
  // Nested actions are configured before their parent action. Refresh only
2967
3202
  // after focusedItem points at the replacement so nested zones stay untabbable.
2968
3203
  refreshActiveDragTabIndices();
@@ -3219,22 +3454,27 @@
3219
3454
  */
3220
3455
  function dragHandle(handle) {
3221
3456
  handle.setAttribute("role", "button");
3457
+ var releaseWindow;
3222
3458
  function startDrag(e) {
3223
3459
  // preventing default to prevent lag on touch devices (because of the browser checking for screen scrolling)
3224
3460
  e.preventDefault();
3225
3461
  isItemsDragDisabled.set(false);
3226
3462
 
3227
- // Reset the startDrag/isItemsDragDisabled if the user releases the mouse/touch without initiating a drag
3228
- window.addEventListener("mouseup", resetStartDrag);
3229
- window.addEventListener("touchend", resetStartDrag);
3463
+ // Reset the startDrag/isItemsDragDisabled if the user releases the mouse/touch without initiating a drag, or if the
3464
+ // handle's window goes away first
3465
+ releaseWindow = getWindowOf(handle);
3466
+ releaseWindow.addEventListener("mouseup", resetStartDrag);
3467
+ releaseWindow.addEventListener("touchend", resetStartDrag);
3468
+ releaseWindow.addEventListener("pagehide", resetStartDrag);
3230
3469
  }
3231
3470
  function handleKeyDown(e) {
3232
3471
  if (isKeyboardDragTriggerKey(e.key)) isItemsDragDisabled.set(false);
3233
3472
  }
3234
3473
  function resetStartDrag() {
3235
3474
  isItemsDragDisabled.set(true);
3236
- window.removeEventListener("mouseup", resetStartDrag);
3237
- window.removeEventListener("touchend", resetStartDrag);
3475
+ releaseWindow.removeEventListener("mouseup", resetStartDrag);
3476
+ releaseWindow.removeEventListener("touchend", resetStartDrag);
3477
+ releaseWindow.removeEventListener("pagehide", resetStartDrag);
3238
3478
  }
3239
3479
  var recomputeHandleState = function recomputeHandleState() {
3240
3480
  var userDisabled = userDragDisabled.get();