@headless-tree/core 0.0.0-20250730213235 → 0.0.0-20250807163338
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/CHANGELOG.md +6 -1
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +29 -14
- package/dist/index.mjs +29 -14
- package/package.json +1 -1
- package/src/features/drag-and-drop/drag-and-drop.spec.ts +9 -0
- package/src/features/drag-and-drop/feature.ts +38 -13
- package/src/features/drag-and-drop/types.ts +16 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# @headless-tree/core
|
|
2
2
|
|
|
3
|
-
## 0.0.0-
|
|
3
|
+
## 0.0.0-20250807163338
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 21d1679: add `canDragForeignDragObjectOver` to allow customizing whether a draggable visualization should be shown when dragging foreign data. This allows differentiating logic between drag-over and drop (via the existing `canDropForeignDataObject`), since for the latter `dataTransfer.getData` is not available by default in browsers.
|
|
4
8
|
|
|
5
9
|
### Patch Changes
|
|
6
10
|
|
|
@@ -10,6 +14,7 @@
|
|
|
10
14
|
- b41e1d2: fixed a bug where ending drag without successful drop doesn't properly reset drag line (#132)
|
|
11
15
|
- b413f74: Fix `aria-posinset` and `aria-level` to be 1-based indexing
|
|
12
16
|
- a250b3b: Fix a bug where expand from the initial keyboard focus fails when rootItemId is an empty string
|
|
17
|
+
- 62867e8: Introduced a short delay before hiding the drag line when leaving a drag target, which helps to reduce flickering of the dragline when moving between items
|
|
13
18
|
- 662e2a8: Improved customizability of checkboxes feature (still alpha state), allowing you to customize `propagateCheckedState` and `canCheckFolders` independently
|
|
14
19
|
- 662e2a8: Changed to new buildtool in core packages (now using tsup) to hopefully fix some ESM/CJS integrations
|
|
15
20
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
interface DndDataRef {
|
|
2
2
|
lastDragCode?: string;
|
|
3
3
|
lastAllowDrop?: boolean;
|
|
4
|
+
lastDragEnter?: number;
|
|
4
5
|
windowDragEndListener?: () => void;
|
|
5
6
|
}
|
|
6
7
|
interface DndState<T> {
|
|
@@ -45,13 +46,22 @@ type DragAndDropFeatureDef<T> = {
|
|
|
45
46
|
createForeignDragObject?: (items: ItemInstance<T>[]) => {
|
|
46
47
|
format: string;
|
|
47
48
|
data: any;
|
|
49
|
+
dropEffect?: DataTransfer["dropEffect"];
|
|
50
|
+
effectAllowed?: DataTransfer["effectAllowed"];
|
|
48
51
|
};
|
|
49
52
|
setDragImage?: (items: ItemInstance<T>[]) => {
|
|
50
53
|
imgElement: Element;
|
|
51
54
|
xOffset?: number;
|
|
52
55
|
yOffset?: number;
|
|
53
56
|
};
|
|
57
|
+
/** Checks if a foreign drag object can be dropped on a target, validating that an actual drop can commence based on
|
|
58
|
+
* the data in the DataTransfer object. */
|
|
54
59
|
canDropForeignDragObject?: (dataTransfer: DataTransfer, target: DragTarget<T>) => boolean;
|
|
60
|
+
/** Checks if a droppable visualization should be displayed when dragging a foreign object over a target. Since this
|
|
61
|
+
* is executed on a dragover event, `dataTransfer.getData()` is not available, so `dataTransfer.effectAllowed` or
|
|
62
|
+
* `dataTransfer.types` should be used instead. Before actually completing the drag, @{link canDropForeignDragObject}
|
|
63
|
+
* will be called by HT before applying the drop. */
|
|
64
|
+
canDragForeignDragObjectOver?: (dataTransfer: DataTransfer, target: DragTarget<T>) => boolean;
|
|
55
65
|
onDrop?: (items: ItemInstance<T>[], target: DragTarget<T>) => void | Promise<void>;
|
|
56
66
|
onDropForeignDragObject?: (dataTransfer: DataTransfer, target: DragTarget<T>) => void | Promise<void>;
|
|
57
67
|
onCompleteForeignDrop?: (items: ItemInstance<T>[]) => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
interface DndDataRef {
|
|
2
2
|
lastDragCode?: string;
|
|
3
3
|
lastAllowDrop?: boolean;
|
|
4
|
+
lastDragEnter?: number;
|
|
4
5
|
windowDragEndListener?: () => void;
|
|
5
6
|
}
|
|
6
7
|
interface DndState<T> {
|
|
@@ -45,13 +46,22 @@ type DragAndDropFeatureDef<T> = {
|
|
|
45
46
|
createForeignDragObject?: (items: ItemInstance<T>[]) => {
|
|
46
47
|
format: string;
|
|
47
48
|
data: any;
|
|
49
|
+
dropEffect?: DataTransfer["dropEffect"];
|
|
50
|
+
effectAllowed?: DataTransfer["effectAllowed"];
|
|
48
51
|
};
|
|
49
52
|
setDragImage?: (items: ItemInstance<T>[]) => {
|
|
50
53
|
imgElement: Element;
|
|
51
54
|
xOffset?: number;
|
|
52
55
|
yOffset?: number;
|
|
53
56
|
};
|
|
57
|
+
/** Checks if a foreign drag object can be dropped on a target, validating that an actual drop can commence based on
|
|
58
|
+
* the data in the DataTransfer object. */
|
|
54
59
|
canDropForeignDragObject?: (dataTransfer: DataTransfer, target: DragTarget<T>) => boolean;
|
|
60
|
+
/** Checks if a droppable visualization should be displayed when dragging a foreign object over a target. Since this
|
|
61
|
+
* is executed on a dragover event, `dataTransfer.getData()` is not available, so `dataTransfer.effectAllowed` or
|
|
62
|
+
* `dataTransfer.types` should be used instead. Before actually completing the drag, @{link canDropForeignDragObject}
|
|
63
|
+
* will be called by HT before applying the drop. */
|
|
64
|
+
canDragForeignDragObjectOver?: (dataTransfer: DataTransfer, target: DragTarget<T>) => boolean;
|
|
55
65
|
onDrop?: (items: ItemInstance<T>[], target: DragTarget<T>) => void | Promise<void>;
|
|
56
66
|
onDropForeignDragObject?: (dataTransfer: DataTransfer, target: DragTarget<T>) => void | Promise<void>;
|
|
57
67
|
onCompleteForeignDrop?: (items: ItemInstance<T>[]) => void;
|
package/dist/index.js
CHANGED
|
@@ -1372,12 +1372,14 @@ var getDragTarget = (e, item, tree, canReorder = tree.getConfig().canReorder) =>
|
|
|
1372
1372
|
};
|
|
1373
1373
|
|
|
1374
1374
|
// src/features/drag-and-drop/feature.ts
|
|
1375
|
+
var defaultCanDropForeignDragObject = () => false;
|
|
1375
1376
|
var dragAndDropFeature = {
|
|
1376
1377
|
key: "drag-and-drop",
|
|
1377
1378
|
deps: ["selection"],
|
|
1378
1379
|
getDefaultConfig: (defaultConfig, tree) => __spreadValues({
|
|
1379
1380
|
canDrop: (_, target) => target.item.isFolder(),
|
|
1380
|
-
canDropForeignDragObject:
|
|
1381
|
+
canDropForeignDragObject: defaultCanDropForeignDragObject,
|
|
1382
|
+
canDragForeignDragObjectOver: defaultConfig.canDropForeignDragObject !== defaultCanDropForeignDragObject ? (dataTransfer) => dataTransfer.effectAllowed !== "none" : () => false,
|
|
1381
1383
|
setDndState: makeStateUpdater("dnd", tree),
|
|
1382
1384
|
canReorder: true
|
|
1383
1385
|
}, defaultConfig),
|
|
@@ -1476,7 +1478,7 @@ var dragAndDropFeature = {
|
|
|
1476
1478
|
draggable: true,
|
|
1477
1479
|
onDragEnter: (e) => e.preventDefault(),
|
|
1478
1480
|
onDragStart: (e) => {
|
|
1479
|
-
var _a, _b, _c
|
|
1481
|
+
var _a, _b, _c;
|
|
1480
1482
|
const selectedItems = tree.getSelectedItems();
|
|
1481
1483
|
const items = selectedItems.includes(item) ? selectedItems : [item];
|
|
1482
1484
|
const config = tree.getConfig();
|
|
@@ -1491,9 +1493,11 @@ var dragAndDropFeature = {
|
|
|
1491
1493
|
const { imgElement, xOffset, yOffset } = config.setDragImage(items);
|
|
1492
1494
|
(_c = e.dataTransfer) == null ? void 0 : _c.setDragImage(imgElement, xOffset != null ? xOffset : 0, yOffset != null ? yOffset : 0);
|
|
1493
1495
|
}
|
|
1494
|
-
if (config.createForeignDragObject) {
|
|
1495
|
-
const { format, data } = config.createForeignDragObject(items);
|
|
1496
|
-
|
|
1496
|
+
if (config.createForeignDragObject && e.dataTransfer) {
|
|
1497
|
+
const { format, data, dropEffect, effectAllowed } = config.createForeignDragObject(items);
|
|
1498
|
+
e.dataTransfer.setData(format, data);
|
|
1499
|
+
if (dropEffect) e.dataTransfer.dropEffect = dropEffect;
|
|
1500
|
+
if (effectAllowed) e.dataTransfer.effectAllowed = effectAllowed;
|
|
1497
1501
|
}
|
|
1498
1502
|
tree.applySubStateUpdate("dnd", {
|
|
1499
1503
|
draggedItems: items,
|
|
@@ -1502,6 +1506,7 @@ var dragAndDropFeature = {
|
|
|
1502
1506
|
},
|
|
1503
1507
|
onDragOver: (e) => {
|
|
1504
1508
|
var _a, _b, _c;
|
|
1509
|
+
e.stopPropagation();
|
|
1505
1510
|
const dataRef = tree.getDataRef();
|
|
1506
1511
|
const nextDragCode = getDragCode(e, item, tree);
|
|
1507
1512
|
if (nextDragCode === dataRef.current.lastDragCode) {
|
|
@@ -1511,8 +1516,9 @@ var dragAndDropFeature = {
|
|
|
1511
1516
|
return;
|
|
1512
1517
|
}
|
|
1513
1518
|
dataRef.current.lastDragCode = nextDragCode;
|
|
1519
|
+
dataRef.current.lastDragEnter = Date.now();
|
|
1514
1520
|
const target = getDragTarget(e, item, tree);
|
|
1515
|
-
if (!((_a = tree.getState().dnd) == null ? void 0 : _a.draggedItems) && (!e.dataTransfer || !((_c = (_b = tree.getConfig()).
|
|
1521
|
+
if (!((_a = tree.getState().dnd) == null ? void 0 : _a.draggedItems) && (!e.dataTransfer || !((_c = (_b = tree.getConfig()).canDragForeignDragObjectOver) == null ? void 0 : _c.call(_b, e.dataTransfer, target)))) {
|
|
1516
1522
|
dataRef.current.lastAllowDrop = false;
|
|
1517
1523
|
return;
|
|
1518
1524
|
}
|
|
@@ -1528,20 +1534,29 @@ var dragAndDropFeature = {
|
|
|
1528
1534
|
e.preventDefault();
|
|
1529
1535
|
},
|
|
1530
1536
|
onDragLeave: () => {
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
+
setTimeout(() => {
|
|
1538
|
+
var _a;
|
|
1539
|
+
const dataRef = tree.getDataRef();
|
|
1540
|
+
if (((_a = dataRef.current.lastDragEnter) != null ? _a : 0) + 100 >= Date.now()) return;
|
|
1541
|
+
dataRef.current.lastDragCode = "no-drag";
|
|
1542
|
+
tree.applySubStateUpdate("dnd", (state) => __spreadProps(__spreadValues({}, state), {
|
|
1543
|
+
draggingOverItem: void 0,
|
|
1544
|
+
dragTarget: void 0
|
|
1545
|
+
}));
|
|
1546
|
+
}, 100);
|
|
1537
1547
|
},
|
|
1538
1548
|
onDragEnd: (e) => {
|
|
1539
|
-
var _a, _b
|
|
1549
|
+
var _a, _b;
|
|
1550
|
+
const { onCompleteForeignDrop, canDragForeignDragObjectOver } = tree.getConfig();
|
|
1540
1551
|
const draggedItems = (_a = tree.getState().dnd) == null ? void 0 : _a.draggedItems;
|
|
1541
1552
|
if (((_b = e.dataTransfer) == null ? void 0 : _b.dropEffect) === "none" || !draggedItems) {
|
|
1542
1553
|
return;
|
|
1543
1554
|
}
|
|
1544
|
-
|
|
1555
|
+
const target = getDragTarget(e, item, tree);
|
|
1556
|
+
if (canDragForeignDragObjectOver && e.dataTransfer && !canDragForeignDragObjectOver(e.dataTransfer, target)) {
|
|
1557
|
+
return;
|
|
1558
|
+
}
|
|
1559
|
+
onCompleteForeignDrop == null ? void 0 : onCompleteForeignDrop(draggedItems);
|
|
1545
1560
|
},
|
|
1546
1561
|
onDrop: (e) => __async(null, null, function* () {
|
|
1547
1562
|
var _a, _b, _c;
|
package/dist/index.mjs
CHANGED
|
@@ -1328,12 +1328,14 @@ var getDragTarget = (e, item, tree, canReorder = tree.getConfig().canReorder) =>
|
|
|
1328
1328
|
};
|
|
1329
1329
|
|
|
1330
1330
|
// src/features/drag-and-drop/feature.ts
|
|
1331
|
+
var defaultCanDropForeignDragObject = () => false;
|
|
1331
1332
|
var dragAndDropFeature = {
|
|
1332
1333
|
key: "drag-and-drop",
|
|
1333
1334
|
deps: ["selection"],
|
|
1334
1335
|
getDefaultConfig: (defaultConfig, tree) => __spreadValues({
|
|
1335
1336
|
canDrop: (_, target) => target.item.isFolder(),
|
|
1336
|
-
canDropForeignDragObject:
|
|
1337
|
+
canDropForeignDragObject: defaultCanDropForeignDragObject,
|
|
1338
|
+
canDragForeignDragObjectOver: defaultConfig.canDropForeignDragObject !== defaultCanDropForeignDragObject ? (dataTransfer) => dataTransfer.effectAllowed !== "none" : () => false,
|
|
1337
1339
|
setDndState: makeStateUpdater("dnd", tree),
|
|
1338
1340
|
canReorder: true
|
|
1339
1341
|
}, defaultConfig),
|
|
@@ -1432,7 +1434,7 @@ var dragAndDropFeature = {
|
|
|
1432
1434
|
draggable: true,
|
|
1433
1435
|
onDragEnter: (e) => e.preventDefault(),
|
|
1434
1436
|
onDragStart: (e) => {
|
|
1435
|
-
var _a, _b, _c
|
|
1437
|
+
var _a, _b, _c;
|
|
1436
1438
|
const selectedItems = tree.getSelectedItems();
|
|
1437
1439
|
const items = selectedItems.includes(item) ? selectedItems : [item];
|
|
1438
1440
|
const config = tree.getConfig();
|
|
@@ -1447,9 +1449,11 @@ var dragAndDropFeature = {
|
|
|
1447
1449
|
const { imgElement, xOffset, yOffset } = config.setDragImage(items);
|
|
1448
1450
|
(_c = e.dataTransfer) == null ? void 0 : _c.setDragImage(imgElement, xOffset != null ? xOffset : 0, yOffset != null ? yOffset : 0);
|
|
1449
1451
|
}
|
|
1450
|
-
if (config.createForeignDragObject) {
|
|
1451
|
-
const { format, data } = config.createForeignDragObject(items);
|
|
1452
|
-
|
|
1452
|
+
if (config.createForeignDragObject && e.dataTransfer) {
|
|
1453
|
+
const { format, data, dropEffect, effectAllowed } = config.createForeignDragObject(items);
|
|
1454
|
+
e.dataTransfer.setData(format, data);
|
|
1455
|
+
if (dropEffect) e.dataTransfer.dropEffect = dropEffect;
|
|
1456
|
+
if (effectAllowed) e.dataTransfer.effectAllowed = effectAllowed;
|
|
1453
1457
|
}
|
|
1454
1458
|
tree.applySubStateUpdate("dnd", {
|
|
1455
1459
|
draggedItems: items,
|
|
@@ -1458,6 +1462,7 @@ var dragAndDropFeature = {
|
|
|
1458
1462
|
},
|
|
1459
1463
|
onDragOver: (e) => {
|
|
1460
1464
|
var _a, _b, _c;
|
|
1465
|
+
e.stopPropagation();
|
|
1461
1466
|
const dataRef = tree.getDataRef();
|
|
1462
1467
|
const nextDragCode = getDragCode(e, item, tree);
|
|
1463
1468
|
if (nextDragCode === dataRef.current.lastDragCode) {
|
|
@@ -1467,8 +1472,9 @@ var dragAndDropFeature = {
|
|
|
1467
1472
|
return;
|
|
1468
1473
|
}
|
|
1469
1474
|
dataRef.current.lastDragCode = nextDragCode;
|
|
1475
|
+
dataRef.current.lastDragEnter = Date.now();
|
|
1470
1476
|
const target = getDragTarget(e, item, tree);
|
|
1471
|
-
if (!((_a = tree.getState().dnd) == null ? void 0 : _a.draggedItems) && (!e.dataTransfer || !((_c = (_b = tree.getConfig()).
|
|
1477
|
+
if (!((_a = tree.getState().dnd) == null ? void 0 : _a.draggedItems) && (!e.dataTransfer || !((_c = (_b = tree.getConfig()).canDragForeignDragObjectOver) == null ? void 0 : _c.call(_b, e.dataTransfer, target)))) {
|
|
1472
1478
|
dataRef.current.lastAllowDrop = false;
|
|
1473
1479
|
return;
|
|
1474
1480
|
}
|
|
@@ -1484,20 +1490,29 @@ var dragAndDropFeature = {
|
|
|
1484
1490
|
e.preventDefault();
|
|
1485
1491
|
},
|
|
1486
1492
|
onDragLeave: () => {
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
+
setTimeout(() => {
|
|
1494
|
+
var _a;
|
|
1495
|
+
const dataRef = tree.getDataRef();
|
|
1496
|
+
if (((_a = dataRef.current.lastDragEnter) != null ? _a : 0) + 100 >= Date.now()) return;
|
|
1497
|
+
dataRef.current.lastDragCode = "no-drag";
|
|
1498
|
+
tree.applySubStateUpdate("dnd", (state) => __spreadProps(__spreadValues({}, state), {
|
|
1499
|
+
draggingOverItem: void 0,
|
|
1500
|
+
dragTarget: void 0
|
|
1501
|
+
}));
|
|
1502
|
+
}, 100);
|
|
1493
1503
|
},
|
|
1494
1504
|
onDragEnd: (e) => {
|
|
1495
|
-
var _a, _b
|
|
1505
|
+
var _a, _b;
|
|
1506
|
+
const { onCompleteForeignDrop, canDragForeignDragObjectOver } = tree.getConfig();
|
|
1496
1507
|
const draggedItems = (_a = tree.getState().dnd) == null ? void 0 : _a.draggedItems;
|
|
1497
1508
|
if (((_b = e.dataTransfer) == null ? void 0 : _b.dropEffect) === "none" || !draggedItems) {
|
|
1498
1509
|
return;
|
|
1499
1510
|
}
|
|
1500
|
-
|
|
1511
|
+
const target = getDragTarget(e, item, tree);
|
|
1512
|
+
if (canDragForeignDragObjectOver && e.dataTransfer && !canDragForeignDragObjectOver(e.dataTransfer, target)) {
|
|
1513
|
+
return;
|
|
1514
|
+
}
|
|
1515
|
+
onCompleteForeignDrop == null ? void 0 : onCompleteForeignDrop(draggedItems);
|
|
1501
1516
|
},
|
|
1502
1517
|
onDrop: (e) => __async(null, null, function* () {
|
|
1503
1518
|
var _a, _b, _c;
|
package/package.json
CHANGED
|
@@ -300,6 +300,9 @@ describe("core-feature/drag-and-drop", () => {
|
|
|
300
300
|
|
|
301
301
|
it("drags foreign object inside tree, on folder", () => {
|
|
302
302
|
tree.mockedHandler("canDropForeignDragObject").mockReturnValue(true);
|
|
303
|
+
tree
|
|
304
|
+
.mockedHandler("canDragForeignDragObjectOver")
|
|
305
|
+
.mockReturnValue(true);
|
|
303
306
|
const onDropForeignDragObject = tree.mockedHandler(
|
|
304
307
|
"onDropForeignDragObject",
|
|
305
308
|
);
|
|
@@ -318,6 +321,9 @@ describe("core-feature/drag-and-drop", () => {
|
|
|
318
321
|
tree
|
|
319
322
|
.mockedHandler("canDropForeignDragObject")
|
|
320
323
|
.mockImplementation((_, target) => target.item.isFolder());
|
|
324
|
+
tree
|
|
325
|
+
.mockedHandler("canDragForeignDragObjectOver")
|
|
326
|
+
.mockImplementation((_, target) => target.item.isFolder());
|
|
321
327
|
const onDropForeignDragObject = tree.mockedHandler(
|
|
322
328
|
"onDropForeignDragObject",
|
|
323
329
|
);
|
|
@@ -340,6 +346,9 @@ describe("core-feature/drag-and-drop", () => {
|
|
|
340
346
|
|
|
341
347
|
it("doesnt drag foreign object inside tree if not allowed", () => {
|
|
342
348
|
tree.mockedHandler("canDropForeignDragObject").mockReturnValue(false);
|
|
349
|
+
tree
|
|
350
|
+
.mockedHandler("canDragForeignDragObjectOver")
|
|
351
|
+
.mockReturnValue(false);
|
|
343
352
|
const onDropForeignDragObject = tree.mockedHandler(
|
|
344
353
|
"onDropForeignDragObject",
|
|
345
354
|
);
|
|
@@ -8,13 +8,18 @@ import {
|
|
|
8
8
|
} from "./utils";
|
|
9
9
|
import { makeStateUpdater } from "../../utils";
|
|
10
10
|
|
|
11
|
+
const defaultCanDropForeignDragObject = () => false;
|
|
11
12
|
export const dragAndDropFeature: FeatureImplementation = {
|
|
12
13
|
key: "drag-and-drop",
|
|
13
14
|
deps: ["selection"],
|
|
14
15
|
|
|
15
16
|
getDefaultConfig: (defaultConfig, tree) => ({
|
|
16
17
|
canDrop: (_, target) => target.item.isFolder(),
|
|
17
|
-
canDropForeignDragObject:
|
|
18
|
+
canDropForeignDragObject: defaultCanDropForeignDragObject,
|
|
19
|
+
canDragForeignDragObjectOver:
|
|
20
|
+
defaultConfig.canDropForeignDragObject !== defaultCanDropForeignDragObject
|
|
21
|
+
? (dataTransfer) => dataTransfer.effectAllowed !== "none"
|
|
22
|
+
: () => false,
|
|
18
23
|
setDndState: makeStateUpdater("dnd", tree),
|
|
19
24
|
canReorder: true,
|
|
20
25
|
...defaultConfig,
|
|
@@ -162,9 +167,13 @@ export const dragAndDropFeature: FeatureImplementation = {
|
|
|
162
167
|
e.dataTransfer?.setDragImage(imgElement, xOffset ?? 0, yOffset ?? 0);
|
|
163
168
|
}
|
|
164
169
|
|
|
165
|
-
if (config.createForeignDragObject) {
|
|
166
|
-
const { format, data } =
|
|
167
|
-
|
|
170
|
+
if (config.createForeignDragObject && e.dataTransfer) {
|
|
171
|
+
const { format, data, dropEffect, effectAllowed } =
|
|
172
|
+
config.createForeignDragObject(items);
|
|
173
|
+
e.dataTransfer.setData(format, data);
|
|
174
|
+
|
|
175
|
+
if (dropEffect) e.dataTransfer.dropEffect = dropEffect;
|
|
176
|
+
if (effectAllowed) e.dataTransfer.effectAllowed = effectAllowed;
|
|
168
177
|
}
|
|
169
178
|
|
|
170
179
|
tree.applySubStateUpdate("dnd", {
|
|
@@ -174,6 +183,7 @@ export const dragAndDropFeature: FeatureImplementation = {
|
|
|
174
183
|
},
|
|
175
184
|
|
|
176
185
|
onDragOver: (e: DragEvent) => {
|
|
186
|
+
e.stopPropagation(); // don't bubble up to container dragover
|
|
177
187
|
const dataRef = tree.getDataRef<DndDataRef>();
|
|
178
188
|
const nextDragCode = getDragCode(e, item, tree);
|
|
179
189
|
if (nextDragCode === dataRef.current.lastDragCode) {
|
|
@@ -183,6 +193,7 @@ export const dragAndDropFeature: FeatureImplementation = {
|
|
|
183
193
|
return;
|
|
184
194
|
}
|
|
185
195
|
dataRef.current.lastDragCode = nextDragCode;
|
|
196
|
+
dataRef.current.lastDragEnter = Date.now();
|
|
186
197
|
|
|
187
198
|
const target = getDragTarget(e, item, tree);
|
|
188
199
|
|
|
@@ -191,7 +202,7 @@ export const dragAndDropFeature: FeatureImplementation = {
|
|
|
191
202
|
(!e.dataTransfer ||
|
|
192
203
|
!tree
|
|
193
204
|
.getConfig()
|
|
194
|
-
.
|
|
205
|
+
.canDragForeignDragObjectOver?.(e.dataTransfer, target))
|
|
195
206
|
) {
|
|
196
207
|
dataRef.current.lastAllowDrop = false;
|
|
197
208
|
return;
|
|
@@ -212,23 +223,37 @@ export const dragAndDropFeature: FeatureImplementation = {
|
|
|
212
223
|
},
|
|
213
224
|
|
|
214
225
|
onDragLeave: () => {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
226
|
+
setTimeout(() => {
|
|
227
|
+
const dataRef = tree.getDataRef<DndDataRef>();
|
|
228
|
+
if ((dataRef.current.lastDragEnter ?? 0) + 100 >= Date.now()) return;
|
|
229
|
+
dataRef.current.lastDragCode = "no-drag";
|
|
230
|
+
tree.applySubStateUpdate("dnd", (state) => ({
|
|
231
|
+
...state,
|
|
232
|
+
draggingOverItem: undefined,
|
|
233
|
+
dragTarget: undefined,
|
|
234
|
+
}));
|
|
235
|
+
}, 100);
|
|
222
236
|
},
|
|
223
237
|
|
|
224
238
|
onDragEnd: (e: DragEvent) => {
|
|
239
|
+
const { onCompleteForeignDrop, canDragForeignDragObjectOver } =
|
|
240
|
+
tree.getConfig();
|
|
225
241
|
const draggedItems = tree.getState().dnd?.draggedItems;
|
|
226
242
|
|
|
227
243
|
if (e.dataTransfer?.dropEffect === "none" || !draggedItems) {
|
|
228
244
|
return;
|
|
229
245
|
}
|
|
230
246
|
|
|
231
|
-
tree
|
|
247
|
+
const target = getDragTarget(e, item, tree);
|
|
248
|
+
if (
|
|
249
|
+
canDragForeignDragObjectOver &&
|
|
250
|
+
e.dataTransfer &&
|
|
251
|
+
!canDragForeignDragObjectOver(e.dataTransfer, target)
|
|
252
|
+
) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
onCompleteForeignDrop?.(draggedItems);
|
|
232
257
|
},
|
|
233
258
|
|
|
234
259
|
onDrop: async (e: DragEvent) => {
|
|
@@ -3,6 +3,7 @@ import { ItemInstance, SetStateFn } from "../../types/core";
|
|
|
3
3
|
export interface DndDataRef {
|
|
4
4
|
lastDragCode?: string;
|
|
5
5
|
lastAllowDrop?: boolean;
|
|
6
|
+
lastDragEnter?: number;
|
|
6
7
|
windowDragEndListener?: () => void;
|
|
7
8
|
}
|
|
8
9
|
|
|
@@ -58,16 +59,31 @@ export type DragAndDropFeatureDef<T> = {
|
|
|
58
59
|
createForeignDragObject?: (items: ItemInstance<T>[]) => {
|
|
59
60
|
format: string;
|
|
60
61
|
data: any;
|
|
62
|
+
dropEffect?: DataTransfer["dropEffect"];
|
|
63
|
+
effectAllowed?: DataTransfer["effectAllowed"];
|
|
61
64
|
};
|
|
62
65
|
setDragImage?: (items: ItemInstance<T>[]) => {
|
|
63
66
|
imgElement: Element;
|
|
64
67
|
xOffset?: number;
|
|
65
68
|
yOffset?: number;
|
|
66
69
|
};
|
|
70
|
+
|
|
71
|
+
/** Checks if a foreign drag object can be dropped on a target, validating that an actual drop can commence based on
|
|
72
|
+
* the data in the DataTransfer object. */
|
|
67
73
|
canDropForeignDragObject?: (
|
|
68
74
|
dataTransfer: DataTransfer,
|
|
69
75
|
target: DragTarget<T>,
|
|
70
76
|
) => boolean;
|
|
77
|
+
|
|
78
|
+
/** Checks if a droppable visualization should be displayed when dragging a foreign object over a target. Since this
|
|
79
|
+
* is executed on a dragover event, `dataTransfer.getData()` is not available, so `dataTransfer.effectAllowed` or
|
|
80
|
+
* `dataTransfer.types` should be used instead. Before actually completing the drag, @{link canDropForeignDragObject}
|
|
81
|
+
* will be called by HT before applying the drop. */
|
|
82
|
+
canDragForeignDragObjectOver?: (
|
|
83
|
+
dataTransfer: DataTransfer,
|
|
84
|
+
target: DragTarget<T>,
|
|
85
|
+
) => boolean;
|
|
86
|
+
|
|
71
87
|
onDrop?: (
|
|
72
88
|
items: ItemInstance<T>[],
|
|
73
89
|
target: DragTarget<T>,
|