@elliemae/ds-treeview 2.2.0-next.3 → 2.2.0-rc.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/TreeView.js +6 -2
- package/cjs/TreeViewContext.js +8 -2
- package/cjs/config/useTreeview.js +13 -3
- package/cjs/index.js +1 -0
- package/cjs/parts/TreeItem.js +68 -43
- package/cjs/parts/TreeList.js +17 -15
- package/cjs/plugins/dnd/TreeDndPlugin.js +37 -0
- package/cjs/plugins/dnd/index.js +1 -0
- package/cjs/plugins/index.js +1 -0
- package/cjs/react-desc-prop-types.js +0 -1
- package/cjs/utils/dnd-helpers.js +140 -0
- package/cjs/utils/keyboard-helpers.js +5 -6
- package/cjs/utils/tree-helpers.js +2 -28
- package/cjs/utils/useDnDHelpers.js +220 -0
- package/cjs/utils/useTree.js +2 -4
- package/esm/TreeView.js +6 -2
- package/esm/TreeViewContext.js +8 -2
- package/esm/config/useTreeview.js +14 -4
- package/esm/index.js +1 -1
- package/esm/parts/TreeItem.js +67 -42
- package/esm/parts/TreeList.js +17 -16
- package/esm/plugins/dnd/TreeDndPlugin.js +33 -1
- package/esm/plugins/dnd/index.js +1 -1
- package/esm/plugins/index.js +1 -1
- package/esm/react-desc-prop-types.js +0 -1
- package/esm/utils/dnd-helpers.js +132 -0
- package/esm/utils/keyboard-helpers.js +5 -6
- package/esm/utils/tree-helpers.js +3 -28
- package/esm/utils/useDnDHelpers.js +216 -0
- package/esm/utils/useTree.js +3 -5
- package/package.json +19 -35
- package/types/TreeView.d.ts +1 -1
- package/types/parts/TreeItem.d.ts +11 -4
- package/types/parts/TreeList.d.ts +4 -0
- package/types/plugins/dnd/TreeDndPlugin.d.ts +2 -0
- package/types/react-desc-prop-types.d.ts +3 -17
- package/types/sharedTypes.d.ts +8 -1
- package/types/utils/dnd-helpers.d.ts +39 -0
- package/types/utils/keyboard-helpers.d.ts +1 -2
- package/types/utils/refs-helpers.d.ts +1 -1
- package/types/utils/tree-helpers.d.ts +1 -4
- package/types/utils/useDnDHelpers.d.ts +26 -0
- package/types/utils/useTree.d.ts +0 -1
- package/cjs/hoc/DnDTreeContext.js +0 -18
- package/cjs/hoc/SortableItemContext.js +0 -21
- package/cjs/hoc/WithConditionalDnDContext.js +0 -138
- package/cjs/hoc/WithDnDSortableItemContext.js +0 -70
- package/cjs/parts/DnDHandle.js +0 -46
- package/cjs/parts/DropIndicator.js +0 -75
- package/esm/hoc/DnDTreeContext.js +0 -14
- package/esm/hoc/SortableItemContext.js +0 -17
- package/esm/hoc/WithConditionalDnDContext.js +0 -128
- package/esm/hoc/WithDnDSortableItemContext.js +0 -61
- package/esm/parts/DnDHandle.js +0 -38
- package/esm/parts/DropIndicator.js +0 -68
- package/types/hoc/DnDTreeContext.d.ts +0 -14
- package/types/hoc/SortableItemContext.d.ts +0 -19
- package/types/hoc/WithConditionalDnDContext.d.ts +0 -4
- package/types/hoc/WithDnDSortableItemContext.d.ts +0 -4
- package/types/parts/DnDHandle.d.ts +0 -6
- package/types/parts/DropIndicator.d.ts +0 -10
|
@@ -19,13 +19,14 @@ var groupExpandsHelpers = require('./group-expands-helpers.js');
|
|
|
19
19
|
// 36 home
|
|
20
20
|
// 56 number 8
|
|
21
21
|
// 106 (NumPad) *
|
|
22
|
-
const useOnItemKeyDown =
|
|
22
|
+
const useOnItemKeyDown = item => {
|
|
23
23
|
const ctx = React.useContext(TreeViewContext["default"]);
|
|
24
24
|
const {
|
|
25
25
|
props: {
|
|
26
26
|
onItemFocus
|
|
27
27
|
},
|
|
28
28
|
triggerTreeRerender,
|
|
29
|
+
isDragging,
|
|
29
30
|
visibleItems,
|
|
30
31
|
setFocusedItem,
|
|
31
32
|
setSelectedItem,
|
|
@@ -54,12 +55,10 @@ const useOnItemKeyDown = (item, draggableProps) => {
|
|
|
54
55
|
keyCode
|
|
55
56
|
} = e;
|
|
56
57
|
|
|
57
|
-
if (!
|
|
58
|
+
if (!isDragging) {
|
|
58
59
|
if (keyCode !== 9) e.preventDefault(); // allow tab/shift+tab
|
|
59
60
|
|
|
60
|
-
if (
|
|
61
|
-
setSelectedItem(item);
|
|
62
|
-
}
|
|
61
|
+
if (keyCode === 13 || keyCode === 32) setSelectedItem(item);
|
|
63
62
|
|
|
64
63
|
if (keyCode === 37) {
|
|
65
64
|
if (isGroup && isExpanded) {
|
|
@@ -147,7 +146,7 @@ const useOnItemKeyDown = (item, draggableProps) => {
|
|
|
147
146
|
}
|
|
148
147
|
}
|
|
149
148
|
}
|
|
150
|
-
}, [
|
|
149
|
+
}, [isDragging, setSelectedItem, item, isGroup, isExpanded, triggerTreeRerender, scrollToIndex, setLatestToggledItem, visibleItems, setFocusedItem, onItemFocusCurry, updateUserExpandedState]);
|
|
151
150
|
return onItemKeyDown;
|
|
152
151
|
};
|
|
153
152
|
const useGlobalKeyboardListener = func => {
|
|
@@ -132,7 +132,7 @@ const getChildrenMatchesSearchQuery = function (parentNode) {
|
|
|
132
132
|
*/
|
|
133
133
|
|
|
134
134
|
const enrichNodeModelInPlace = function (node) {
|
|
135
|
-
var _node$model
|
|
135
|
+
var _node$model, _node$model$children;
|
|
136
136
|
|
|
137
137
|
let searchQuery = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
|
|
138
138
|
let selection = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
|
|
@@ -148,7 +148,7 @@ const enrichNodeModelInPlace = function (node) {
|
|
|
148
148
|
// this is useful to avoid having to always check both the flag and the children.length...
|
|
149
149
|
|
|
150
150
|
|
|
151
|
-
if (typeof node.model.isGroup !== 'boolean' && ((_node$model
|
|
151
|
+
if (typeof node.model.isGroup !== 'boolean' && ((_node$model = node.model) === null || _node$model === void 0 ? void 0 : (_node$model$children = _node$model.children) === null || _node$model$children === void 0 ? void 0 : _node$model$children.length) > 0) {
|
|
152
152
|
node.model.isGroup = true;
|
|
153
153
|
}
|
|
154
154
|
|
|
@@ -180,35 +180,9 @@ const focusItem = item => {
|
|
|
180
180
|
|
|
181
181
|
item === null || item === void 0 ? void 0 : (_item$nodeItemRef = item.nodeItemRef) === null || _item$nodeItemRef === void 0 ? void 0 : (_item$nodeItemRef$cur = _item$nodeItemRef.current) === null || _item$nodeItemRef$cur === void 0 ? void 0 : _item$nodeItemRef$cur.focus();
|
|
182
182
|
};
|
|
183
|
-
const flattenTreeForDnD = function (data) {
|
|
184
|
-
let shouldDescend = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : () => true;
|
|
185
|
-
const flattenedTree = [];
|
|
186
|
-
let index = 0;
|
|
187
|
-
|
|
188
|
-
const doit = function (node) {
|
|
189
|
-
var _node$children$length, _node$children, _node$children2;
|
|
190
|
-
|
|
191
|
-
let depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
192
|
-
let parentId = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
|
|
193
|
-
flattenedTree.push({
|
|
194
|
-
uid: node.id.toString(),
|
|
195
|
-
depth,
|
|
196
|
-
realIndex: index,
|
|
197
|
-
childrenCount: (_node$children$length = (_node$children = node.children) === null || _node$children === void 0 ? void 0 : _node$children.length) !== null && _node$children$length !== void 0 ? _node$children$length : 0,
|
|
198
|
-
parentId: parentId !== null ? parentId.toString() : null,
|
|
199
|
-
original: node
|
|
200
|
-
});
|
|
201
|
-
index += 1;
|
|
202
|
-
if (shouldDescend(node)) (_node$children2 = node.children) === null || _node$children2 === void 0 ? void 0 : _node$children2.forEach(child => doit(child, depth + 1, node.id.toString()));
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
data.forEach(datum => doit(datum));
|
|
206
|
-
return flattenedTree;
|
|
207
|
-
};
|
|
208
183
|
|
|
209
184
|
exports.cloneNode = cloneNode;
|
|
210
185
|
exports.enrichNodeModelInPlace = enrichNodeModelInPlace;
|
|
211
|
-
exports.flattenTreeForDnD = flattenTreeForDnD;
|
|
212
186
|
exports.focusItem = focusItem;
|
|
213
187
|
exports.getChildrenMatchesSearchQuery = getChildrenMatchesSearchQuery;
|
|
214
188
|
exports.getItemsParentNode = getItemsParentNode;
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
require('core-js/modules/esnext.async-iterator.constructor.js');
|
|
6
|
+
require('core-js/modules/esnext.async-iterator.drop.js');
|
|
7
|
+
require('core-js/modules/esnext.iterator.constructor.js');
|
|
8
|
+
require('core-js/modules/esnext.iterator.drop.js');
|
|
9
|
+
require('core-js/modules/web.dom-collections.iterator.js');
|
|
10
|
+
var React = require('react');
|
|
11
|
+
var lodash = require('lodash');
|
|
12
|
+
var TreeViewContext = require('../TreeViewContext.js');
|
|
13
|
+
var treeHelpers = require('./tree-helpers.js');
|
|
14
|
+
var dndHelpers = require('./dnd-helpers.js');
|
|
15
|
+
|
|
16
|
+
const useDnDHelpers = () => {
|
|
17
|
+
const ctx = React.useContext(TreeViewContext["default"]);
|
|
18
|
+
const {
|
|
19
|
+
props: {
|
|
20
|
+
data,
|
|
21
|
+
onOrderChange,
|
|
22
|
+
restrictDragAndDrop
|
|
23
|
+
},
|
|
24
|
+
relativeMouseCord,
|
|
25
|
+
setRelativeMouseCord,
|
|
26
|
+
visibleItems,
|
|
27
|
+
isDragging,
|
|
28
|
+
setIsDragging,
|
|
29
|
+
isKeyboardDragging,
|
|
30
|
+
setIsKeyboardDragging,
|
|
31
|
+
setDraggedItem,
|
|
32
|
+
setFocusedItem,
|
|
33
|
+
triggerTreeRerender,
|
|
34
|
+
tree,
|
|
35
|
+
treeRoot,
|
|
36
|
+
draggedItem
|
|
37
|
+
} = ctx;
|
|
38
|
+
const onSortEnd = React.useCallback(_ref => {
|
|
39
|
+
let {
|
|
40
|
+
oldIndex,
|
|
41
|
+
newIndex,
|
|
42
|
+
isKeySorting
|
|
43
|
+
} = _ref;
|
|
44
|
+
const {
|
|
45
|
+
isBefore,
|
|
46
|
+
isAfter,
|
|
47
|
+
item: overItem,
|
|
48
|
+
isValid
|
|
49
|
+
} = relativeMouseCord;
|
|
50
|
+
|
|
51
|
+
if (draggedItem && tree && treeRoot && overItem) {
|
|
52
|
+
let newItems = lodash.cloneDeep(data); // if dropping in same position do nothing
|
|
53
|
+
// if the drop is not allowed do nothing
|
|
54
|
+
|
|
55
|
+
if (oldIndex === newIndex || !isValid) {
|
|
56
|
+
// onOrderChange(newItems, data);
|
|
57
|
+
setIsDragging(false);
|
|
58
|
+
setDraggedItem(null);
|
|
59
|
+
triggerTreeRerender();
|
|
60
|
+
return;
|
|
61
|
+
} // if we are moving from topToBottom the offset changes
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
const movementDirection = newIndex > oldIndex ? 'topToBottom' : 'bottomToTop';
|
|
65
|
+
let newNestedIndex;
|
|
66
|
+
let newParentNode;
|
|
67
|
+
|
|
68
|
+
if (isKeySorting) {
|
|
69
|
+
const kinship = dndHelpers.inferKeyboardKinshipDrop({
|
|
70
|
+
items: visibleItems,
|
|
71
|
+
newIndex
|
|
72
|
+
});
|
|
73
|
+
newNestedIndex = kinship.newNestedIndex;
|
|
74
|
+
newParentNode = kinship.newParentNode;
|
|
75
|
+
} else {
|
|
76
|
+
const kinship = dndHelpers.inferHoverItemKinshipDrop({
|
|
77
|
+
isBefore,
|
|
78
|
+
isAfter,
|
|
79
|
+
overItem,
|
|
80
|
+
movementDirection,
|
|
81
|
+
draggedItem
|
|
82
|
+
});
|
|
83
|
+
newNestedIndex = kinship.newNestedIndex;
|
|
84
|
+
newParentNode = kinship.newParentNode;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const newMovedItemNode = tree.parse(draggedItem.node.model);
|
|
88
|
+
treeHelpers.getNodeById(treeRoot, draggedItem.id).drop();
|
|
89
|
+
|
|
90
|
+
if (newParentNode && typeof newNestedIndex === 'number') {
|
|
91
|
+
newParentNode.addChildAtIndex(newMovedItemNode, newNestedIndex);
|
|
92
|
+
|
|
93
|
+
if (newParentNode.isRoot()) {
|
|
94
|
+
newItems = [...newParentNode.model.children];
|
|
95
|
+
} else {
|
|
96
|
+
newItems = [...treeRoot.model.children];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
onOrderChange(newItems, data, draggedItem);
|
|
101
|
+
setIsDragging(false);
|
|
102
|
+
setDraggedItem(null);
|
|
103
|
+
triggerTreeRerender();
|
|
104
|
+
}
|
|
105
|
+
}, [data, draggedItem, relativeMouseCord, visibleItems, tree, treeRoot, onOrderChange, triggerTreeRerender, setIsDragging, setDraggedItem]);
|
|
106
|
+
const onSortStart = React.useCallback(() => {}, // ({ node, index, collection, isKeySorting }, event) => {
|
|
107
|
+
// console.log('node :>> ', node);
|
|
108
|
+
// console.log('isKeySorting :>> ', isKeySorting);
|
|
109
|
+
// console.log('event :>> ', event);
|
|
110
|
+
// const item = collection?.[index] || visibleItems[index];
|
|
111
|
+
// setIsDragging(true);
|
|
112
|
+
// setDraggedItem(item);
|
|
113
|
+
// },
|
|
114
|
+
[]);
|
|
115
|
+
const updateBeforeSortStart = React.useCallback(beforeStartArgs => new Promise(resolve => {
|
|
116
|
+
const {
|
|
117
|
+
index,
|
|
118
|
+
isKeySorting
|
|
119
|
+
} = beforeStartArgs;
|
|
120
|
+
const item = visibleItems[index];
|
|
121
|
+
const {
|
|
122
|
+
isExpanded
|
|
123
|
+
} = item.node.model;
|
|
124
|
+
|
|
125
|
+
if (isExpanded) {
|
|
126
|
+
item.node.model.isExpanded = false;
|
|
127
|
+
triggerTreeRerender();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
setRelativeMouseCord(TreeViewContext.defaultRelativeMouseCoord);
|
|
131
|
+
setIsKeyboardDragging(isKeySorting);
|
|
132
|
+
setIsDragging(true);
|
|
133
|
+
setDraggedItem(item);
|
|
134
|
+
treeHelpers.focusItem(null);
|
|
135
|
+
setFocusedItem(null);
|
|
136
|
+
if (document.activeElement && typeof document.activeElement.blur === 'function') document.activeElement.blur();
|
|
137
|
+
resolve(true);
|
|
138
|
+
}), [visibleItems, setRelativeMouseCord, setIsKeyboardDragging, setIsDragging, setDraggedItem, setFocusedItem, triggerTreeRerender]); // tracks mouse coords relative to an element to check if the drop happens
|
|
139
|
+
// at the top/center/bottom of an element to choose where to move the element
|
|
140
|
+
// requested in the specs
|
|
141
|
+
|
|
142
|
+
const onMouseDragOverItem = React.useCallback(_ref2 => {
|
|
143
|
+
let {
|
|
144
|
+
event,
|
|
145
|
+
item,
|
|
146
|
+
itemIndex,
|
|
147
|
+
setIsDraggingOverThis,
|
|
148
|
+
openFolderOnHoverTimeout
|
|
149
|
+
} = _ref2;
|
|
150
|
+
const {
|
|
151
|
+
node
|
|
152
|
+
} = item;
|
|
153
|
+
const {
|
|
154
|
+
nodeItemRef
|
|
155
|
+
} = node.model;
|
|
156
|
+
|
|
157
|
+
if (isDragging && !isKeyboardDragging && draggedItem) {
|
|
158
|
+
var _nodeItemRef$current;
|
|
159
|
+
|
|
160
|
+
const rect = nodeItemRef === null || nodeItemRef === void 0 ? void 0 : (_nodeItemRef$current = nodeItemRef.current) === null || _nodeItemRef$current === void 0 ? void 0 : _nodeItemRef$current.getBoundingClientRect();
|
|
161
|
+
|
|
162
|
+
if (rect) {
|
|
163
|
+
const x = event.clientX - rect.left; // x position within the element.
|
|
164
|
+
|
|
165
|
+
const y = event.clientY - rect.top; // y position within the element.
|
|
166
|
+
// by spec we need to track where the drop will happens
|
|
167
|
+
// if drop happens on top 25% drop before
|
|
168
|
+
// if drop happens on bottom of 25% drop after
|
|
169
|
+
// if drop happens in the middle 50% either invalid or drop into children
|
|
170
|
+
|
|
171
|
+
const {
|
|
172
|
+
height: elementHeight
|
|
173
|
+
} = rect;
|
|
174
|
+
const dropBeforeMaxY = elementHeight * 0.25;
|
|
175
|
+
const dropAfterMinY = elementHeight - elementHeight * 0.25;
|
|
176
|
+
const isBefore = y < dropBeforeMaxY;
|
|
177
|
+
const isAfter = y > dropAfterMinY;
|
|
178
|
+
const isValid = dndHelpers.isDropValid({
|
|
179
|
+
isBefore,
|
|
180
|
+
isAfter,
|
|
181
|
+
draggedItem,
|
|
182
|
+
overItem: item,
|
|
183
|
+
restrictDragAndDrop
|
|
184
|
+
});
|
|
185
|
+
setRelativeMouseCord({
|
|
186
|
+
isBefore,
|
|
187
|
+
isAfter,
|
|
188
|
+
isValid,
|
|
189
|
+
item,
|
|
190
|
+
x,
|
|
191
|
+
y,
|
|
192
|
+
relativeElementDOMRect: rect,
|
|
193
|
+
relativeElementIndex: itemIndex
|
|
194
|
+
});
|
|
195
|
+
setIsDraggingOverThis(true);
|
|
196
|
+
|
|
197
|
+
if (!(openFolderOnHoverTimeout !== null && openFolderOnHoverTimeout !== void 0 && openFolderOnHoverTimeout.current) && // only set the interval if it wasn't set already once
|
|
198
|
+
(draggedItem === null || draggedItem === void 0 ? void 0 : draggedItem.id) !== (item === null || item === void 0 ? void 0 : item.id) && ( // don't open ghost element
|
|
199
|
+
item.isGroup || item.children.length > 0) && // only open groups
|
|
200
|
+
!restrictDragAndDrop) {
|
|
201
|
+
openFolderOnHoverTimeout.current = setTimeout(() => {
|
|
202
|
+
item.node.model.isExpanded = true;
|
|
203
|
+
openFolderOnHoverTimeout.current = null;
|
|
204
|
+
triggerTreeRerender();
|
|
205
|
+
}, 1000);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
} else {
|
|
209
|
+
setRelativeMouseCord(TreeViewContext.defaultRelativeMouseCoord);
|
|
210
|
+
}
|
|
211
|
+
}, [isDragging, isKeyboardDragging, draggedItem, restrictDragAndDrop, setRelativeMouseCord, triggerTreeRerender]);
|
|
212
|
+
return {
|
|
213
|
+
updateBeforeSortStart,
|
|
214
|
+
onSortStart,
|
|
215
|
+
onSortEnd,
|
|
216
|
+
onMouseDragOverItem
|
|
217
|
+
};
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
exports.useDnDHelpers = useDnDHelpers;
|
package/cjs/utils/useTree.js
CHANGED
|
@@ -76,9 +76,9 @@ const useTree = (data, props, states) => {
|
|
|
76
76
|
React.useEffect(() => {
|
|
77
77
|
const parsedVisibleItems = [];
|
|
78
78
|
const newExpandedHashMap = {};
|
|
79
|
-
const shouldContinueWalking = true; // tree-model ask we return true if we want to continue walking
|
|
80
|
-
|
|
81
79
|
treeRoot.walk(node => {
|
|
80
|
+
const shouldContinueWalking = true; // tree-model ask we return true if we want to continue walking
|
|
81
|
+
|
|
82
82
|
treeHelpers.enrichNodeModelInPlace(node, searchQuery, selection, expanded);
|
|
83
83
|
if (node.model.id && node.model.isGroup && node.model.isExpanded) newExpandedHashMap[node.model.id] = true;
|
|
84
84
|
return shouldContinueWalking;
|
|
@@ -93,10 +93,8 @@ const useTree = (data, props, states) => {
|
|
|
93
93
|
setExpandedGroups(newExpandedHashMap); // eslint-disable-next-line react-hooks/exhaustive-deps
|
|
94
94
|
}, [treeRoot, rerenderItems, searchQuery, highlightOnlyQuery, selection, expanded, setExpandedGroups // onVisibleItemsChange // evaluate potential performance hit?
|
|
95
95
|
]);
|
|
96
|
-
const flattenedItems = React.useMemo(() => treeHelpers.flattenTreeForDnD(data), [data]);
|
|
97
96
|
return {
|
|
98
97
|
visibleItems,
|
|
99
|
-
flattenedItems,
|
|
100
98
|
tree,
|
|
101
99
|
treeRoot,
|
|
102
100
|
rerenderItems,
|
package/esm/TreeView.js
CHANGED
|
@@ -3,17 +3,21 @@ import 'react';
|
|
|
3
3
|
import { describe } from 'react-desc';
|
|
4
4
|
import { TreeViewPropTypes } from './react-desc-prop-types.js';
|
|
5
5
|
import TreeViewContext from './TreeViewContext.js';
|
|
6
|
+
import { TreeDndPlugin, TreeDnD } from './plugins/dnd/TreeDndPlugin.js';
|
|
6
7
|
import { useTreeview } from './config/useTreeview.js';
|
|
7
8
|
import { TreeList } from './parts/TreeList.js';
|
|
8
9
|
|
|
9
|
-
var _TreeList;
|
|
10
|
+
var _TreeDnD, _TreeList;
|
|
10
11
|
function TreeView(props) {
|
|
12
|
+
const {
|
|
13
|
+
plugins
|
|
14
|
+
} = props;
|
|
11
15
|
const {
|
|
12
16
|
ctx
|
|
13
17
|
} = useTreeview(props);
|
|
14
18
|
return /*#__PURE__*/_jsx(TreeViewContext.Provider, {
|
|
15
19
|
value: ctx
|
|
16
|
-
}, void 0, _TreeList || (_TreeList = /*#__PURE__*/_jsx(TreeList, {})));
|
|
20
|
+
}, void 0, plugins !== null && plugins !== void 0 && plugins.includes(TreeDndPlugin) ? _TreeDnD || (_TreeDnD = /*#__PURE__*/_jsx(TreeDnD, {})) : _TreeList || (_TreeList = /*#__PURE__*/_jsx(TreeList, {})));
|
|
17
21
|
}
|
|
18
22
|
const TreeViewWithSchema = describe(TreeView);
|
|
19
23
|
TreeViewWithSchema.propTypes = TreeViewPropTypes;
|
package/esm/TreeViewContext.js
CHANGED
|
@@ -14,7 +14,6 @@ const defaultProps = {
|
|
|
14
14
|
fluid: false,
|
|
15
15
|
isMultiSelect: false,
|
|
16
16
|
isSingleSelect: false,
|
|
17
|
-
getIsDropValid: () => true,
|
|
18
17
|
restrictDragAndDrop: false,
|
|
19
18
|
showChildrenAmount: false,
|
|
20
19
|
sortable: false,
|
|
@@ -55,10 +54,11 @@ const defaultRelativeMouseCoord = {
|
|
|
55
54
|
const defaultContext = {
|
|
56
55
|
props: defaultProps,
|
|
57
56
|
visibleItems: [],
|
|
58
|
-
flattenedItems: [],
|
|
59
57
|
withRadioChecks: false,
|
|
60
58
|
withCheckboxChecks: false,
|
|
61
59
|
withDragAndDrop: false,
|
|
60
|
+
relativeMouseCord: defaultRelativeMouseCoord,
|
|
61
|
+
setRelativeMouseCord: noop,
|
|
62
62
|
triggerTreeRerender: noop,
|
|
63
63
|
tree: undefined,
|
|
64
64
|
treeRoot: undefined,
|
|
@@ -69,6 +69,12 @@ const defaultContext = {
|
|
|
69
69
|
scrollToIndex: noop
|
|
70
70
|
},
|
|
71
71
|
virtualListRef: undefined,
|
|
72
|
+
isDragging: false,
|
|
73
|
+
setIsDragging: noop,
|
|
74
|
+
isKeyboardDragging: false,
|
|
75
|
+
setIsKeyboardDragging: noop,
|
|
76
|
+
draggedItem: null,
|
|
77
|
+
setDraggedItem: noop,
|
|
72
78
|
uniqueTreeViewUUID: '',
|
|
73
79
|
selectedItem: null,
|
|
74
80
|
setSelectedItem: noop,
|
|
@@ -10,7 +10,7 @@ import { useVirtual } from 'react-virtual';
|
|
|
10
10
|
import { useMemoMergePropsWithDefault, useValidateTypescriptPropTypes } from '@elliemae/ds-props-helpers';
|
|
11
11
|
import { v4 } from 'uuid';
|
|
12
12
|
import { TreeViewPropTypes } from '../react-desc-prop-types.js';
|
|
13
|
-
import { defaultProps } from '../TreeViewContext.js';
|
|
13
|
+
import { defaultProps, defaultRelativeMouseCoord } from '../TreeViewContext.js';
|
|
14
14
|
import { TreeDndPlugin } from '../plugins/dnd/TreeDndPlugin.js';
|
|
15
15
|
import { useTree } from '../utils/useTree.js';
|
|
16
16
|
import { toggleItemExpand, useNotifyExpandedChange } from '../utils/group-expands-helpers.js';
|
|
@@ -42,7 +42,11 @@ const useTreeview = props => {
|
|
|
42
42
|
selection,
|
|
43
43
|
onInstanceRefInitialized
|
|
44
44
|
} = propsWithDefault;
|
|
45
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
46
|
+
const [isKeyboardDragging, setIsKeyboardDragging] = useState(false);
|
|
47
|
+
const [relativeMouseCord, setRelativeMouseCord] = useState(defaultRelativeMouseCoord);
|
|
45
48
|
const [uniqueTreeViewUUID] = useState(v4());
|
|
49
|
+
const [draggedItem, setDraggedItem] = useState(null);
|
|
46
50
|
const [hoverItem, setHoverItem] = useState(null);
|
|
47
51
|
const [latestToggledItem, setLatestToggledItem] = useState(null);
|
|
48
52
|
const [selectedItem, setSelectedItem] = useState(null);
|
|
@@ -51,7 +55,6 @@ const useTreeview = props => {
|
|
|
51
55
|
const [expandedGroups, setExpandedGroups] = useState({});
|
|
52
56
|
const {
|
|
53
57
|
visibleItems,
|
|
54
|
-
flattenedItems,
|
|
55
58
|
tree,
|
|
56
59
|
treeRoot,
|
|
57
60
|
triggerTreeRerender,
|
|
@@ -73,13 +76,20 @@ const useTreeview = props => {
|
|
|
73
76
|
const handleExpandGroup = useCallback(toggleItemExpand, []);
|
|
74
77
|
const ctx = useMemo(() => ({
|
|
75
78
|
props: propsWithDefault,
|
|
79
|
+
setRelativeMouseCord,
|
|
80
|
+
relativeMouseCord,
|
|
76
81
|
virtualListHelpers,
|
|
77
82
|
virtualListRef,
|
|
78
83
|
tree,
|
|
79
84
|
treeRoot,
|
|
80
85
|
visibleItems,
|
|
81
|
-
flattenedItems,
|
|
82
86
|
triggerTreeRerender,
|
|
87
|
+
isDragging,
|
|
88
|
+
setIsDragging,
|
|
89
|
+
isKeyboardDragging,
|
|
90
|
+
setIsKeyboardDragging,
|
|
91
|
+
draggedItem,
|
|
92
|
+
setDraggedItem,
|
|
83
93
|
withRadioChecks,
|
|
84
94
|
withCheckboxChecks,
|
|
85
95
|
withDragAndDrop,
|
|
@@ -98,7 +108,7 @@ const useTreeview = props => {
|
|
|
98
108
|
setHoverItem,
|
|
99
109
|
handleExpandGroup,
|
|
100
110
|
updateUserExpandedState
|
|
101
|
-
}), [
|
|
111
|
+
}), [draggedItem, expandedGroups, focusedItem, handleExpandGroup, hoverItem, isDragging, isKeyboardDragging, latestToggledItem, propsWithDefault, relativeMouseCord, selectedCheckboxes, selectedItem, tree, treeRoot, triggerTreeRerender, uniqueTreeViewUUID, virtualListHelpers, visibleItems, withCheckboxChecks, withDragAndDrop, withRadioChecks, updateUserExpandedState]); // this hooks can't use the context because the context provider doesn't wrap this
|
|
102
112
|
// as such we manually pass the second parameter as fit
|
|
103
113
|
|
|
104
114
|
if (instanceRef) instanceRef.current = treeRef.current;
|
package/esm/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { TreeView, TreeViewWithSchema } from './TreeView.js';
|
|
2
|
-
export { TreeDndPlugin } from './plugins/dnd/TreeDndPlugin.js';
|
|
2
|
+
export { TreeDnD, TreeDndPlugin } from './plugins/dnd/TreeDndPlugin.js';
|
|
3
3
|
export { TreeRovingPlugin } from './plugins/roving/TreeRovingPlugin.js';
|
|
4
4
|
export { SelectablePluginTree } from './plugins/selectable/SelectablePluginTree.js';
|
|
5
5
|
export { TreeToolbarPlugin } from './plugins/toolbar/TreeToolbarPlugin.js';
|
package/esm/parts/TreeItem.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
import _defineProperty from '@babel/runtime/helpers/esm/defineProperty';
|
|
2
|
+
import _jsx from '@babel/runtime/helpers/esm/jsx';
|
|
3
|
+
import 'core-js/modules/web.dom-collections.iterator.js';
|
|
1
4
|
import 'core-js/modules/esnext.async-iterator.filter.js';
|
|
2
5
|
import 'core-js/modules/esnext.iterator.constructor.js';
|
|
3
6
|
import 'core-js/modules/esnext.iterator.filter.js';
|
|
4
7
|
import 'core-js/modules/esnext.async-iterator.for-each.js';
|
|
5
8
|
import 'core-js/modules/esnext.iterator.for-each.js';
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import { useContext, useMemo, useCallback } from 'react';
|
|
9
|
+
import { useRef, useState, useContext, useMemo, useCallback } from 'react';
|
|
10
|
+
import { SortableHandle, SortableElement } from '@elliemae/react-sortable-hoc';
|
|
9
11
|
import { aggregatedClasses } from '@elliemae/ds-classnames';
|
|
12
|
+
import { GripperVertical } from '@elliemae/ds-icons';
|
|
10
13
|
import RadioSelectable from './RadioSelectable.js';
|
|
11
14
|
import CheckboxSelectable from './CheckboxSelectable.js';
|
|
12
15
|
import TreeViewContext from '../TreeViewContext.js';
|
|
@@ -17,10 +20,6 @@ import { Icon } from './Icon.js';
|
|
|
17
20
|
import { ExpandCaret } from './ExpandCaret.js';
|
|
18
21
|
import { TreeItemText } from './TreeItemText.js';
|
|
19
22
|
import { treeItemBlockName, DSTreeViewPrefix } from '../config/cssClassesConstants.js';
|
|
20
|
-
import { DnDHandle } from './DnDHandle.js';
|
|
21
|
-
import { withDnDSortableItemContext } from '../hoc/WithDnDSortableItemContext.js';
|
|
22
|
-
import { SortableItemContext, DropIndicatorPosition } from '../hoc/SortableItemContext.js';
|
|
23
|
-
import DropIndicator from './DropIndicator.js';
|
|
24
23
|
import { jsxs } from 'react/jsx-runtime';
|
|
25
24
|
|
|
26
25
|
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
@@ -33,6 +32,23 @@ const TreeListItem = aggregatedClasses('li', {
|
|
|
33
32
|
})(treeItemBlockName);
|
|
34
33
|
const TreeListItemAddonsWrapper = aggregatedClasses('div')(treeItemBlockName, 'addons' // temp fix untill we move to styled components
|
|
35
34
|
);
|
|
35
|
+
const DnDHandle = SortableHandle(_ref => {
|
|
36
|
+
let {
|
|
37
|
+
id
|
|
38
|
+
} = _ref;
|
|
39
|
+
return /*#__PURE__*/_jsx(GripperVertical, {
|
|
40
|
+
"data-testid": "drag-handle",
|
|
41
|
+
className: "drag-handle",
|
|
42
|
+
size: "s",
|
|
43
|
+
onMouseDown: e => {
|
|
44
|
+
// prevent focusing the drag-handle to avoid borders
|
|
45
|
+
if (e) {
|
|
46
|
+
e.preventDefault();
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
tabIndex: "0"
|
|
50
|
+
}, "".concat(id, "-drag-handle"));
|
|
51
|
+
});
|
|
36
52
|
const TreeItem = props => {
|
|
37
53
|
const {
|
|
38
54
|
item,
|
|
@@ -40,7 +56,7 @@ const TreeItem = props => {
|
|
|
40
56
|
itemWrapperStyle = {},
|
|
41
57
|
virtualItemRef,
|
|
42
58
|
spaceForNestingLevel = 1.5,
|
|
43
|
-
|
|
59
|
+
onMouseDragOverItem
|
|
44
60
|
} = props;
|
|
45
61
|
const {
|
|
46
62
|
id,
|
|
@@ -49,10 +65,9 @@ const TreeItem = props => {
|
|
|
49
65
|
const {
|
|
50
66
|
nodeItemRef
|
|
51
67
|
} = node.model;
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const onItemKeyDown = useOnItemKeyDown(item, draggableProps);
|
|
68
|
+
const openFolderOnHoverTimeout = useRef(null);
|
|
69
|
+
const [isDraggingOverThis, setIsDraggingOverThis] = useState(false);
|
|
70
|
+
const onItemKeyDown = useOnItemKeyDown(item);
|
|
56
71
|
const ctx = useContext(TreeViewContext);
|
|
57
72
|
const {
|
|
58
73
|
props: {
|
|
@@ -63,12 +78,14 @@ const TreeItem = props => {
|
|
|
63
78
|
rowSize,
|
|
64
79
|
labelOverflow
|
|
65
80
|
},
|
|
81
|
+
relativeMouseCord,
|
|
66
82
|
withRadioChecks,
|
|
67
83
|
withCheckboxChecks,
|
|
68
84
|
withDragAndDrop,
|
|
69
85
|
selectedItem,
|
|
70
86
|
selectedCheckboxes,
|
|
71
87
|
hoverItem,
|
|
88
|
+
isDragging,
|
|
72
89
|
virtualListHelpers: {
|
|
73
90
|
scrollToIndex
|
|
74
91
|
},
|
|
@@ -78,27 +95,24 @@ const TreeItem = props => {
|
|
|
78
95
|
} = ctx; // Sortable Items needs to update
|
|
79
96
|
// - the virtual list ref (for react-virtual requirement)
|
|
80
97
|
// - nodeItemRef which is a prop avaiable in the TreeModel nodes
|
|
81
|
-
// - the drag and drop node reference
|
|
82
98
|
|
|
83
99
|
const setItemRefs = ref => {
|
|
84
|
-
setMultipleRefs(virtualItemRef,
|
|
100
|
+
setMultipleRefs(virtualItemRef, nodeItemRef)(ref);
|
|
85
101
|
};
|
|
86
102
|
|
|
87
|
-
const dropIndicatorPosition = draggableProps && draggableProps.shouldShowDropIndicatorPosition && draggableProps.dropIndicatorPosition;
|
|
88
|
-
const isDropIndicatorPositionInside = dropIndicatorPosition === DropIndicatorPosition.Inside;
|
|
89
|
-
const isDragging = draggableProps && draggableProps.isDragging;
|
|
90
|
-
const isDropValid = draggableProps && draggableProps.isDropValid;
|
|
91
103
|
const dropPreviewClassName = useMemo(() => {
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
104
|
+
if (!withDragAndDrop || !relativeMouseCord || !isDraggingOverThis) return '';
|
|
105
|
+
const {
|
|
106
|
+
isBefore,
|
|
107
|
+
isAfter,
|
|
108
|
+
isValid
|
|
109
|
+
} = relativeMouseCord;
|
|
110
|
+
let className = "".concat(DSTreeViewPrefix, "-").concat(treeItemBlockName).concat(isValid ? '--valid' : '--invalid');
|
|
111
|
+
if (isBefore) className += '-drop-before';else if (isAfter) className += '-drop-after';else className += '-drop-in';
|
|
112
|
+
return className;
|
|
113
|
+
}, [withDragAndDrop, relativeMouseCord, isDraggingOverThis]);
|
|
114
|
+
const selectedClassName = useMemo(() => (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.id) === item.id ? 'selected' : '', [item.id, selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.id]);
|
|
115
|
+
const hoverClassName = useMemo(() => (hoverItem === null || hoverItem === void 0 ? void 0 : hoverItem.id) !== item.id || withDragAndDrop && isDragging ? '' : 'hover', [hoverItem === null || hoverItem === void 0 ? void 0 : hoverItem.id, item.id, withDragAndDrop, isDragging]);
|
|
102
116
|
const fixedHeightClassname = useMemo(() => {
|
|
103
117
|
if (labelOverflow === 'truncate') {
|
|
104
118
|
if (rowSize === 'compact') return "".concat(DSTreeViewPrefix, "-").concat(treeItemBlockName, "--fixed-height-compact");
|
|
@@ -107,7 +121,7 @@ const TreeItem = props => {
|
|
|
107
121
|
|
|
108
122
|
return '';
|
|
109
123
|
}, [labelOverflow, rowSize]);
|
|
110
|
-
const finalClassName = useMemo(() => "".concat(dropPreviewClassName, " ").concat(fixedHeightClassname, " ").concat(selectedClassName, " ").concat(hoverClassName
|
|
124
|
+
const finalClassName = useMemo(() => "".concat(dropPreviewClassName, " ").concat(fixedHeightClassname, " ").concat(selectedClassName, " ").concat(hoverClassName), [dropPreviewClassName, selectedClassName, hoverClassName, fixedHeightClassname]);
|
|
111
125
|
const handleItemClick = useCallback(e => {
|
|
112
126
|
onItemClick(item, e);
|
|
113
127
|
setFocusedItem(item);
|
|
@@ -126,7 +140,23 @@ const TreeItem = props => {
|
|
|
126
140
|
}, [item, scrollToIndex, onItemClick, onItemFocus, setSelectedItem, setFocusedItem]);
|
|
127
141
|
return /*#__PURE__*/jsxs(TreeListItem, {
|
|
128
142
|
tabIndex: 0,
|
|
143
|
+
onMouseMove: event => {
|
|
144
|
+
if (withDragAndDrop && onMouseDragOverItem) {
|
|
145
|
+
onMouseDragOverItem({
|
|
146
|
+
event,
|
|
147
|
+
item,
|
|
148
|
+
itemIndex,
|
|
149
|
+
setIsDraggingOverThis,
|
|
150
|
+
openFolderOnHoverTimeout
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
},
|
|
129
154
|
onMouseLeave: () => {
|
|
155
|
+
if (withDragAndDrop) {
|
|
156
|
+
setIsDraggingOverThis(false);
|
|
157
|
+
if (openFolderOnHoverTimeout !== null && openFolderOnHoverTimeout !== void 0 && openFolderOnHoverTimeout.current) clearTimeout(openFolderOnHoverTimeout.current);
|
|
158
|
+
}
|
|
159
|
+
|
|
130
160
|
if ((hoverItem === null || hoverItem === void 0 ? void 0 : hoverItem.id) === item.id) {
|
|
131
161
|
setHoverItem(null);
|
|
132
162
|
}
|
|
@@ -135,7 +165,6 @@ const TreeItem = props => {
|
|
|
135
165
|
setHoverItem(item);
|
|
136
166
|
},
|
|
137
167
|
onClick: handleItemClick,
|
|
138
|
-
onBlur: () => setFocusedItem(null),
|
|
139
168
|
ref: setItemRefs,
|
|
140
169
|
style: _objectSpread({}, itemWrapperStyle),
|
|
141
170
|
className: finalClassName,
|
|
@@ -145,29 +174,25 @@ const TreeItem = props => {
|
|
|
145
174
|
"data-index": item.virtualIndex,
|
|
146
175
|
onKeyDown: onItemKeyDown,
|
|
147
176
|
children: [/*#__PURE__*/_jsx(TreeListItemAddonsWrapper, {}, void 0, withDragAndDrop ? /*#__PURE__*/_jsx(DnDHandle, {
|
|
148
|
-
id: id
|
|
149
|
-
|
|
150
|
-
}) : null, withRadioChecks && !isDragOverlay ? /*#__PURE__*/_jsx(RadioSelectable, {
|
|
177
|
+
id: id
|
|
178
|
+
}) : null, withRadioChecks ? /*#__PURE__*/_jsx(RadioSelectable, {
|
|
151
179
|
item: item,
|
|
152
180
|
itemIndex: itemIndex
|
|
153
|
-
}) : null, withCheckboxChecks
|
|
181
|
+
}) : null, withCheckboxChecks ? /*#__PURE__*/_jsx(CheckboxSelectable, {
|
|
154
182
|
item: item,
|
|
155
183
|
itemIndex: itemIndex
|
|
156
|
-
}) : null,
|
|
184
|
+
}) : null, /*#__PURE__*/_jsx(NestingSpace, {
|
|
157
185
|
item: item,
|
|
158
186
|
spaceForNestingLevel: spaceForNestingLevel
|
|
159
|
-
}),
|
|
187
|
+
}), /*#__PURE__*/_jsx(ExpandCaret, {
|
|
160
188
|
item: item
|
|
161
189
|
}), !disableIcons ? /*#__PURE__*/_jsx(Icon, {
|
|
162
190
|
item: item
|
|
163
191
|
}) : null), /*#__PURE__*/_jsx(TreeItemText, {
|
|
164
192
|
item: item
|
|
165
|
-
}), rightAddons && /*#__PURE__*/_jsx(TreeListItemAddonsWrapper, {}, void 0, typeof rightAddons === 'function' ? rightAddons(item) : rightAddons)
|
|
166
|
-
dropIndicatorPosition: dropIndicatorPosition,
|
|
167
|
-
isDropValid: isDropValid
|
|
168
|
-
})]
|
|
193
|
+
}), rightAddons && /*#__PURE__*/_jsx(TreeListItemAddonsWrapper, {}, void 0, typeof rightAddons === 'function' ? rightAddons(item) : rightAddons)]
|
|
169
194
|
});
|
|
170
195
|
};
|
|
171
|
-
|
|
196
|
+
const DragAndDropTreeItem = SortableElement(TreeItem);
|
|
172
197
|
|
|
173
|
-
export { TreeItem, TreeListItem, TreeListItemAddonsWrapper
|
|
198
|
+
export { DragAndDropTreeItem, TreeItem, TreeListItem, TreeListItemAddonsWrapper };
|