@elliemae/ds-drag-and-drop 2.2.1 → 2.2.3-next.0
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/cjs/tree/customCollisionDetection.js +92 -88
- package/cjs/tree/getTreeKeyboardCoordinates.js +0 -1
- package/cjs/tree/useTreeActionHandlers.js +5 -6
- package/cjs/tree/useTreeDndkitConfig.js +26 -16
- package/esm/tree/customCollisionDetection.js +92 -88
- package/esm/tree/getTreeKeyboardCoordinates.js +0 -1
- package/esm/tree/useTreeActionHandlers.js +5 -6
- package/esm/tree/useTreeDndkitConfig.js +26 -16
- package/package.json +1 -1
- package/types/hierarchy/types.d.ts +1 -1
- package/types/tree/types.d.ts +17 -13
- package/types/tree/useTreeActionHandlers.d.ts +2 -2
- package/types/tree/useTreeDndkitConfig.d.ts +2 -2
- package/types/tree/useTreePreviewHandlers.d.ts +2 -2
|
@@ -33,105 +33,109 @@ const RECT_DOWN = {
|
|
|
33
33
|
const thresholdRatio = 0.2; // Percentage to be inside
|
|
34
34
|
|
|
35
35
|
const insideThreshold = 0.7;
|
|
36
|
-
const customCollisionDetection = (activeId, visibleItemsDictionary, setDropIndicatorPosition, maxDragAndDropLevel, lastPosition, setLastPosition) =>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
droppableContainers,
|
|
41
|
-
collisionRect
|
|
42
|
-
} = _ref;
|
|
43
|
-
const originalContainer = droppableContainers.find(_ref2 => {
|
|
36
|
+
const customCollisionDetection = (activeId, visibleItemsDictionary, setDropIndicatorPosition, maxDragAndDropLevel, lastPosition, setLastPosition) => {
|
|
37
|
+
const func = _ref => {
|
|
38
|
+
var _originalContainer$re;
|
|
39
|
+
|
|
44
40
|
let {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (originalRect) {
|
|
55
|
-
isUp = originalRect.offsetTop > collisionRect.top;
|
|
56
|
-
} // Threshold
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const threshold = collisionRect.height * thresholdRatio;
|
|
60
|
-
let collidingContainer = null;
|
|
61
|
-
|
|
62
|
-
if (isUp) {
|
|
63
|
-
// Up -- We need to find the first rectangle downwards
|
|
64
|
-
collidingContainer = droppableContainers.reduce((firstRectDown, container) => {
|
|
65
|
-
const rect = container.rect.current;
|
|
66
|
-
|
|
67
|
-
if (rect && firstRectDown.rect.current) {
|
|
68
|
-
const {
|
|
69
|
-
offsetTop: rectOffsetTop
|
|
70
|
-
} = rect;
|
|
71
|
-
const {
|
|
72
|
-
offsetTop: firstRectDownOffsetTop
|
|
73
|
-
} = firstRectDown.rect.current;
|
|
74
|
-
|
|
75
|
-
if (rectOffsetTop + threshold > collisionRect.top && rectOffsetTop < firstRectDownOffsetTop) {
|
|
76
|
-
return container;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return firstRectDown;
|
|
81
|
-
}, {
|
|
82
|
-
id: DUMMY_ID,
|
|
83
|
-
rect: {
|
|
84
|
-
current: RECT_DOWN
|
|
85
|
-
}
|
|
41
|
+
droppableContainers,
|
|
42
|
+
collisionRect
|
|
43
|
+
} = _ref;
|
|
44
|
+
const originalContainer = droppableContainers.find(_ref2 => {
|
|
45
|
+
let {
|
|
46
|
+
id
|
|
47
|
+
} = _ref2;
|
|
48
|
+
return id === activeId;
|
|
86
49
|
});
|
|
87
|
-
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
50
|
+
const originalRect = originalContainer === null || originalContainer === void 0 ? void 0 : (_originalContainer$re = originalContainer.rect) === null || _originalContainer$re === void 0 ? void 0 : _originalContainer$re.current; // We first check if the item was moved up or down
|
|
51
|
+
// This modifies how to search the matching colliding rect
|
|
52
|
+
|
|
53
|
+
let isUp = lastPosition === 'up';
|
|
54
|
+
|
|
55
|
+
if (originalRect) {
|
|
56
|
+
isUp = originalRect.offsetTop > collisionRect.top;
|
|
57
|
+
} // Threshold
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
const threshold = collisionRect.height * thresholdRatio;
|
|
61
|
+
let collidingContainer = null;
|
|
62
|
+
|
|
63
|
+
if (isUp) {
|
|
64
|
+
// Up -- We need to find the first rectangle downwards
|
|
65
|
+
collidingContainer = droppableContainers.reduce((firstRectDown, container) => {
|
|
66
|
+
const rect = container.rect.current;
|
|
67
|
+
|
|
68
|
+
if (rect && firstRectDown.rect.current) {
|
|
69
|
+
const {
|
|
70
|
+
offsetTop: rectOffsetTop
|
|
71
|
+
} = rect;
|
|
72
|
+
const {
|
|
73
|
+
offsetTop: firstRectDownOffsetTop
|
|
74
|
+
} = firstRectDown.rect.current;
|
|
75
|
+
|
|
76
|
+
if (rectOffsetTop + threshold > collisionRect.top && rectOffsetTop < firstRectDownOffsetTop) {
|
|
77
|
+
return container;
|
|
78
|
+
}
|
|
102
79
|
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return firstRectUp;
|
|
106
|
-
}, {
|
|
107
|
-
id: DUMMY_ID,
|
|
108
|
-
rect: {
|
|
109
|
-
current: RECT_UP
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
} // If we didn't find a match, return null
|
|
113
80
|
|
|
81
|
+
return firstRectDown;
|
|
82
|
+
}, {
|
|
83
|
+
id: DUMMY_ID,
|
|
84
|
+
rect: {
|
|
85
|
+
current: RECT_DOWN
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
} else {
|
|
89
|
+
// Down -- We need to find the first rectangle upwards
|
|
90
|
+
collidingContainer = droppableContainers.reduce((firstRectUp, container) => {
|
|
91
|
+
const rect = container.rect.current;
|
|
92
|
+
|
|
93
|
+
if (rect && firstRectUp.rect.current) {
|
|
94
|
+
const {
|
|
95
|
+
offsetTop: rectOffsetTop
|
|
96
|
+
} = rect;
|
|
97
|
+
const {
|
|
98
|
+
offsetTop: firstRectUpOffsetTop
|
|
99
|
+
} = firstRectUp.rect.current;
|
|
100
|
+
|
|
101
|
+
if (rectOffsetTop - threshold < collisionRect.top && rectOffsetTop > firstRectUpOffsetTop) {
|
|
102
|
+
return container;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return firstRectUp;
|
|
107
|
+
}, {
|
|
108
|
+
id: DUMMY_ID,
|
|
109
|
+
rect: {
|
|
110
|
+
current: RECT_UP
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
} // If we didn't find a match, return null
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
if (collidingContainer.id === DUMMY_ID) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
114
119
|
|
|
115
|
-
|
|
116
|
-
return null;
|
|
117
|
-
}
|
|
120
|
+
const collidingRect = collidingContainer.rect.current;
|
|
121
|
+
if (!collidingRect) return null; // Calculate the intersection interval
|
|
118
122
|
|
|
119
|
-
|
|
120
|
-
if (!collidingRect) return null; // Calculate the intersection interval
|
|
123
|
+
const [top, bottom] = [Math.max(collisionRect.top, collidingRect.offsetTop), Math.min(collisionRect.top + collisionRect.height, collidingRect.offsetTop + collidingRect.height)]; // Calculate the percentage of intersection
|
|
121
124
|
|
|
122
|
-
|
|
125
|
+
const intersectionPercentage = Math.abs(bottom - top) / collidingRect.height;
|
|
123
126
|
|
|
124
|
-
|
|
127
|
+
if (intersectionPercentage > insideThreshold && visibleItemsDictionary[collidingContainer.id].depth + 1 <= maxDragAndDropLevel && collidingContainer.id !== activeId) {
|
|
128
|
+
setDropIndicatorPosition(constants.DropIndicatorPosition.Inside);
|
|
129
|
+
} else {
|
|
130
|
+
setDropIndicatorPosition(isUp ? constants.DropIndicatorPosition.Before : constants.DropIndicatorPosition.After);
|
|
131
|
+
}
|
|
125
132
|
|
|
126
|
-
|
|
127
|
-
setDropIndicatorPosition(constants.DropIndicatorPosition.Inside);
|
|
128
|
-
} else {
|
|
129
|
-
setDropIndicatorPosition(isUp ? constants.DropIndicatorPosition.Before : constants.DropIndicatorPosition.After);
|
|
130
|
-
}
|
|
133
|
+
if (isUp && lastPosition !== 'up') setLastPosition('up');else if (!isUp && lastPosition !== 'down') setLastPosition('down'); // Return the id of the match rectangle
|
|
131
134
|
|
|
132
|
-
|
|
135
|
+
return collidingContainer.id;
|
|
136
|
+
};
|
|
133
137
|
|
|
134
|
-
return
|
|
138
|
+
return func;
|
|
135
139
|
};
|
|
136
140
|
|
|
137
141
|
exports.customCollisionDetection = customCollisionDetection;
|
|
@@ -22,7 +22,6 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
|
|
|
22
22
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty__default["default"](target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
23
23
|
const directions = [core.KeyboardCode.Down, core.KeyboardCode.Right, core.KeyboardCode.Up, core.KeyboardCode.Left];
|
|
24
24
|
const horizontal = [core.KeyboardCode.Left, core.KeyboardCode.Right];
|
|
25
|
-
[core.KeyboardCode.Up, core.KeyboardCode.Down];
|
|
26
25
|
|
|
27
26
|
const getVerticalKeyboardCoordinates = _ref => {
|
|
28
27
|
var _droppableContainers$, _droppableContainers$2;
|
|
@@ -17,7 +17,8 @@ const useTreeActionHandlers = _ref => {
|
|
|
17
17
|
onReorder,
|
|
18
18
|
flattenedItems,
|
|
19
19
|
projected,
|
|
20
|
-
dropIndicatorPosition
|
|
20
|
+
dropIndicatorPosition,
|
|
21
|
+
isDropValid
|
|
21
22
|
} = _ref;
|
|
22
23
|
const onDragStart = react.useCallback(e => {
|
|
23
24
|
handlePreviewDragStart(e);
|
|
@@ -34,17 +35,15 @@ const useTreeActionHandlers = _ref => {
|
|
|
34
35
|
active,
|
|
35
36
|
over
|
|
36
37
|
} = e;
|
|
37
|
-
if (over === null) return;
|
|
38
|
+
if (over === null || !isDropValid) return;
|
|
38
39
|
const activeIndex = flattenedItems.findIndex(item => item.uid === active.id);
|
|
39
40
|
let considerExpanding = null;
|
|
40
41
|
let overIndex = flattenedItems.findIndex(item => item.uid === over.id); // If drop indicator is inside, then put it last,
|
|
41
42
|
// It will be reconstructed well later
|
|
42
43
|
|
|
43
44
|
if (dropIndicatorPosition === constants.DropIndicatorPosition.Inside) {
|
|
44
|
-
var _flattenedItems$overI, _flattenedItems$overI2;
|
|
45
|
-
|
|
46
45
|
considerExpanding = over.id;
|
|
47
|
-
overIndex = flattenedItems[overIndex].realIndex +
|
|
46
|
+
overIndex = flattenedItems[overIndex].realIndex + flattenedItems[overIndex].childrenCount + 1;
|
|
48
47
|
} // If we are dropping the item in a new position, or new depth
|
|
49
48
|
|
|
50
49
|
|
|
@@ -59,7 +58,7 @@ const useTreeActionHandlers = _ref => {
|
|
|
59
58
|
fromIndex: activeIndex
|
|
60
59
|
}, considerExpanding || '');
|
|
61
60
|
}
|
|
62
|
-
}, [handlePreviewDragEnd, flattenedItems, projected, onReorder
|
|
61
|
+
}, [handlePreviewDragEnd, isDropValid, flattenedItems, dropIndicatorPosition, projected, onReorder]);
|
|
63
62
|
const onDragCancel = react.useCallback(e => {
|
|
64
63
|
handlePreviewDragCancel(e);
|
|
65
64
|
}, [handlePreviewDragCancel]);
|
|
@@ -13,7 +13,6 @@ require('core-js/modules/esnext.async-iterator.filter.js');
|
|
|
13
13
|
require('core-js/modules/esnext.iterator.filter.js');
|
|
14
14
|
var react = require('react');
|
|
15
15
|
var core = require('@dnd-kit/core');
|
|
16
|
-
var sortable = require('@dnd-kit/sortable');
|
|
17
16
|
var useTreePreviewHandlers = require('./useTreePreviewHandlers.js');
|
|
18
17
|
var getTreeKeyboardCoordinates = require('./getTreeKeyboardCoordinates.js');
|
|
19
18
|
var utilities = require('./utilities.js');
|
|
@@ -31,25 +30,29 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
|
|
|
31
30
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty__default["default"](target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
32
31
|
// if second parameter is true, the space will be done on the horizontal axis
|
|
33
32
|
|
|
34
|
-
const adjustTranslate = isHorizontalDnD =>
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
const adjustTranslate = isHorizontalDnD => {
|
|
34
|
+
const func = _ref => {
|
|
35
|
+
let {
|
|
36
|
+
transform
|
|
37
|
+
} = _ref;
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
const newTransform = _objectSpread({}, transform);
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
if (isHorizontalDnD) {
|
|
42
|
+
newTransform.x = transform.x + 25;
|
|
43
|
+
} else {
|
|
44
|
+
newTransform.x = transform.x + 15;
|
|
45
|
+
}
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
return newTransform;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return func;
|
|
48
51
|
};
|
|
49
52
|
|
|
50
53
|
const measuring = {
|
|
51
54
|
droppable: {
|
|
52
|
-
strategy: core.MeasuringStrategy.
|
|
55
|
+
strategy: core.MeasuringStrategy.Always
|
|
53
56
|
}
|
|
54
57
|
};
|
|
55
58
|
const useTreeDndkitConfig = _ref2 => {
|
|
@@ -61,6 +64,7 @@ const useTreeDndkitConfig = _ref2 => {
|
|
|
61
64
|
isHorizontalDnD = false,
|
|
62
65
|
isExpandable = false,
|
|
63
66
|
onReorder,
|
|
67
|
+
getIsDropValid = () => true,
|
|
64
68
|
maxDragAndDropLevel
|
|
65
69
|
} = _ref2;
|
|
66
70
|
const [activeId, setActiveId] = react.useState('');
|
|
@@ -85,6 +89,10 @@ const useTreeDndkitConfig = _ref2 => {
|
|
|
85
89
|
});
|
|
86
90
|
return dictionary;
|
|
87
91
|
}, [visibleItems]);
|
|
92
|
+
const isDropValid = react.useMemo(() => {
|
|
93
|
+
if (!activeId || !overId) return true;
|
|
94
|
+
return getIsDropValid(visibleItemsDictionary[activeId], visibleItemsDictionary[overId], ['none', 'before', 'after', 'inside'][dropIndicatorPosition]);
|
|
95
|
+
}, [getIsDropValid, visibleItemsDictionary, activeId, overId, dropIndicatorPosition]);
|
|
88
96
|
const modifiers = react.useMemo(() => [adjustTranslate(isHorizontalDnD)], [isHorizontalDnD]);
|
|
89
97
|
const sensorContext = react.useRef({
|
|
90
98
|
items: visibleItems,
|
|
@@ -113,7 +121,8 @@ const useTreeDndkitConfig = _ref2 => {
|
|
|
113
121
|
onReorder,
|
|
114
122
|
projected,
|
|
115
123
|
flattenedItems,
|
|
116
|
-
dropIndicatorPosition
|
|
124
|
+
dropIndicatorPosition,
|
|
125
|
+
isDropValid
|
|
117
126
|
}));
|
|
118
127
|
const announcements = useTreeAnnouncements.useTreeAnnouncements(visibleItemsDictionary, dropIndicatorPosition);
|
|
119
128
|
const dndContextProps = react.useMemo(() => _objectSpread({
|
|
@@ -125,11 +134,12 @@ const useTreeDndkitConfig = _ref2 => {
|
|
|
125
134
|
}, dragActionHandlers), [announcements, modifiers, sensors, dragActionHandlers, visibleItemsDictionary, setDropIndicatorPosition, activeId, maxDragAndDropLevel, lastPosition, setLastPosition]);
|
|
126
135
|
const sortableContextProps = react.useMemo(() => ({
|
|
127
136
|
items: sortedIds,
|
|
128
|
-
strategy:
|
|
129
|
-
}), [sortedIds
|
|
137
|
+
strategy: () => null
|
|
138
|
+
}), [sortedIds]);
|
|
130
139
|
return {
|
|
131
140
|
dndContextProps,
|
|
132
141
|
sortableContextProps,
|
|
142
|
+
isDropValid,
|
|
133
143
|
activeId,
|
|
134
144
|
activeIndex: (_visibleItemsDictiona = (_visibleItemsDictiona2 = visibleItemsDictionary[activeId]) === null || _visibleItemsDictiona2 === void 0 ? void 0 : _visibleItemsDictiona2.realIndex) !== null && _visibleItemsDictiona !== void 0 ? _visibleItemsDictiona : -1,
|
|
135
145
|
overId,
|
|
@@ -29,105 +29,109 @@ const RECT_DOWN = {
|
|
|
29
29
|
const thresholdRatio = 0.2; // Percentage to be inside
|
|
30
30
|
|
|
31
31
|
const insideThreshold = 0.7;
|
|
32
|
-
const customCollisionDetection = (activeId, visibleItemsDictionary, setDropIndicatorPosition, maxDragAndDropLevel, lastPosition, setLastPosition) =>
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
droppableContainers,
|
|
37
|
-
collisionRect
|
|
38
|
-
} = _ref;
|
|
39
|
-
const originalContainer = droppableContainers.find(_ref2 => {
|
|
32
|
+
const customCollisionDetection = (activeId, visibleItemsDictionary, setDropIndicatorPosition, maxDragAndDropLevel, lastPosition, setLastPosition) => {
|
|
33
|
+
const func = _ref => {
|
|
34
|
+
var _originalContainer$re;
|
|
35
|
+
|
|
40
36
|
let {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (originalRect) {
|
|
51
|
-
isUp = originalRect.offsetTop > collisionRect.top;
|
|
52
|
-
} // Threshold
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const threshold = collisionRect.height * thresholdRatio;
|
|
56
|
-
let collidingContainer = null;
|
|
57
|
-
|
|
58
|
-
if (isUp) {
|
|
59
|
-
// Up -- We need to find the first rectangle downwards
|
|
60
|
-
collidingContainer = droppableContainers.reduce((firstRectDown, container) => {
|
|
61
|
-
const rect = container.rect.current;
|
|
62
|
-
|
|
63
|
-
if (rect && firstRectDown.rect.current) {
|
|
64
|
-
const {
|
|
65
|
-
offsetTop: rectOffsetTop
|
|
66
|
-
} = rect;
|
|
67
|
-
const {
|
|
68
|
-
offsetTop: firstRectDownOffsetTop
|
|
69
|
-
} = firstRectDown.rect.current;
|
|
70
|
-
|
|
71
|
-
if (rectOffsetTop + threshold > collisionRect.top && rectOffsetTop < firstRectDownOffsetTop) {
|
|
72
|
-
return container;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return firstRectDown;
|
|
77
|
-
}, {
|
|
78
|
-
id: DUMMY_ID,
|
|
79
|
-
rect: {
|
|
80
|
-
current: RECT_DOWN
|
|
81
|
-
}
|
|
37
|
+
droppableContainers,
|
|
38
|
+
collisionRect
|
|
39
|
+
} = _ref;
|
|
40
|
+
const originalContainer = droppableContainers.find(_ref2 => {
|
|
41
|
+
let {
|
|
42
|
+
id
|
|
43
|
+
} = _ref2;
|
|
44
|
+
return id === activeId;
|
|
82
45
|
});
|
|
83
|
-
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
46
|
+
const originalRect = originalContainer === null || originalContainer === void 0 ? void 0 : (_originalContainer$re = originalContainer.rect) === null || _originalContainer$re === void 0 ? void 0 : _originalContainer$re.current; // We first check if the item was moved up or down
|
|
47
|
+
// This modifies how to search the matching colliding rect
|
|
48
|
+
|
|
49
|
+
let isUp = lastPosition === 'up';
|
|
50
|
+
|
|
51
|
+
if (originalRect) {
|
|
52
|
+
isUp = originalRect.offsetTop > collisionRect.top;
|
|
53
|
+
} // Threshold
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
const threshold = collisionRect.height * thresholdRatio;
|
|
57
|
+
let collidingContainer = null;
|
|
58
|
+
|
|
59
|
+
if (isUp) {
|
|
60
|
+
// Up -- We need to find the first rectangle downwards
|
|
61
|
+
collidingContainer = droppableContainers.reduce((firstRectDown, container) => {
|
|
62
|
+
const rect = container.rect.current;
|
|
63
|
+
|
|
64
|
+
if (rect && firstRectDown.rect.current) {
|
|
65
|
+
const {
|
|
66
|
+
offsetTop: rectOffsetTop
|
|
67
|
+
} = rect;
|
|
68
|
+
const {
|
|
69
|
+
offsetTop: firstRectDownOffsetTop
|
|
70
|
+
} = firstRectDown.rect.current;
|
|
71
|
+
|
|
72
|
+
if (rectOffsetTop + threshold > collisionRect.top && rectOffsetTop < firstRectDownOffsetTop) {
|
|
73
|
+
return container;
|
|
74
|
+
}
|
|
98
75
|
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return firstRectUp;
|
|
102
|
-
}, {
|
|
103
|
-
id: DUMMY_ID,
|
|
104
|
-
rect: {
|
|
105
|
-
current: RECT_UP
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
} // If we didn't find a match, return null
|
|
109
76
|
|
|
77
|
+
return firstRectDown;
|
|
78
|
+
}, {
|
|
79
|
+
id: DUMMY_ID,
|
|
80
|
+
rect: {
|
|
81
|
+
current: RECT_DOWN
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
} else {
|
|
85
|
+
// Down -- We need to find the first rectangle upwards
|
|
86
|
+
collidingContainer = droppableContainers.reduce((firstRectUp, container) => {
|
|
87
|
+
const rect = container.rect.current;
|
|
88
|
+
|
|
89
|
+
if (rect && firstRectUp.rect.current) {
|
|
90
|
+
const {
|
|
91
|
+
offsetTop: rectOffsetTop
|
|
92
|
+
} = rect;
|
|
93
|
+
const {
|
|
94
|
+
offsetTop: firstRectUpOffsetTop
|
|
95
|
+
} = firstRectUp.rect.current;
|
|
96
|
+
|
|
97
|
+
if (rectOffsetTop - threshold < collisionRect.top && rectOffsetTop > firstRectUpOffsetTop) {
|
|
98
|
+
return container;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return firstRectUp;
|
|
103
|
+
}, {
|
|
104
|
+
id: DUMMY_ID,
|
|
105
|
+
rect: {
|
|
106
|
+
current: RECT_UP
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
} // If we didn't find a match, return null
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
if (collidingContainer.id === DUMMY_ID) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
110
115
|
|
|
111
|
-
|
|
112
|
-
return null;
|
|
113
|
-
}
|
|
116
|
+
const collidingRect = collidingContainer.rect.current;
|
|
117
|
+
if (!collidingRect) return null; // Calculate the intersection interval
|
|
114
118
|
|
|
115
|
-
|
|
116
|
-
if (!collidingRect) return null; // Calculate the intersection interval
|
|
119
|
+
const [top, bottom] = [Math.max(collisionRect.top, collidingRect.offsetTop), Math.min(collisionRect.top + collisionRect.height, collidingRect.offsetTop + collidingRect.height)]; // Calculate the percentage of intersection
|
|
117
120
|
|
|
118
|
-
|
|
121
|
+
const intersectionPercentage = Math.abs(bottom - top) / collidingRect.height;
|
|
119
122
|
|
|
120
|
-
|
|
123
|
+
if (intersectionPercentage > insideThreshold && visibleItemsDictionary[collidingContainer.id].depth + 1 <= maxDragAndDropLevel && collidingContainer.id !== activeId) {
|
|
124
|
+
setDropIndicatorPosition(DropIndicatorPosition.Inside);
|
|
125
|
+
} else {
|
|
126
|
+
setDropIndicatorPosition(isUp ? DropIndicatorPosition.Before : DropIndicatorPosition.After);
|
|
127
|
+
}
|
|
121
128
|
|
|
122
|
-
|
|
123
|
-
setDropIndicatorPosition(DropIndicatorPosition.Inside);
|
|
124
|
-
} else {
|
|
125
|
-
setDropIndicatorPosition(isUp ? DropIndicatorPosition.Before : DropIndicatorPosition.After);
|
|
126
|
-
}
|
|
129
|
+
if (isUp && lastPosition !== 'up') setLastPosition('up');else if (!isUp && lastPosition !== 'down') setLastPosition('down'); // Return the id of the match rectangle
|
|
127
130
|
|
|
128
|
-
|
|
131
|
+
return collidingContainer.id;
|
|
132
|
+
};
|
|
129
133
|
|
|
130
|
-
return
|
|
134
|
+
return func;
|
|
131
135
|
};
|
|
132
136
|
|
|
133
137
|
export { customCollisionDetection };
|
|
@@ -14,7 +14,6 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
|
|
|
14
14
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
15
15
|
const directions = [KeyboardCode.Down, KeyboardCode.Right, KeyboardCode.Up, KeyboardCode.Left];
|
|
16
16
|
const horizontal = [KeyboardCode.Left, KeyboardCode.Right];
|
|
17
|
-
[KeyboardCode.Up, KeyboardCode.Down];
|
|
18
17
|
|
|
19
18
|
const getVerticalKeyboardCoordinates = _ref => {
|
|
20
19
|
var _droppableContainers$, _droppableContainers$2;
|
|
@@ -13,7 +13,8 @@ const useTreeActionHandlers = _ref => {
|
|
|
13
13
|
onReorder,
|
|
14
14
|
flattenedItems,
|
|
15
15
|
projected,
|
|
16
|
-
dropIndicatorPosition
|
|
16
|
+
dropIndicatorPosition,
|
|
17
|
+
isDropValid
|
|
17
18
|
} = _ref;
|
|
18
19
|
const onDragStart = useCallback(e => {
|
|
19
20
|
handlePreviewDragStart(e);
|
|
@@ -30,17 +31,15 @@ const useTreeActionHandlers = _ref => {
|
|
|
30
31
|
active,
|
|
31
32
|
over
|
|
32
33
|
} = e;
|
|
33
|
-
if (over === null) return;
|
|
34
|
+
if (over === null || !isDropValid) return;
|
|
34
35
|
const activeIndex = flattenedItems.findIndex(item => item.uid === active.id);
|
|
35
36
|
let considerExpanding = null;
|
|
36
37
|
let overIndex = flattenedItems.findIndex(item => item.uid === over.id); // If drop indicator is inside, then put it last,
|
|
37
38
|
// It will be reconstructed well later
|
|
38
39
|
|
|
39
40
|
if (dropIndicatorPosition === DropIndicatorPosition.Inside) {
|
|
40
|
-
var _flattenedItems$overI, _flattenedItems$overI2;
|
|
41
|
-
|
|
42
41
|
considerExpanding = over.id;
|
|
43
|
-
overIndex = flattenedItems[overIndex].realIndex +
|
|
42
|
+
overIndex = flattenedItems[overIndex].realIndex + flattenedItems[overIndex].childrenCount + 1;
|
|
44
43
|
} // If we are dropping the item in a new position, or new depth
|
|
45
44
|
|
|
46
45
|
|
|
@@ -55,7 +54,7 @@ const useTreeActionHandlers = _ref => {
|
|
|
55
54
|
fromIndex: activeIndex
|
|
56
55
|
}, considerExpanding || '');
|
|
57
56
|
}
|
|
58
|
-
}, [handlePreviewDragEnd, flattenedItems, projected, onReorder
|
|
57
|
+
}, [handlePreviewDragEnd, isDropValid, flattenedItems, dropIndicatorPosition, projected, onReorder]);
|
|
59
58
|
const onDragCancel = useCallback(e => {
|
|
60
59
|
handlePreviewDragCancel(e);
|
|
61
60
|
}, [handlePreviewDragCancel]);
|
|
@@ -9,7 +9,6 @@ import 'core-js/modules/esnext.iterator.constructor.js';
|
|
|
9
9
|
import 'core-js/modules/esnext.iterator.for-each.js';
|
|
10
10
|
import { useState, useMemo, useRef, useEffect } from 'react';
|
|
11
11
|
import { MeasuringStrategy, useSensors, useSensor, PointerSensor, KeyboardSensor } from '@dnd-kit/core';
|
|
12
|
-
import { horizontalListSortingStrategy, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
|
13
12
|
import { useTreePreviewHandlers } from './useTreePreviewHandlers.js';
|
|
14
13
|
import { getTreeKeyboardCoordinates } from './getTreeKeyboardCoordinates.js';
|
|
15
14
|
import { removeChildrenOf, getProjection } from './utilities.js';
|
|
@@ -23,25 +22,29 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
|
|
|
23
22
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
24
23
|
// if second parameter is true, the space will be done on the horizontal axis
|
|
25
24
|
|
|
26
|
-
const adjustTranslate = isHorizontalDnD =>
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
const adjustTranslate = isHorizontalDnD => {
|
|
26
|
+
const func = _ref => {
|
|
27
|
+
let {
|
|
28
|
+
transform
|
|
29
|
+
} = _ref;
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
const newTransform = _objectSpread({}, transform);
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
if (isHorizontalDnD) {
|
|
34
|
+
newTransform.x = transform.x + 25;
|
|
35
|
+
} else {
|
|
36
|
+
newTransform.x = transform.x + 15;
|
|
37
|
+
}
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
return newTransform;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return func;
|
|
40
43
|
};
|
|
41
44
|
|
|
42
45
|
const measuring = {
|
|
43
46
|
droppable: {
|
|
44
|
-
strategy: MeasuringStrategy.
|
|
47
|
+
strategy: MeasuringStrategy.Always
|
|
45
48
|
}
|
|
46
49
|
};
|
|
47
50
|
const useTreeDndkitConfig = _ref2 => {
|
|
@@ -53,6 +56,7 @@ const useTreeDndkitConfig = _ref2 => {
|
|
|
53
56
|
isHorizontalDnD = false,
|
|
54
57
|
isExpandable = false,
|
|
55
58
|
onReorder,
|
|
59
|
+
getIsDropValid = () => true,
|
|
56
60
|
maxDragAndDropLevel
|
|
57
61
|
} = _ref2;
|
|
58
62
|
const [activeId, setActiveId] = useState('');
|
|
@@ -77,6 +81,10 @@ const useTreeDndkitConfig = _ref2 => {
|
|
|
77
81
|
});
|
|
78
82
|
return dictionary;
|
|
79
83
|
}, [visibleItems]);
|
|
84
|
+
const isDropValid = useMemo(() => {
|
|
85
|
+
if (!activeId || !overId) return true;
|
|
86
|
+
return getIsDropValid(visibleItemsDictionary[activeId], visibleItemsDictionary[overId], ['none', 'before', 'after', 'inside'][dropIndicatorPosition]);
|
|
87
|
+
}, [getIsDropValid, visibleItemsDictionary, activeId, overId, dropIndicatorPosition]);
|
|
80
88
|
const modifiers = useMemo(() => [adjustTranslate(isHorizontalDnD)], [isHorizontalDnD]);
|
|
81
89
|
const sensorContext = useRef({
|
|
82
90
|
items: visibleItems,
|
|
@@ -105,7 +113,8 @@ const useTreeDndkitConfig = _ref2 => {
|
|
|
105
113
|
onReorder,
|
|
106
114
|
projected,
|
|
107
115
|
flattenedItems,
|
|
108
|
-
dropIndicatorPosition
|
|
116
|
+
dropIndicatorPosition,
|
|
117
|
+
isDropValid
|
|
109
118
|
}));
|
|
110
119
|
const announcements = useTreeAnnouncements(visibleItemsDictionary, dropIndicatorPosition);
|
|
111
120
|
const dndContextProps = useMemo(() => _objectSpread({
|
|
@@ -117,11 +126,12 @@ const useTreeDndkitConfig = _ref2 => {
|
|
|
117
126
|
}, dragActionHandlers), [announcements, modifiers, sensors, dragActionHandlers, visibleItemsDictionary, setDropIndicatorPosition, activeId, maxDragAndDropLevel, lastPosition, setLastPosition]);
|
|
118
127
|
const sortableContextProps = useMemo(() => ({
|
|
119
128
|
items: sortedIds,
|
|
120
|
-
strategy:
|
|
121
|
-
}), [sortedIds
|
|
129
|
+
strategy: () => null
|
|
130
|
+
}), [sortedIds]);
|
|
122
131
|
return {
|
|
123
132
|
dndContextProps,
|
|
124
133
|
sortableContextProps,
|
|
134
|
+
isDropValid,
|
|
125
135
|
activeId,
|
|
126
136
|
activeIndex: (_visibleItemsDictiona = (_visibleItemsDictiona2 = visibleItemsDictionary[activeId]) === null || _visibleItemsDictiona2 === void 0 ? void 0 : _visibleItemsDictiona2.realIndex) !== null && _visibleItemsDictiona !== void 0 ? _visibleItemsDictiona : -1,
|
|
127
137
|
overId,
|
package/package.json
CHANGED
|
@@ -64,7 +64,7 @@ export declare type useHierarchyDndkitConfigReturn = {
|
|
|
64
64
|
overId: string | null;
|
|
65
65
|
activeIndex: number | undefined;
|
|
66
66
|
};
|
|
67
|
-
export declare type
|
|
67
|
+
export declare type GetKeyboardCoordinatesArgs = {
|
|
68
68
|
items: Item[];
|
|
69
69
|
active: Active | null;
|
|
70
70
|
over: Over | null;
|
package/types/tree/types.d.ts
CHANGED
|
@@ -4,12 +4,13 @@ import type { SortingStrategy } from '@dnd-kit/sortable';
|
|
|
4
4
|
import { Coordinates } from '@dnd-kit/core/dist/types';
|
|
5
5
|
import React, { MutableRefObject } from 'react';
|
|
6
6
|
import { DropIndicatorPosition } from './constants';
|
|
7
|
-
export declare type Item = {
|
|
7
|
+
export declare type Item<T = unknown> = {
|
|
8
8
|
uid: string;
|
|
9
9
|
depth: number;
|
|
10
|
-
parentId: string;
|
|
10
|
+
parentId: string | null;
|
|
11
11
|
realIndex: number;
|
|
12
|
-
|
|
12
|
+
childrenCount: number;
|
|
13
|
+
original: T;
|
|
13
14
|
};
|
|
14
15
|
export declare type DndContextPropsType = {
|
|
15
16
|
announcements: Announcements;
|
|
@@ -27,49 +28,51 @@ export declare type SortableContextPropsType = {
|
|
|
27
28
|
items: string[];
|
|
28
29
|
strategy: SortingStrategy;
|
|
29
30
|
};
|
|
30
|
-
export declare type
|
|
31
|
+
export declare type UseTreePreviewHandlersReturn = {
|
|
31
32
|
handlePreviewDragStart: (e: DragStartEvent) => void;
|
|
32
33
|
handlePreviewDragMove: (e: DragMoveEvent) => void;
|
|
33
34
|
handlePreviewDragOver: (e: DragOverEvent) => void;
|
|
34
35
|
handlePreviewDragEnd: (e: DragEndEvent) => void;
|
|
35
36
|
handlePreviewDragCancel: (e: DragCancelEvent) => void;
|
|
36
37
|
};
|
|
37
|
-
export declare type
|
|
38
|
+
export declare type UseTreePreviewHandlersArgs = {
|
|
38
39
|
setOverId: React.Dispatch<React.SetStateAction<string>>;
|
|
39
40
|
setActiveId: React.Dispatch<React.SetStateAction<string>>;
|
|
40
41
|
setDropIndicatorPosition: React.Dispatch<React.SetStateAction<DropIndicatorPosition>>;
|
|
41
42
|
};
|
|
42
|
-
export declare type
|
|
43
|
+
export declare type UseTreeActionHandlersArgs<T = unknown> = UseTreePreviewHandlersReturn & {
|
|
43
44
|
dropIndicatorPosition: DropIndicatorPosition;
|
|
44
45
|
flattenedItems: Item[];
|
|
45
46
|
projected: {
|
|
46
47
|
depth: number;
|
|
47
48
|
parentId: string;
|
|
48
49
|
} | null;
|
|
49
|
-
onReorder: (newData: Item[], indexes: {
|
|
50
|
+
onReorder: <S = T>(newData: Item<S>[], indexes: {
|
|
50
51
|
targetIndex: number;
|
|
51
52
|
fromIndex: number;
|
|
52
53
|
}, considerExpanding: string) => void;
|
|
54
|
+
isDropValid: boolean;
|
|
53
55
|
};
|
|
54
|
-
export declare type
|
|
56
|
+
export declare type UseTreeActionHandlersReturn = {
|
|
55
57
|
onDragStart: (e: DragStartEvent) => void;
|
|
56
58
|
onDragMove: (e: DragMoveEvent) => void;
|
|
57
59
|
onDragOver: (e: DragOverEvent) => void;
|
|
58
60
|
onDragEnd: (e: DragEndEvent) => void;
|
|
59
61
|
onDragCancel: (e: DragCancelEvent) => void;
|
|
60
62
|
};
|
|
61
|
-
export declare type
|
|
63
|
+
export declare type UseTreeDndkitConfigArgs<T> = {
|
|
62
64
|
flattenedItems: Item[];
|
|
63
65
|
visibleItems: Item[];
|
|
64
66
|
isHorizontalDnD?: boolean;
|
|
65
67
|
isExpandable: boolean;
|
|
66
|
-
onReorder: (newData: Item[], indexes: {
|
|
68
|
+
onReorder: <S = T>(newData: Item<S>[], indexes: {
|
|
67
69
|
targetIndex: number;
|
|
68
70
|
fromIndex: number;
|
|
69
71
|
}, considerExpanding: string) => void;
|
|
72
|
+
getIsDropValid: <S = T>(active: Item<S>, over: Item<S>, dropIndicatorPosition: 'none' | 'before' | 'after' | 'inside') => boolean;
|
|
70
73
|
maxDragAndDropLevel: number;
|
|
71
74
|
};
|
|
72
|
-
export declare type
|
|
75
|
+
export declare type UseTreeDndkitConfigReturn = {
|
|
73
76
|
dndContextProps: DndContextPropsType;
|
|
74
77
|
sortableContextProps: SortableContextPropsType;
|
|
75
78
|
activeId: string;
|
|
@@ -77,10 +80,11 @@ export declare type useTreeDndkitConfigReturn = {
|
|
|
77
80
|
overId: string;
|
|
78
81
|
depth: number;
|
|
79
82
|
dropIndicatorPosition: DropIndicatorPosition;
|
|
83
|
+
isDropValid: boolean;
|
|
80
84
|
visibleItems: Item[];
|
|
81
85
|
};
|
|
82
|
-
export declare type
|
|
83
|
-
export declare type
|
|
86
|
+
export declare type UseTreeDndkitConfigType = <T = unknown>(args: UseTreeDndkitConfigArgs<T>) => UseTreeDndkitConfigReturn;
|
|
87
|
+
export declare type GetKeyboardCoordinatesArgs = {
|
|
84
88
|
items: Item[];
|
|
85
89
|
active: Active;
|
|
86
90
|
over: Over;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare const useTreeActionHandlers: ({ handlePreviewDragStart, handlePreviewDragMove, handlePreviewDragOver, handlePreviewDragEnd, handlePreviewDragCancel, onReorder, flattenedItems, projected, dropIndicatorPosition, }:
|
|
1
|
+
import type { UseTreeActionHandlersReturn, UseTreeActionHandlersArgs } from './types';
|
|
2
|
+
export declare const useTreeActionHandlers: ({ handlePreviewDragStart, handlePreviewDragMove, handlePreviewDragOver, handlePreviewDragEnd, handlePreviewDragCancel, onReorder, flattenedItems, projected, dropIndicatorPosition, isDropValid, }: UseTreeActionHandlersArgs) => UseTreeActionHandlersReturn;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare const useTreeDndkitConfig:
|
|
1
|
+
import type { UseTreeDndkitConfigType } from './types';
|
|
2
|
+
export declare const useTreeDndkitConfig: UseTreeDndkitConfigType;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare const useTreePreviewHandlers: ({ setOverId, setActiveId, setDropIndicatorPosition, }:
|
|
1
|
+
import type { UseTreePreviewHandlersReturn, UseTreePreviewHandlersArgs } from './types';
|
|
2
|
+
export declare const useTreePreviewHandlers: ({ setOverId, setActiveId, setDropIndicatorPosition, }: UseTreePreviewHandlersArgs) => UseTreePreviewHandlersReturn;
|