@elliemae/ds-treeview 2.2.0-next.6 → 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
package/esm/parts/TreeList.js
CHANGED
|
@@ -8,13 +8,12 @@ import 'core-js/modules/esnext.iterator.filter.js';
|
|
|
8
8
|
import 'core-js/modules/esnext.async-iterator.for-each.js';
|
|
9
9
|
import 'core-js/modules/esnext.iterator.for-each.js';
|
|
10
10
|
import { useContext } from 'react';
|
|
11
|
+
import { SortableContainer } from '@elliemae/react-sortable-hoc';
|
|
11
12
|
import { aggregatedClasses } from '@elliemae/ds-classnames';
|
|
12
13
|
import { DSCircularProgressIndicator } from '@elliemae/ds-circular-progress-indicator';
|
|
13
14
|
import TreeViewContext from '../TreeViewContext.js';
|
|
14
15
|
import { treeListBlockName, DSTreeViewPrefix } from '../config/cssClassesConstants.js';
|
|
15
|
-
import TreeItem from './TreeItem.js';
|
|
16
|
-
import { withConditionalDnDRowContext } from '../hoc/WithConditionalDnDContext.js';
|
|
17
|
-
import { DnDTreeContext } from '../hoc/DnDTreeContext.js';
|
|
16
|
+
import { DragAndDropTreeItem, TreeItem } from './TreeItem.js';
|
|
18
17
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
19
18
|
|
|
20
19
|
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; }
|
|
@@ -39,13 +38,16 @@ const TreeListWrapper = aggregatedClasses('ul', {
|
|
|
39
38
|
});
|
|
40
39
|
const compactRowClass = "".concat(DSTreeViewPrefix, "-tv-row-size-compact");
|
|
41
40
|
const normaRowClass = "".concat(DSTreeViewPrefix, "-tv-row-size-normal");
|
|
42
|
-
|
|
43
|
-
const
|
|
41
|
+
const TreeList = props => {
|
|
42
|
+
const {
|
|
43
|
+
onMouseDragOverItem
|
|
44
|
+
} = props;
|
|
44
45
|
const ctx = useContext(TreeViewContext);
|
|
45
46
|
const {
|
|
46
47
|
virtualListRef,
|
|
47
48
|
virtualListHelpers,
|
|
48
|
-
visibleItems
|
|
49
|
+
visibleItems,
|
|
50
|
+
withDragAndDrop,
|
|
49
51
|
props: {
|
|
50
52
|
width,
|
|
51
53
|
height,
|
|
@@ -59,9 +61,6 @@ const TreeListComp = () => {
|
|
|
59
61
|
virtualItems
|
|
60
62
|
} = virtualListHelpers;
|
|
61
63
|
const className = "".concat(rowSize === 'compact' ? "".concat(compactRowClass) : "".concat(normaRowClass));
|
|
62
|
-
const {
|
|
63
|
-
visibleItems
|
|
64
|
-
} = useContext(DnDTreeContext);
|
|
65
64
|
return /*#__PURE__*/jsxs(TreeListWrapper, {
|
|
66
65
|
ref: virtualListRef,
|
|
67
66
|
className: className,
|
|
@@ -96,17 +95,17 @@ const TreeListComp = () => {
|
|
|
96
95
|
measureRef,
|
|
97
96
|
start
|
|
98
97
|
} = virtualItem;
|
|
99
|
-
const item =
|
|
100
|
-
if (!item) return null;
|
|
98
|
+
const item = visibleItems[index];
|
|
101
99
|
item.virtualIndex = index;
|
|
102
100
|
const {
|
|
103
101
|
id
|
|
104
102
|
} = item;
|
|
105
103
|
const style = {
|
|
106
104
|
position: 'absolute',
|
|
107
|
-
top:
|
|
105
|
+
top: 0,
|
|
108
106
|
left: 0,
|
|
109
|
-
width: '100%'
|
|
107
|
+
width: '100%',
|
|
108
|
+
transform: "translateY(".concat(start, "px)")
|
|
110
109
|
};
|
|
111
110
|
const listItemProps = {
|
|
112
111
|
key: "DS-TreeView-List-Item-".concat(id),
|
|
@@ -117,11 +116,13 @@ const TreeListComp = () => {
|
|
|
117
116
|
item,
|
|
118
117
|
itemWrapperStyle: style
|
|
119
118
|
};
|
|
119
|
+
if (withDragAndDrop) return /*#__PURE__*/jsx(DragAndDropTreeItem, _objectSpread(_objectSpread({}, listItemProps), {}, {
|
|
120
|
+
onMouseDragOverItem: onMouseDragOverItem
|
|
121
|
+
}));
|
|
120
122
|
return /*#__PURE__*/jsx(TreeItem, _objectSpread({}, listItemProps));
|
|
121
123
|
})) : null]
|
|
122
124
|
});
|
|
123
125
|
};
|
|
126
|
+
const DragAndDropTreeList = SortableContainer(TreeList);
|
|
124
127
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
export { TreeList, TreeListNoItems, TreeListWrapper, treeListNoItemsBn };
|
|
128
|
+
export { DragAndDropTreeList, TreeList, TreeListNoItems, TreeListWrapper, treeListNoItemsBn };
|
|
@@ -1,3 +1,35 @@
|
|
|
1
|
+
import _jsx from '@babel/runtime/helpers/esm/jsx';
|
|
2
|
+
import { useContext } from 'react';
|
|
3
|
+
import { DragAndDropTreeList } from '../../parts/TreeList.js';
|
|
4
|
+
import { useDnDHelpers } from '../../utils/useDnDHelpers.js';
|
|
5
|
+
import { keyCodes } from '../../utils/dnd-helpers.js';
|
|
6
|
+
import TreeViewContext from '../../TreeViewContext.js';
|
|
7
|
+
|
|
8
|
+
const TreeDnD = () => {
|
|
9
|
+
const {
|
|
10
|
+
updateBeforeSortStart,
|
|
11
|
+
onSortStart,
|
|
12
|
+
onMouseDragOverItem,
|
|
13
|
+
onSortEnd
|
|
14
|
+
} = useDnDHelpers();
|
|
15
|
+
const ctx = useContext(TreeViewContext);
|
|
16
|
+
const {
|
|
17
|
+
virtualListRef
|
|
18
|
+
} = ctx;
|
|
19
|
+
return /*#__PURE__*/_jsx(DragAndDropTreeList, {
|
|
20
|
+
useDragHandle: true,
|
|
21
|
+
transitionDuration: 0,
|
|
22
|
+
keyboardSortingTransitionDuration: 0,
|
|
23
|
+
hideSortableGhost: false,
|
|
24
|
+
disableAnimation: true,
|
|
25
|
+
updateBeforeSortStart: updateBeforeSortStart,
|
|
26
|
+
onSortStart: onSortStart,
|
|
27
|
+
onMouseDragOverItem: onMouseDragOverItem,
|
|
28
|
+
onSortEnd: onSortEnd,
|
|
29
|
+
getContainer: () => virtualListRef === null || virtualListRef === void 0 ? void 0 : virtualListRef.current,
|
|
30
|
+
keyCodes: keyCodes
|
|
31
|
+
});
|
|
32
|
+
};
|
|
1
33
|
const TreeDndPlugin = 'tree-dnd';
|
|
2
34
|
|
|
3
|
-
export { TreeDndPlugin };
|
|
35
|
+
export { TreeDnD, TreeDndPlugin };
|
package/esm/plugins/dnd/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { TreeDndPlugin } from './TreeDndPlugin.js';
|
|
1
|
+
export { TreeDnD, TreeDndPlugin } from './TreeDndPlugin.js';
|
package/esm/plugins/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { TreeDndPlugin } from './dnd/TreeDndPlugin.js';
|
|
1
|
+
export { TreeDnD, TreeDndPlugin } from './dnd/TreeDndPlugin.js';
|
|
2
2
|
export { TreeRovingPlugin } from './roving/TreeRovingPlugin.js';
|
|
3
3
|
export { SelectablePluginTree } from './selectable/SelectablePluginTree.js';
|
|
4
4
|
export { TreeToolbarPlugin } from './toolbar/TreeToolbarPlugin.js';
|
|
@@ -26,7 +26,6 @@ const TreeViewPropTypes = {
|
|
|
26
26
|
isMultiSelect: PropTypes.bool.description('whether the tree view is multi select or not').defaultValue(false),
|
|
27
27
|
isSingleSelect: PropTypes.bool.description('wheter the tree view is single select or not').defaultValue(false),
|
|
28
28
|
isLoading: PropTypes.bool.description('wheter the tree should show the loading state or not').defaultValue(false),
|
|
29
|
-
getIsDropValid: PropTypes.func.description("Function that returns true if the drop is valid and false if it's not").defaultValue('() => true'),
|
|
30
29
|
restrictDragAndDrop: PropTypes.bool.description('whether to restrict DnD functionality or not').defaultValue(false),
|
|
31
30
|
showChildrenAmount: PropTypes.bool.description('whether to show the amount of children or not').defaultValue(false),
|
|
32
31
|
sortable: PropTypes.bool.description('whether the sortable functionality is active or not').defaultValue(false),
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { getItemsParentNode, itemsShareSameParent } from './tree-helpers.js';
|
|
2
|
+
|
|
3
|
+
/* eslint-disable max-lines */
|
|
4
|
+
const keyCodes = {
|
|
5
|
+
lift: [32, 13],
|
|
6
|
+
drop: [32, 13],
|
|
7
|
+
cancel: [27],
|
|
8
|
+
up: [38, 37],
|
|
9
|
+
down: [40, 39]
|
|
10
|
+
};
|
|
11
|
+
const sameParentAsHoverItemDrop = _ref => {
|
|
12
|
+
let {
|
|
13
|
+
isBefore,
|
|
14
|
+
isAfter,
|
|
15
|
+
draggedItem,
|
|
16
|
+
overItem,
|
|
17
|
+
movementDirection
|
|
18
|
+
} = _ref;
|
|
19
|
+
const hoveredItemNodeNestedIndex = overItem.node.getIndex();
|
|
20
|
+
let newParentNode = getItemsParentNode([overItem], true);
|
|
21
|
+
const {
|
|
22
|
+
treeDepth: draggedItemTreeDepth
|
|
23
|
+
} = draggedItem;
|
|
24
|
+
const {
|
|
25
|
+
treeDepth: overItemTreeDepth
|
|
26
|
+
} = overItem;
|
|
27
|
+
const directionOffset = // we don't apply the offset for direction
|
|
28
|
+
// if moving from less nested to more nested
|
|
29
|
+
// or moving bottom to top
|
|
30
|
+
// the offset is required when moving elemnts down in their nest level
|
|
31
|
+
draggedItemTreeDepth <= overItemTreeDepth && movementDirection === 'topToBottom' ? -1 : 0;
|
|
32
|
+
let newNestedIndex;
|
|
33
|
+
if (isBefore) newNestedIndex = hoveredItemNodeNestedIndex + directionOffset;else if (isAfter) newNestedIndex = hoveredItemNodeNestedIndex + 1 + directionOffset;else {
|
|
34
|
+
newNestedIndex = overItem.children.length; // append as last child of the group
|
|
35
|
+
|
|
36
|
+
newParentNode = overItem.node;
|
|
37
|
+
}
|
|
38
|
+
if (newNestedIndex < 0) newNestedIndex = 0;
|
|
39
|
+
return {
|
|
40
|
+
newNestedIndex,
|
|
41
|
+
newParentNode
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
const inferHoverItemKinshipDrop = _ref2 => {
|
|
45
|
+
let {
|
|
46
|
+
isBefore,
|
|
47
|
+
isAfter,
|
|
48
|
+
draggedItem,
|
|
49
|
+
overItem,
|
|
50
|
+
movementDirection
|
|
51
|
+
} = _ref2;
|
|
52
|
+
const hoverItemModel = overItem.node.model;
|
|
53
|
+
const isGroupAndIsExpanded = hoverItemModel.isExpanded && hoverItemModel.children.length > 0;
|
|
54
|
+
|
|
55
|
+
if (isAfter && isGroupAndIsExpanded) {
|
|
56
|
+
return {
|
|
57
|
+
newNestedIndex: 0,
|
|
58
|
+
// append after the group, so basically as first child
|
|
59
|
+
newParentNode: overItem.node
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (isBefore || isAfter && !isGroupAndIsExpanded) {
|
|
64
|
+
return sameParentAsHoverItemDrop({
|
|
65
|
+
isBefore,
|
|
66
|
+
isAfter,
|
|
67
|
+
draggedItem,
|
|
68
|
+
overItem,
|
|
69
|
+
movementDirection
|
|
70
|
+
});
|
|
71
|
+
} // drop inside
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
newNestedIndex: overItem.children.length,
|
|
76
|
+
// append as last child of the group
|
|
77
|
+
newParentNode: overItem.node
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
const isDropValid = _ref3 => {
|
|
81
|
+
let {
|
|
82
|
+
isBefore,
|
|
83
|
+
isAfter,
|
|
84
|
+
draggedItem,
|
|
85
|
+
overItem,
|
|
86
|
+
restrictDragAndDrop
|
|
87
|
+
} = _ref3;
|
|
88
|
+
const {
|
|
89
|
+
id: movedId
|
|
90
|
+
} = draggedItem;
|
|
91
|
+
const {
|
|
92
|
+
id: hoverId
|
|
93
|
+
} = overItem;
|
|
94
|
+
const isInside = !isBefore && !isAfter;
|
|
95
|
+
if (movedId === hoverId) return false;
|
|
96
|
+
|
|
97
|
+
if (isInside && !overItem.isGroup) {
|
|
98
|
+
return false; // we can't drop inside an item that isn't a group
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (restrictDragAndDrop) {
|
|
102
|
+
if (isInside) return false; // in this mode we can't drop inside another element
|
|
103
|
+
|
|
104
|
+
const sameParent = itemsShareSameParent([draggedItem, overItem]);
|
|
105
|
+
if (overItem.node.model.isExpanded && isAfter) return false;
|
|
106
|
+
if (!sameParent) return false;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return true;
|
|
110
|
+
};
|
|
111
|
+
const inferKeyboardKinshipDrop = _ref4 => {
|
|
112
|
+
let {
|
|
113
|
+
items,
|
|
114
|
+
newIndex
|
|
115
|
+
} = _ref4;
|
|
116
|
+
const kinship = {};
|
|
117
|
+
const itemBefore = items[newIndex];
|
|
118
|
+
const itemBeforeModel = itemBefore.node.model;
|
|
119
|
+
const isGroupAndIsExpanded = itemBeforeModel.isExpanded && itemBeforeModel.children.length > 0;
|
|
120
|
+
|
|
121
|
+
if (isGroupAndIsExpanded) {
|
|
122
|
+
kinship.newParentNode = itemBefore.node;
|
|
123
|
+
kinship.newNestedIndex = 0;
|
|
124
|
+
} else {
|
|
125
|
+
kinship.newParentNode = getItemsParentNode([itemBefore], true);
|
|
126
|
+
kinship.newNestedIndex = itemBefore.node.getIndex();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return kinship;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export { inferHoverItemKinshipDrop, inferKeyboardKinshipDrop, isDropValid, keyCodes, sameParentAsHoverItemDrop };
|
|
@@ -15,13 +15,14 @@ import { toggleItemExpand, toggleExpandAllHelper } from './group-expands-helpers
|
|
|
15
15
|
// 36 home
|
|
16
16
|
// 56 number 8
|
|
17
17
|
// 106 (NumPad) *
|
|
18
|
-
const useOnItemKeyDown =
|
|
18
|
+
const useOnItemKeyDown = item => {
|
|
19
19
|
const ctx = useContext(TreeViewContext);
|
|
20
20
|
const {
|
|
21
21
|
props: {
|
|
22
22
|
onItemFocus
|
|
23
23
|
},
|
|
24
24
|
triggerTreeRerender,
|
|
25
|
+
isDragging,
|
|
25
26
|
visibleItems,
|
|
26
27
|
setFocusedItem,
|
|
27
28
|
setSelectedItem,
|
|
@@ -50,12 +51,10 @@ const useOnItemKeyDown = (item, draggableProps) => {
|
|
|
50
51
|
keyCode
|
|
51
52
|
} = e;
|
|
52
53
|
|
|
53
|
-
if (!
|
|
54
|
+
if (!isDragging) {
|
|
54
55
|
if (keyCode !== 9) e.preventDefault(); // allow tab/shift+tab
|
|
55
56
|
|
|
56
|
-
if (
|
|
57
|
-
setSelectedItem(item);
|
|
58
|
-
}
|
|
57
|
+
if (keyCode === 13 || keyCode === 32) setSelectedItem(item);
|
|
59
58
|
|
|
60
59
|
if (keyCode === 37) {
|
|
61
60
|
if (isGroup && isExpanded) {
|
|
@@ -143,7 +142,7 @@ const useOnItemKeyDown = (item, draggableProps) => {
|
|
|
143
142
|
}
|
|
144
143
|
}
|
|
145
144
|
}
|
|
146
|
-
}, [
|
|
145
|
+
}, [isDragging, setSelectedItem, item, isGroup, isExpanded, triggerTreeRerender, scrollToIndex, setLatestToggledItem, visibleItems, setFocusedItem, onItemFocusCurry, updateUserExpandedState]);
|
|
147
146
|
return onItemKeyDown;
|
|
148
147
|
};
|
|
149
148
|
const useGlobalKeyboardListener = func => {
|
|
@@ -128,7 +128,7 @@ const getChildrenMatchesSearchQuery = function (parentNode) {
|
|
|
128
128
|
*/
|
|
129
129
|
|
|
130
130
|
const enrichNodeModelInPlace = function (node) {
|
|
131
|
-
var _node$model
|
|
131
|
+
var _node$model, _node$model$children;
|
|
132
132
|
|
|
133
133
|
let searchQuery = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
|
|
134
134
|
let selection = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
|
|
@@ -144,7 +144,7 @@ const enrichNodeModelInPlace = function (node) {
|
|
|
144
144
|
// this is useful to avoid having to always check both the flag and the children.length...
|
|
145
145
|
|
|
146
146
|
|
|
147
|
-
if (typeof node.model.isGroup !== 'boolean' && ((_node$model
|
|
147
|
+
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) {
|
|
148
148
|
node.model.isGroup = true;
|
|
149
149
|
}
|
|
150
150
|
|
|
@@ -176,30 +176,5 @@ const focusItem = item => {
|
|
|
176
176
|
|
|
177
177
|
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();
|
|
178
178
|
};
|
|
179
|
-
const flattenTreeForDnD = function (data) {
|
|
180
|
-
let shouldDescend = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : () => true;
|
|
181
|
-
const flattenedTree = [];
|
|
182
|
-
let index = 0;
|
|
183
|
-
|
|
184
|
-
const doit = function (node) {
|
|
185
|
-
var _node$children$length, _node$children, _node$children2;
|
|
186
|
-
|
|
187
|
-
let depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
188
|
-
let parentId = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
|
|
189
|
-
flattenedTree.push({
|
|
190
|
-
uid: node.id.toString(),
|
|
191
|
-
depth,
|
|
192
|
-
realIndex: index,
|
|
193
|
-
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,
|
|
194
|
-
parentId: parentId !== null ? parentId.toString() : null,
|
|
195
|
-
original: node
|
|
196
|
-
});
|
|
197
|
-
index += 1;
|
|
198
|
-
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()));
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
data.forEach(datum => doit(datum));
|
|
202
|
-
return flattenedTree;
|
|
203
|
-
};
|
|
204
179
|
|
|
205
|
-
export { cloneNode, enrichNodeModelInPlace,
|
|
180
|
+
export { cloneNode, enrichNodeModelInPlace, focusItem, getChildrenMatchesSearchQuery, getItemsParentNode, getNodeById, getNodeMatchesSearchQuery, itemsShareSameParent, walkAllNodeChildren, walkParents, walkVisibles };
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import 'core-js/modules/esnext.async-iterator.constructor.js';
|
|
2
|
+
import 'core-js/modules/esnext.async-iterator.drop.js';
|
|
3
|
+
import 'core-js/modules/esnext.iterator.constructor.js';
|
|
4
|
+
import 'core-js/modules/esnext.iterator.drop.js';
|
|
5
|
+
import 'core-js/modules/web.dom-collections.iterator.js';
|
|
6
|
+
import { useContext, useCallback } from 'react';
|
|
7
|
+
import { cloneDeep } from 'lodash';
|
|
8
|
+
import TreeViewContext, { defaultRelativeMouseCoord } from '../TreeViewContext.js';
|
|
9
|
+
import { getNodeById, focusItem } from './tree-helpers.js';
|
|
10
|
+
import { inferKeyboardKinshipDrop, inferHoverItemKinshipDrop, isDropValid } from './dnd-helpers.js';
|
|
11
|
+
|
|
12
|
+
const useDnDHelpers = () => {
|
|
13
|
+
const ctx = useContext(TreeViewContext);
|
|
14
|
+
const {
|
|
15
|
+
props: {
|
|
16
|
+
data,
|
|
17
|
+
onOrderChange,
|
|
18
|
+
restrictDragAndDrop
|
|
19
|
+
},
|
|
20
|
+
relativeMouseCord,
|
|
21
|
+
setRelativeMouseCord,
|
|
22
|
+
visibleItems,
|
|
23
|
+
isDragging,
|
|
24
|
+
setIsDragging,
|
|
25
|
+
isKeyboardDragging,
|
|
26
|
+
setIsKeyboardDragging,
|
|
27
|
+
setDraggedItem,
|
|
28
|
+
setFocusedItem,
|
|
29
|
+
triggerTreeRerender,
|
|
30
|
+
tree,
|
|
31
|
+
treeRoot,
|
|
32
|
+
draggedItem
|
|
33
|
+
} = ctx;
|
|
34
|
+
const onSortEnd = useCallback(_ref => {
|
|
35
|
+
let {
|
|
36
|
+
oldIndex,
|
|
37
|
+
newIndex,
|
|
38
|
+
isKeySorting
|
|
39
|
+
} = _ref;
|
|
40
|
+
const {
|
|
41
|
+
isBefore,
|
|
42
|
+
isAfter,
|
|
43
|
+
item: overItem,
|
|
44
|
+
isValid
|
|
45
|
+
} = relativeMouseCord;
|
|
46
|
+
|
|
47
|
+
if (draggedItem && tree && treeRoot && overItem) {
|
|
48
|
+
let newItems = cloneDeep(data); // if dropping in same position do nothing
|
|
49
|
+
// if the drop is not allowed do nothing
|
|
50
|
+
|
|
51
|
+
if (oldIndex === newIndex || !isValid) {
|
|
52
|
+
// onOrderChange(newItems, data);
|
|
53
|
+
setIsDragging(false);
|
|
54
|
+
setDraggedItem(null);
|
|
55
|
+
triggerTreeRerender();
|
|
56
|
+
return;
|
|
57
|
+
} // if we are moving from topToBottom the offset changes
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
const movementDirection = newIndex > oldIndex ? 'topToBottom' : 'bottomToTop';
|
|
61
|
+
let newNestedIndex;
|
|
62
|
+
let newParentNode;
|
|
63
|
+
|
|
64
|
+
if (isKeySorting) {
|
|
65
|
+
const kinship = inferKeyboardKinshipDrop({
|
|
66
|
+
items: visibleItems,
|
|
67
|
+
newIndex
|
|
68
|
+
});
|
|
69
|
+
newNestedIndex = kinship.newNestedIndex;
|
|
70
|
+
newParentNode = kinship.newParentNode;
|
|
71
|
+
} else {
|
|
72
|
+
const kinship = inferHoverItemKinshipDrop({
|
|
73
|
+
isBefore,
|
|
74
|
+
isAfter,
|
|
75
|
+
overItem,
|
|
76
|
+
movementDirection,
|
|
77
|
+
draggedItem
|
|
78
|
+
});
|
|
79
|
+
newNestedIndex = kinship.newNestedIndex;
|
|
80
|
+
newParentNode = kinship.newParentNode;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const newMovedItemNode = tree.parse(draggedItem.node.model);
|
|
84
|
+
getNodeById(treeRoot, draggedItem.id).drop();
|
|
85
|
+
|
|
86
|
+
if (newParentNode && typeof newNestedIndex === 'number') {
|
|
87
|
+
newParentNode.addChildAtIndex(newMovedItemNode, newNestedIndex);
|
|
88
|
+
|
|
89
|
+
if (newParentNode.isRoot()) {
|
|
90
|
+
newItems = [...newParentNode.model.children];
|
|
91
|
+
} else {
|
|
92
|
+
newItems = [...treeRoot.model.children];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
onOrderChange(newItems, data, draggedItem);
|
|
97
|
+
setIsDragging(false);
|
|
98
|
+
setDraggedItem(null);
|
|
99
|
+
triggerTreeRerender();
|
|
100
|
+
}
|
|
101
|
+
}, [data, draggedItem, relativeMouseCord, visibleItems, tree, treeRoot, onOrderChange, triggerTreeRerender, setIsDragging, setDraggedItem]);
|
|
102
|
+
const onSortStart = useCallback(() => {}, // ({ node, index, collection, isKeySorting }, event) => {
|
|
103
|
+
// console.log('node :>> ', node);
|
|
104
|
+
// console.log('isKeySorting :>> ', isKeySorting);
|
|
105
|
+
// console.log('event :>> ', event);
|
|
106
|
+
// const item = collection?.[index] || visibleItems[index];
|
|
107
|
+
// setIsDragging(true);
|
|
108
|
+
// setDraggedItem(item);
|
|
109
|
+
// },
|
|
110
|
+
[]);
|
|
111
|
+
const updateBeforeSortStart = useCallback(beforeStartArgs => new Promise(resolve => {
|
|
112
|
+
const {
|
|
113
|
+
index,
|
|
114
|
+
isKeySorting
|
|
115
|
+
} = beforeStartArgs;
|
|
116
|
+
const item = visibleItems[index];
|
|
117
|
+
const {
|
|
118
|
+
isExpanded
|
|
119
|
+
} = item.node.model;
|
|
120
|
+
|
|
121
|
+
if (isExpanded) {
|
|
122
|
+
item.node.model.isExpanded = false;
|
|
123
|
+
triggerTreeRerender();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
setRelativeMouseCord(defaultRelativeMouseCoord);
|
|
127
|
+
setIsKeyboardDragging(isKeySorting);
|
|
128
|
+
setIsDragging(true);
|
|
129
|
+
setDraggedItem(item);
|
|
130
|
+
focusItem(null);
|
|
131
|
+
setFocusedItem(null);
|
|
132
|
+
if (document.activeElement && typeof document.activeElement.blur === 'function') document.activeElement.blur();
|
|
133
|
+
resolve(true);
|
|
134
|
+
}), [visibleItems, setRelativeMouseCord, setIsKeyboardDragging, setIsDragging, setDraggedItem, setFocusedItem, triggerTreeRerender]); // tracks mouse coords relative to an element to check if the drop happens
|
|
135
|
+
// at the top/center/bottom of an element to choose where to move the element
|
|
136
|
+
// requested in the specs
|
|
137
|
+
|
|
138
|
+
const onMouseDragOverItem = useCallback(_ref2 => {
|
|
139
|
+
let {
|
|
140
|
+
event,
|
|
141
|
+
item,
|
|
142
|
+
itemIndex,
|
|
143
|
+
setIsDraggingOverThis,
|
|
144
|
+
openFolderOnHoverTimeout
|
|
145
|
+
} = _ref2;
|
|
146
|
+
const {
|
|
147
|
+
node
|
|
148
|
+
} = item;
|
|
149
|
+
const {
|
|
150
|
+
nodeItemRef
|
|
151
|
+
} = node.model;
|
|
152
|
+
|
|
153
|
+
if (isDragging && !isKeyboardDragging && draggedItem) {
|
|
154
|
+
var _nodeItemRef$current;
|
|
155
|
+
|
|
156
|
+
const rect = nodeItemRef === null || nodeItemRef === void 0 ? void 0 : (_nodeItemRef$current = nodeItemRef.current) === null || _nodeItemRef$current === void 0 ? void 0 : _nodeItemRef$current.getBoundingClientRect();
|
|
157
|
+
|
|
158
|
+
if (rect) {
|
|
159
|
+
const x = event.clientX - rect.left; // x position within the element.
|
|
160
|
+
|
|
161
|
+
const y = event.clientY - rect.top; // y position within the element.
|
|
162
|
+
// by spec we need to track where the drop will happens
|
|
163
|
+
// if drop happens on top 25% drop before
|
|
164
|
+
// if drop happens on bottom of 25% drop after
|
|
165
|
+
// if drop happens in the middle 50% either invalid or drop into children
|
|
166
|
+
|
|
167
|
+
const {
|
|
168
|
+
height: elementHeight
|
|
169
|
+
} = rect;
|
|
170
|
+
const dropBeforeMaxY = elementHeight * 0.25;
|
|
171
|
+
const dropAfterMinY = elementHeight - elementHeight * 0.25;
|
|
172
|
+
const isBefore = y < dropBeforeMaxY;
|
|
173
|
+
const isAfter = y > dropAfterMinY;
|
|
174
|
+
const isValid = isDropValid({
|
|
175
|
+
isBefore,
|
|
176
|
+
isAfter,
|
|
177
|
+
draggedItem,
|
|
178
|
+
overItem: item,
|
|
179
|
+
restrictDragAndDrop
|
|
180
|
+
});
|
|
181
|
+
setRelativeMouseCord({
|
|
182
|
+
isBefore,
|
|
183
|
+
isAfter,
|
|
184
|
+
isValid,
|
|
185
|
+
item,
|
|
186
|
+
x,
|
|
187
|
+
y,
|
|
188
|
+
relativeElementDOMRect: rect,
|
|
189
|
+
relativeElementIndex: itemIndex
|
|
190
|
+
});
|
|
191
|
+
setIsDraggingOverThis(true);
|
|
192
|
+
|
|
193
|
+
if (!(openFolderOnHoverTimeout !== null && openFolderOnHoverTimeout !== void 0 && openFolderOnHoverTimeout.current) && // only set the interval if it wasn't set already once
|
|
194
|
+
(draggedItem === null || draggedItem === void 0 ? void 0 : draggedItem.id) !== (item === null || item === void 0 ? void 0 : item.id) && ( // don't open ghost element
|
|
195
|
+
item.isGroup || item.children.length > 0) && // only open groups
|
|
196
|
+
!restrictDragAndDrop) {
|
|
197
|
+
openFolderOnHoverTimeout.current = setTimeout(() => {
|
|
198
|
+
item.node.model.isExpanded = true;
|
|
199
|
+
openFolderOnHoverTimeout.current = null;
|
|
200
|
+
triggerTreeRerender();
|
|
201
|
+
}, 1000);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
} else {
|
|
205
|
+
setRelativeMouseCord(defaultRelativeMouseCoord);
|
|
206
|
+
}
|
|
207
|
+
}, [isDragging, isKeyboardDragging, draggedItem, restrictDragAndDrop, setRelativeMouseCord, triggerTreeRerender]);
|
|
208
|
+
return {
|
|
209
|
+
updateBeforeSortStart,
|
|
210
|
+
onSortStart,
|
|
211
|
+
onSortEnd,
|
|
212
|
+
onMouseDragOverItem
|
|
213
|
+
};
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
export { useDnDHelpers };
|
package/esm/utils/useTree.js
CHANGED
|
@@ -8,7 +8,7 @@ import 'core-js/modules/web.dom-collections.iterator.js';
|
|
|
8
8
|
import { useMemo, useState, useEffect, useCallback } from 'react';
|
|
9
9
|
import TreeModel from 'tree-model';
|
|
10
10
|
import { cloneDeep } from 'lodash';
|
|
11
|
-
import { enrichNodeModelInPlace, walkVisibles
|
|
11
|
+
import { enrichNodeModelInPlace, walkVisibles } from './tree-helpers.js';
|
|
12
12
|
import { updateExpandedState } from './group-expands-helpers.js';
|
|
13
13
|
|
|
14
14
|
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; }
|
|
@@ -67,9 +67,9 @@ const useTree = (data, props, states) => {
|
|
|
67
67
|
useEffect(() => {
|
|
68
68
|
const parsedVisibleItems = [];
|
|
69
69
|
const newExpandedHashMap = {};
|
|
70
|
-
const shouldContinueWalking = true; // tree-model ask we return true if we want to continue walking
|
|
71
|
-
|
|
72
70
|
treeRoot.walk(node => {
|
|
71
|
+
const shouldContinueWalking = true; // tree-model ask we return true if we want to continue walking
|
|
72
|
+
|
|
73
73
|
enrichNodeModelInPlace(node, searchQuery, selection, expanded);
|
|
74
74
|
if (node.model.id && node.model.isGroup && node.model.isExpanded) newExpandedHashMap[node.model.id] = true;
|
|
75
75
|
return shouldContinueWalking;
|
|
@@ -84,10 +84,8 @@ const useTree = (data, props, states) => {
|
|
|
84
84
|
setExpandedGroups(newExpandedHashMap); // eslint-disable-next-line react-hooks/exhaustive-deps
|
|
85
85
|
}, [treeRoot, rerenderItems, searchQuery, highlightOnlyQuery, selection, expanded, setExpandedGroups // onVisibleItemsChange // evaluate potential performance hit?
|
|
86
86
|
]);
|
|
87
|
-
const flattenedItems = useMemo(() => flattenTreeForDnD(data), [data]);
|
|
88
87
|
return {
|
|
89
88
|
visibleItems,
|
|
90
|
-
flattenedItems,
|
|
91
89
|
tree,
|
|
92
90
|
treeRoot,
|
|
93
91
|
rerenderItems,
|