@onehat/ui 0.2.76 → 0.2.77
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/package.json +1 -1
- package/src/Components/Grid/Grid.js +6 -7
- package/src/Components/Hoc/withDraggable.js +8 -3
- package/src/Components/Hoc/withEditor.js +1 -1
- package/src/Components/Tree/Tree.js +282 -257
- package/src/Components/Tree/TreeNode.js +12 -18
- package/src/Constants/Directions.js +1 -0
- package/src/Constants/Styles.js +4 -0
- package/src/Constants/Tree.js +4 -1
- package/src/Constants/UiModes.js +3 -3
package/package.json
CHANGED
|
@@ -223,7 +223,6 @@ function GridComponent(props) {
|
|
|
223
223
|
if (canRowsReorder) {
|
|
224
224
|
items.unshift(<IconButton
|
|
225
225
|
key="reorderBtn"
|
|
226
|
-
{...iconButtonProps}
|
|
227
226
|
onPress={() => setIsReorderMode(!isReorderMode)}
|
|
228
227
|
icon={<Icon as={isReorderMode ? NoReorderRows : ReorderRows} color={styles.GRID_TOOLBAR_ITEMS_COLOR} />}
|
|
229
228
|
/>);
|
|
@@ -388,7 +387,7 @@ function GridComponent(props) {
|
|
|
388
387
|
row = node.parentElement.parentElement,
|
|
389
388
|
parent = row.parentElement,
|
|
390
389
|
parentRect = parent.getBoundingClientRect(),
|
|
391
|
-
rows = _.filter(
|
|
390
|
+
rows = _.filter(parent.children, (childNode) => {
|
|
392
391
|
return childNode.getBoundingClientRect().height !== 0; // Skip zero-height children
|
|
393
392
|
}),
|
|
394
393
|
currentY = proxyRect.top - parentRect.top, // top position of pointer, relative to page
|
|
@@ -473,7 +472,7 @@ function GridComponent(props) {
|
|
|
473
472
|
row = node.parentElement.parentElement,
|
|
474
473
|
parent = row.parentElement,
|
|
475
474
|
parentRect = parent.getBoundingClientRect(),
|
|
476
|
-
rows = _.filter(
|
|
475
|
+
rows = _.filter(parent.children, (childNode) => {
|
|
477
476
|
return childNode.getBoundingClientRect().height !== 0; // Skip zero-height children
|
|
478
477
|
}),
|
|
479
478
|
currentY = proxyRect.top - parentRect.top, // top position of pointer, relative to page
|
|
@@ -537,7 +536,7 @@ function GridComponent(props) {
|
|
|
537
536
|
const
|
|
538
537
|
rowContainerRect = rows[newIx].getBoundingClientRect(),
|
|
539
538
|
top = (useBottom ? rowContainerRect.bottom : rowContainerRect.top) - parentRect.top - parseInt(parent.style.borderWidth); // get relative Y position
|
|
540
|
-
let marker = dragRowSlot
|
|
539
|
+
let marker = dragRowSlot?.marker;
|
|
541
540
|
if (marker) {
|
|
542
541
|
marker.style.top = top -4 + 'px'; // -4 so it's always visible
|
|
543
542
|
}
|
|
@@ -782,9 +781,9 @@ function GridComponent(props) {
|
|
|
782
781
|
nestedScrollEnabled={true}
|
|
783
782
|
contentContainerStyle={{
|
|
784
783
|
overflow: 'auto',
|
|
785
|
-
borderWidth: isReorderMode ?
|
|
786
|
-
borderColor: isReorderMode ?
|
|
787
|
-
borderStyle:
|
|
784
|
+
borderWidth: isReorderMode ? styles.REORDER_BORDER_WIDTH : 0,
|
|
785
|
+
borderColor: isReorderMode ? styles.REORDER_BORDER_COLOR : null,
|
|
786
|
+
borderStyle: styles.REORDER_BORDER_STYLE,
|
|
788
787
|
flex: 1,
|
|
789
788
|
}}
|
|
790
789
|
refreshing={isLoading}
|
|
@@ -29,11 +29,13 @@ export default function withDraggable(WrappedComponent) {
|
|
|
29
29
|
onDrag,
|
|
30
30
|
onDragStop,
|
|
31
31
|
onChangeIsDragging,
|
|
32
|
+
getDraggableNodeFromNode = (node) => node,
|
|
32
33
|
getParentNode = (node) => node.parentElement.parentElement,
|
|
33
34
|
getProxy,
|
|
34
35
|
proxyParent,
|
|
35
36
|
proxyPositionRelativeToParent = false,
|
|
36
37
|
handle,
|
|
38
|
+
draggableProps = {},
|
|
37
39
|
...propsToPass
|
|
38
40
|
} = props,
|
|
39
41
|
{
|
|
@@ -57,7 +59,7 @@ export default function withDraggable(WrappedComponent) {
|
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
const
|
|
60
|
-
node = info.node,
|
|
62
|
+
node = getDraggableNodeFromNode(info.node),
|
|
61
63
|
parentContainer = getParentNode && getParentNode(node);
|
|
62
64
|
|
|
63
65
|
setNode(node);
|
|
@@ -226,8 +228,9 @@ export default function withDraggable(WrappedComponent) {
|
|
|
226
228
|
onStop={handleStop}
|
|
227
229
|
position={{ x: 0, y: 0, /* reset to dropped position */ }}
|
|
228
230
|
// bounds={bounds}
|
|
231
|
+
{...draggableProps}
|
|
229
232
|
>
|
|
230
|
-
<div className="nsResize">
|
|
233
|
+
<div className="nsResize" style={{ width: '100%', }}>
|
|
231
234
|
<WrappedComponent {...propsToPass} />
|
|
232
235
|
</div>
|
|
233
236
|
</Draggable>;
|
|
@@ -239,6 +242,7 @@ export default function withDraggable(WrappedComponent) {
|
|
|
239
242
|
onStop={handleStop}
|
|
240
243
|
position={{ x: 0, y: 0, /* reset to dropped position */ }}
|
|
241
244
|
// bounds={bounds}
|
|
245
|
+
{...draggableProps}
|
|
242
246
|
>
|
|
243
247
|
<div className="ewResize" style={{ height: '100%', }}>
|
|
244
248
|
<WrappedComponent {...propsToPass} />
|
|
@@ -252,8 +256,9 @@ export default function withDraggable(WrappedComponent) {
|
|
|
252
256
|
onStart={handleStart}
|
|
253
257
|
onDrag={handleDrag}
|
|
254
258
|
onStop={handleStop}
|
|
255
|
-
|
|
259
|
+
position={{ x: 0, y: 0, /* reset to dropped position */ }}
|
|
256
260
|
handle={handle}
|
|
261
|
+
{...draggableProps}
|
|
257
262
|
>
|
|
258
263
|
<WrappedComponent {...propsToPass} />
|
|
259
264
|
</Draggable>;
|
|
@@ -165,7 +165,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
165
165
|
await getListeners().onBeforeDeleteSave(selection);
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
await Repository.delete(selection);
|
|
168
|
+
await Repository.delete(selection, moveSubtreeUp);
|
|
169
169
|
if (!Repository.isAutoSave) {
|
|
170
170
|
await Repository.save();
|
|
171
171
|
}
|
|
@@ -16,8 +16,9 @@ import {
|
|
|
16
16
|
VERTICAL,
|
|
17
17
|
} from '../../Constants/Directions.js';
|
|
18
18
|
import {
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
COLLAPSED,
|
|
20
|
+
EXPANDED,
|
|
21
|
+
LEAF,
|
|
21
22
|
} from '../../Constants/Tree.js';
|
|
22
23
|
import * as colourMixer from '@k-renwick/colour-mixer'
|
|
23
24
|
import UiGlobals from '../../UiGlobals.js';
|
|
@@ -33,12 +34,12 @@ import withMultiSelection from '../Hoc/withMultiSelection.js';
|
|
|
33
34
|
import withSelection from '../Hoc/withSelection.js';
|
|
34
35
|
import withWindowedEditor from '../Hoc/withWindowedEditor.js';
|
|
35
36
|
import getIconButtonFromConfig from '../../Functions/getIconButtonFromConfig.js';
|
|
37
|
+
import inArray from '../../Functions/inArray.js';
|
|
36
38
|
import testProps from '../../Functions/testProps.js';
|
|
37
39
|
import nbToRgb from '../../Functions/nbToRgb.js';
|
|
38
|
-
import TreeNode, {
|
|
40
|
+
import TreeNode, { DraggableTreeNode } from './TreeNode.js';
|
|
39
41
|
import FormPanel from '../Panel/FormPanel.js';
|
|
40
42
|
import Input from '../Form/Field/Input.js';
|
|
41
|
-
import IconButton from '../Buttons/IconButton.js';
|
|
42
43
|
import Dot from '../Icons/Dot.js';
|
|
43
44
|
import Collapse from '../Icons/Collapse.js';
|
|
44
45
|
import FolderClosed from '../Icons/FolderClosed.js';
|
|
@@ -51,6 +52,7 @@ import NoRecordsFound from '../Grid/NoRecordsFound.js';
|
|
|
51
52
|
import Toolbar from '../Toolbar/Toolbar.js';
|
|
52
53
|
import _ from 'lodash';
|
|
53
54
|
|
|
55
|
+
const DEPTH_INDENT_PX = 20;
|
|
54
56
|
|
|
55
57
|
function TreeComponent(props) {
|
|
56
58
|
const {
|
|
@@ -62,17 +64,19 @@ function TreeComponent(props) {
|
|
|
62
64
|
}
|
|
63
65
|
return item[displayIx];
|
|
64
66
|
},
|
|
65
|
-
getNodeIcon = (
|
|
67
|
+
getNodeIcon = (which, item) => { // decides what icon to show for this node
|
|
66
68
|
// TODO: Allow for dynamic props on the icon (e.g. special color for some icons)
|
|
67
69
|
let icon;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
icon = FolderOpen;
|
|
71
|
-
} else {
|
|
70
|
+
switch(which) {
|
|
71
|
+
case COLLAPSED:
|
|
72
72
|
icon = FolderClosed;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
break;
|
|
74
|
+
case EXPANDED:
|
|
75
|
+
icon = FolderOpen;
|
|
76
|
+
break;
|
|
77
|
+
case LEAF:
|
|
78
|
+
icon = Dot;
|
|
79
|
+
break;
|
|
76
80
|
}
|
|
77
81
|
return icon;
|
|
78
82
|
},
|
|
@@ -133,12 +137,14 @@ function TreeComponent(props) {
|
|
|
133
137
|
treeNodeData = useRef(),
|
|
134
138
|
[isReady, setIsReady] = useState(false),
|
|
135
139
|
[isLoading, setIsLoading] = useState(false),
|
|
136
|
-
[isReorderMode, setIsReorderMode] = useState(false),
|
|
137
140
|
[isSearchModalShown, setIsSearchModalShown] = useState(false),
|
|
141
|
+
[rowToDatumMap, setRowToDatumMap] = useState({}),
|
|
138
142
|
[searchResults, setSearchResults] = useState([]),
|
|
139
143
|
[searchFormData, setSearchFormData] = useState([]),
|
|
140
|
-
[
|
|
141
|
-
[
|
|
144
|
+
[highlitedDatum, setHighlitedDatum] = useState(null),
|
|
145
|
+
[isDragMode, setIsDragMode] = useState(false),
|
|
146
|
+
[dragNodeId, setDragNodeId] = useState(null),
|
|
147
|
+
[dropRowIx, setDropRowIx] = useState(null),
|
|
142
148
|
[treeSearchValue, setTreeSearchValue] = useState(''),
|
|
143
149
|
|
|
144
150
|
// state getters & setters
|
|
@@ -237,6 +243,8 @@ function TreeComponent(props) {
|
|
|
237
243
|
const entityDatum = buildTreeNodeDatum(entity);
|
|
238
244
|
parentDatum.children.unshift(entityDatum);
|
|
239
245
|
forceUpdate();
|
|
246
|
+
|
|
247
|
+
buildRowToDatumMap();
|
|
240
248
|
},
|
|
241
249
|
onBeforeEditSave = (entities) => {
|
|
242
250
|
onBeforeSave(entities);
|
|
@@ -249,8 +257,18 @@ function TreeComponent(props) {
|
|
|
249
257
|
newDatum = buildTreeNodeDatum(node);
|
|
250
258
|
|
|
251
259
|
// copy the updated data to existingDatum
|
|
252
|
-
_.
|
|
260
|
+
_.assign(existingDatum, newDatum);
|
|
253
261
|
existingDatum.isLoading = false;
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
if (node.parent?.id) {
|
|
265
|
+
const
|
|
266
|
+
existingParentDatum = getNodeData(node.parent.id),
|
|
267
|
+
newParentDatum = buildTreeNodeDatum(node.parent);
|
|
268
|
+
_.assign(existingParentDatum, newParentDatum);
|
|
269
|
+
existingParentDatum.isExpanded = true;
|
|
270
|
+
}
|
|
271
|
+
|
|
254
272
|
forceUpdate();
|
|
255
273
|
},
|
|
256
274
|
onBeforeDeleteSave = (entities) => {
|
|
@@ -265,9 +283,12 @@ function TreeComponent(props) {
|
|
|
265
283
|
forceUpdate();
|
|
266
284
|
},
|
|
267
285
|
onAfterDelete = async (entities) => {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
286
|
+
const parent = entities[0].parent;
|
|
287
|
+
if (parent) {
|
|
288
|
+
await reloadNode(parent); // includes buildRowToDatumMap
|
|
289
|
+
} else {
|
|
290
|
+
buildRowToDatumMap();
|
|
291
|
+
}
|
|
271
292
|
},
|
|
272
293
|
onToggle = (datum) => {
|
|
273
294
|
if (datum.isLoading) {
|
|
@@ -286,6 +307,8 @@ function TreeComponent(props) {
|
|
|
286
307
|
}
|
|
287
308
|
|
|
288
309
|
forceUpdate();
|
|
310
|
+
|
|
311
|
+
buildRowToDatumMap();
|
|
289
312
|
},
|
|
290
313
|
onCollapseAll = (setNewTreeNodeData = true) => {
|
|
291
314
|
// Go through whole tree and collapse all nodes
|
|
@@ -295,6 +318,7 @@ function TreeComponent(props) {
|
|
|
295
318
|
if (setNewTreeNodeData) {
|
|
296
319
|
setTreeNodeData(newTreeNodeData);
|
|
297
320
|
}
|
|
321
|
+
buildRowToDatumMap();
|
|
298
322
|
return newTreeNodeData;
|
|
299
323
|
},
|
|
300
324
|
onSearchTree = async (value) => {
|
|
@@ -323,21 +347,27 @@ function TreeComponent(props) {
|
|
|
323
347
|
},
|
|
324
348
|
|
|
325
349
|
// utilities
|
|
326
|
-
getNodeData = (
|
|
327
|
-
function findNodeById(node
|
|
350
|
+
getNodeData = (id) => {
|
|
351
|
+
function findNodeById(node) {
|
|
328
352
|
if (node.item.id === id) {
|
|
329
353
|
return node;
|
|
330
354
|
}
|
|
331
355
|
if (!_.isEmpty(node.children)) {
|
|
332
|
-
|
|
333
|
-
|
|
356
|
+
let found1 = null;
|
|
357
|
+
_.each(node.children, (node2) => {
|
|
358
|
+
const found2 = findNodeById(node2);
|
|
359
|
+
if (found2) {
|
|
360
|
+
found1 = found2;
|
|
361
|
+
return false; // break loop
|
|
362
|
+
}
|
|
334
363
|
})
|
|
364
|
+
return found1
|
|
335
365
|
}
|
|
336
366
|
return false;
|
|
337
367
|
}
|
|
338
368
|
let found = null;
|
|
339
369
|
_.each(getTreeNodeData(), (node) => {
|
|
340
|
-
const foundNode = findNodeById(node
|
|
370
|
+
const foundNode = findNodeById(node);
|
|
341
371
|
if (foundNode) {
|
|
342
372
|
found = foundNode;
|
|
343
373
|
return false;
|
|
@@ -355,9 +385,9 @@ function TreeComponent(props) {
|
|
|
355
385
|
datum = {
|
|
356
386
|
item: treeNode,
|
|
357
387
|
text: getNodeText(treeNode),
|
|
358
|
-
iconCollapsed: getNodeIcon(
|
|
359
|
-
iconExpanded: getNodeIcon(
|
|
360
|
-
iconLeaf: getNodeIcon(treeNode),
|
|
388
|
+
iconCollapsed: getNodeIcon(COLLAPSED, treeNode),
|
|
389
|
+
iconExpanded: getNodeIcon(EXPANDED, treeNode),
|
|
390
|
+
iconLeaf: getNodeIcon(LEAF, treeNode),
|
|
361
391
|
isExpanded: isRoot, // all non-root treeNodes are collapsed by default
|
|
362
392
|
isVisible: isRoot ? areRootsVisible : true,
|
|
363
393
|
isLoading: false,
|
|
@@ -385,7 +415,35 @@ function TreeComponent(props) {
|
|
|
385
415
|
nodes = assembleDataTreeNodes();
|
|
386
416
|
}
|
|
387
417
|
|
|
388
|
-
|
|
418
|
+
const treeNodeData = buildTreeNodeData(nodes);
|
|
419
|
+
setTreeNodeData(treeNodeData);
|
|
420
|
+
|
|
421
|
+
buildRowToDatumMap();
|
|
422
|
+
},
|
|
423
|
+
buildRowToDatumMap = () => {
|
|
424
|
+
const rowToDatumMap = {};
|
|
425
|
+
let ix = 0;
|
|
426
|
+
|
|
427
|
+
function walkTree(datum) {
|
|
428
|
+
if (!datum.isVisible) {
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Add this datum's id
|
|
433
|
+
rowToDatumMap[ix] = datum;
|
|
434
|
+
ix++;
|
|
435
|
+
|
|
436
|
+
if (datum.isExpanded) {
|
|
437
|
+
_.each(datum.children, (child) => {
|
|
438
|
+
walkTree(child);
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
_.each(getTreeNodeData(), (rootDatum) => {
|
|
443
|
+
walkTree(rootDatum);
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
setRowToDatumMap(rowToDatumMap);
|
|
389
447
|
},
|
|
390
448
|
datumContainsSelection = (datum) => {
|
|
391
449
|
if (_.isEmpty(selection)) {
|
|
@@ -416,7 +474,7 @@ function TreeComponent(props) {
|
|
|
416
474
|
});
|
|
417
475
|
return found;
|
|
418
476
|
}
|
|
419
|
-
return searchChildren(
|
|
477
|
+
return searchChildren(getTreeNodeData());
|
|
420
478
|
},
|
|
421
479
|
getTreeNodeByNodeId = (node_id) => {
|
|
422
480
|
if (Repository) {
|
|
@@ -436,6 +494,27 @@ function TreeComponent(props) {
|
|
|
436
494
|
});
|
|
437
495
|
return ids;
|
|
438
496
|
},
|
|
497
|
+
getDatumById = (id) => {
|
|
498
|
+
|
|
499
|
+
let found = null;
|
|
500
|
+
|
|
501
|
+
function walkTree(datum) {
|
|
502
|
+
if (datum.item.id === id) {
|
|
503
|
+
found = datum;
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
_.each(datum.children, (child) => {
|
|
507
|
+
if (!found) {
|
|
508
|
+
walkTree(child);
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
_.each(getTreeNodeData(), (rootDatum) => {
|
|
513
|
+
walkTree(rootDatum);
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
return found;
|
|
517
|
+
},
|
|
439
518
|
assembleDataTreeNodes = () => {
|
|
440
519
|
// Populates the TreeNodes with .parent and .children references
|
|
441
520
|
// NOTE: This is only for 'data', not for Repositories!
|
|
@@ -486,6 +565,25 @@ function TreeComponent(props) {
|
|
|
486
565
|
Repository.areRootNodesLoaded = false;
|
|
487
566
|
return buildAndSetTreeNodeData();
|
|
488
567
|
},
|
|
568
|
+
reloadNode = async (node) => {
|
|
569
|
+
// mark node as loading
|
|
570
|
+
const existingDatum = getNodeData(node.id);
|
|
571
|
+
existingDatum.isLoading = true;
|
|
572
|
+
forceUpdate();
|
|
573
|
+
|
|
574
|
+
// reload from server
|
|
575
|
+
await node.reload();
|
|
576
|
+
|
|
577
|
+
// Refresh the node's display
|
|
578
|
+
const newDatum = buildTreeNodeDatum(node);
|
|
579
|
+
|
|
580
|
+
// copy the updated data to existingDatum
|
|
581
|
+
_.assign(existingDatum, _.omit(newDatum, ['isExpanded']));
|
|
582
|
+
existingDatum.isLoading = false;
|
|
583
|
+
forceUpdate();
|
|
584
|
+
|
|
585
|
+
buildRowToDatumMap();
|
|
586
|
+
},
|
|
489
587
|
loadChildren = async (datum, depth = 1) => {
|
|
490
588
|
// Show loading indicator (spinner underneath current node?)
|
|
491
589
|
datum.isLoading = true;
|
|
@@ -508,12 +606,18 @@ function TreeComponent(props) {
|
|
|
508
606
|
// Hide loading indicator
|
|
509
607
|
datum.isLoading = false;
|
|
510
608
|
forceUpdate();
|
|
609
|
+
|
|
610
|
+
buildRowToDatumMap();
|
|
511
611
|
},
|
|
512
612
|
collapseNodes = (nodes) => {
|
|
613
|
+
collapseNodesRecursive(nodes);
|
|
614
|
+
buildRowToDatumMap();
|
|
615
|
+
},
|
|
616
|
+
collapseNodesRecursive = (nodes) => {
|
|
513
617
|
_.each(nodes, (node) => {
|
|
514
618
|
node.isExpanded = false;
|
|
515
619
|
if (!_.isEmpty(node.children)) {
|
|
516
|
-
|
|
620
|
+
collapseNodesRecursive(node.children);
|
|
517
621
|
}
|
|
518
622
|
});
|
|
519
623
|
},
|
|
@@ -561,9 +665,10 @@ function TreeComponent(props) {
|
|
|
561
665
|
|
|
562
666
|
setSelection([currentNode]);
|
|
563
667
|
scrollToNode(currentNode);
|
|
564
|
-
|
|
668
|
+
setHighlitedDatum(currentDatum);
|
|
565
669
|
|
|
566
670
|
setTreeNodeData(newTreeNodeData);
|
|
671
|
+
buildRowToDatumMap();
|
|
567
672
|
},
|
|
568
673
|
scrollToNode = (node) => {
|
|
569
674
|
// Helper for expandPath
|
|
@@ -572,15 +677,6 @@ function TreeComponent(props) {
|
|
|
572
677
|
// TODO: This will probably need different methods in web and mobile
|
|
573
678
|
|
|
574
679
|
|
|
575
|
-
},
|
|
576
|
-
highlightNode = (node) => {
|
|
577
|
-
// Helper for expandPath
|
|
578
|
-
// Show a brief highlight animation to draw attention to the node
|
|
579
|
-
|
|
580
|
-
// TODO: This will probably need different methods in web and mobile
|
|
581
|
-
// react-highlight for web?
|
|
582
|
-
|
|
583
|
-
|
|
584
680
|
},
|
|
585
681
|
|
|
586
682
|
// render
|
|
@@ -605,9 +701,11 @@ function TreeComponent(props) {
|
|
|
605
701
|
if (canNodesReorder) {
|
|
606
702
|
buttons.push({
|
|
607
703
|
key: 'reorderBtn',
|
|
608
|
-
text: 'Enter reorder mode',
|
|
609
|
-
handler: () =>
|
|
610
|
-
|
|
704
|
+
text: (isDragMode ? 'Exit' : 'Enter') + ' reorder mode',
|
|
705
|
+
handler: () => {
|
|
706
|
+
setIsDragMode(!isDragMode)
|
|
707
|
+
},
|
|
708
|
+
icon: isDragMode ? NoReorderRows : ReorderRows,
|
|
611
709
|
isDisabled: false,
|
|
612
710
|
});
|
|
613
711
|
}
|
|
@@ -652,7 +750,7 @@ function TreeComponent(props) {
|
|
|
652
750
|
if (e.preventDefault && e.cancelable) {
|
|
653
751
|
e.preventDefault();
|
|
654
752
|
}
|
|
655
|
-
if (
|
|
753
|
+
if (isDragMode) {
|
|
656
754
|
return
|
|
657
755
|
}
|
|
658
756
|
switch (e.detail) {
|
|
@@ -676,7 +774,7 @@ function TreeComponent(props) {
|
|
|
676
774
|
if (e.preventDefault && e.cancelable) {
|
|
677
775
|
e.preventDefault();
|
|
678
776
|
}
|
|
679
|
-
if (
|
|
777
|
+
if (isDragMode) {
|
|
680
778
|
return;
|
|
681
779
|
}
|
|
682
780
|
|
|
@@ -692,7 +790,7 @@ function TreeComponent(props) {
|
|
|
692
790
|
}
|
|
693
791
|
}}
|
|
694
792
|
flexDirection="row"
|
|
695
|
-
ml={((areRootsVisible ? depth : depth -1) *
|
|
793
|
+
ml={((areRootsVisible ? depth : depth -1) * DEPTH_INDENT_PX) + 'px'}
|
|
696
794
|
>
|
|
697
795
|
{({
|
|
698
796
|
isHovered,
|
|
@@ -701,45 +799,52 @@ function TreeComponent(props) {
|
|
|
701
799
|
}) => {
|
|
702
800
|
let bg = nodeProps.bg || styles.TREE_NODE_BG,
|
|
703
801
|
mixWith;
|
|
704
|
-
if (
|
|
705
|
-
if (
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
802
|
+
if (!isDragMode) {
|
|
803
|
+
if (isSelected) {
|
|
804
|
+
if (showHovers && isHovered) {
|
|
805
|
+
mixWith = styles.TREE_NODE_SELECTED_HOVER_BG;
|
|
806
|
+
} else {
|
|
807
|
+
mixWith = styles.TREE_NODE_SELECTED_BG;
|
|
808
|
+
}
|
|
809
|
+
} else if (showHovers && isHovered) {
|
|
810
|
+
mixWith = styles.TREE_NODE_HOVER_BG;
|
|
709
811
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
812
|
+
if (mixWith) {
|
|
813
|
+
const
|
|
814
|
+
mixWithObj = nbToRgb(mixWith),
|
|
815
|
+
ratio = mixWithObj.alpha ? 1 - mixWithObj.alpha : 0.5;
|
|
816
|
+
bg = colourMixer.blend(bg, ratio, mixWithObj.color);
|
|
817
|
+
}
|
|
818
|
+
} else {
|
|
819
|
+
|
|
718
820
|
}
|
|
719
821
|
let WhichTreeNode = TreeNode,
|
|
720
|
-
|
|
721
|
-
if (canNodesReorder &&
|
|
722
|
-
WhichTreeNode =
|
|
723
|
-
|
|
822
|
+
dragProps = {};
|
|
823
|
+
if (canNodesReorder && isDragMode && !datum.item.isRoot) { // Can't drag root nodes
|
|
824
|
+
WhichTreeNode = DraggableTreeNode;
|
|
825
|
+
dragProps = {
|
|
724
826
|
mode: VERTICAL,
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
827
|
+
onDrag,
|
|
828
|
+
onDragStop,
|
|
829
|
+
getParentNode: (node) => node.parentElement.parentElement,
|
|
830
|
+
getDraggableNodeFromNode: (node) => node.parentElement,
|
|
831
|
+
getProxy: getDragProxy,
|
|
832
|
+
proxyParent: treeRef.current,
|
|
729
833
|
proxyPositionRelativeToParent: true,
|
|
730
|
-
getParentNode: (node) => node.parentElement.parentElement.parentElement,
|
|
731
|
-
getProxy: getReorderProxy,
|
|
732
834
|
};
|
|
835
|
+
nodeProps.width = '100%';
|
|
733
836
|
}
|
|
734
837
|
|
|
735
838
|
return <WhichTreeNode
|
|
736
839
|
nodeProps={nodeProps}
|
|
840
|
+
{...dragProps}
|
|
737
841
|
bg={bg}
|
|
738
842
|
datum={datum}
|
|
739
843
|
onToggle={onToggle}
|
|
844
|
+
isDragMode={isDragMode}
|
|
845
|
+
isHighlighted={highlitedDatum === datum}
|
|
740
846
|
|
|
741
847
|
// fields={fields}
|
|
742
|
-
{...rowReorderProps}
|
|
743
848
|
/>;
|
|
744
849
|
}}
|
|
745
850
|
</Pressable>;
|
|
@@ -762,129 +867,60 @@ function TreeComponent(props) {
|
|
|
762
867
|
},
|
|
763
868
|
|
|
764
869
|
// drag/drop
|
|
765
|
-
|
|
870
|
+
getDragProxy = (node) => {
|
|
871
|
+
|
|
872
|
+
// TODO: Maybe the proxy should grab itself and all descendants??
|
|
873
|
+
|
|
766
874
|
const
|
|
767
|
-
row = node
|
|
875
|
+
row = node,
|
|
768
876
|
rowRect = row.getBoundingClientRect(),
|
|
769
877
|
parent = row.parentElement,
|
|
770
878
|
parentRect = parent.getBoundingClientRect(),
|
|
771
879
|
proxy = row.cloneNode(true),
|
|
772
880
|
top = rowRect.top - parentRect.top,
|
|
773
|
-
|
|
881
|
+
rows = _.filter(parent.children, (childNode) => {
|
|
882
|
+
if (childNode.getBoundingClientRect().height === 0 && childNode.style.visibility !== 'hidden') {
|
|
883
|
+
return false; // Skip zero-height children
|
|
884
|
+
}
|
|
885
|
+
if (childNode === proxy) {
|
|
886
|
+
return false;
|
|
887
|
+
}
|
|
888
|
+
return true;
|
|
889
|
+
}),
|
|
890
|
+
dragRowIx = Array.from(rows).indexOf(row),
|
|
891
|
+
dragRowRecord = rowToDatumMap[dragRowIx].item;
|
|
774
892
|
|
|
775
|
-
|
|
893
|
+
setDragNodeId(dragRowRecord.id); // the id of which record is being dragged
|
|
776
894
|
|
|
777
895
|
proxy.style.top = top + 'px';
|
|
778
|
-
proxy.style.left = '
|
|
896
|
+
proxy.style.left = (dragRowRecord.depth * DEPTH_INDENT_PX) + 'px';
|
|
779
897
|
proxy.style.height = rowRect.height + 'px';
|
|
780
898
|
proxy.style.width = rowRect.width + 'px';
|
|
781
899
|
proxy.style.display = 'flex';
|
|
782
|
-
// proxy.style.backgroundColor = '#ccc';
|
|
783
900
|
proxy.style.position = 'absolute';
|
|
784
|
-
proxy.style.border = '1px solid #
|
|
901
|
+
proxy.style.border = '1px solid #bbb';
|
|
785
902
|
return proxy;
|
|
786
903
|
},
|
|
787
|
-
|
|
788
|
-
// console.log('
|
|
904
|
+
onDrag = (info, e, proxy, node) => {
|
|
905
|
+
// console.log('onDrag', info, e, proxy, node);
|
|
789
906
|
const
|
|
790
907
|
proxyRect = proxy.getBoundingClientRect(),
|
|
791
|
-
row = node
|
|
908
|
+
row = node,
|
|
792
909
|
parent = row.parentElement,
|
|
793
910
|
parentRect = parent.getBoundingClientRect(),
|
|
794
|
-
rows = _.filter(
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
currentY = proxyRect.top - parentRect.top, // top position of pointer, relative to page
|
|
798
|
-
headerNodeIx = showHeaders ? 0 : null,
|
|
799
|
-
firstActualNodeIx = showHeaders ? 1 : 0;
|
|
800
|
-
|
|
801
|
-
// Figure out which index the user wants
|
|
802
|
-
let newIx = 0;
|
|
803
|
-
_.each(rows, (child, ix, all) => {
|
|
804
|
-
const
|
|
805
|
-
rect = child.getBoundingClientRect(), // rect of the row of this iteration
|
|
806
|
-
{
|
|
807
|
-
top,
|
|
808
|
-
bottom,
|
|
809
|
-
height,
|
|
810
|
-
} = rect,
|
|
811
|
-
compensatedTop = top - parentRect.top,
|
|
812
|
-
compensatedBottom = bottom - parentRect.top,
|
|
813
|
-
halfHeight = height / 2;
|
|
814
|
-
|
|
815
|
-
if (ix === headerNodeIx || child === proxy) {
|
|
816
|
-
return;
|
|
817
|
-
}
|
|
818
|
-
if (ix === firstActualNodeIx) {
|
|
819
|
-
// first row
|
|
820
|
-
if (currentY < compensatedTop + halfHeight) {
|
|
821
|
-
newIx = firstActualNodeIx;
|
|
822
|
-
return false;
|
|
823
|
-
} else if (currentY < compensatedBottom) {
|
|
824
|
-
newIx = firstActualNodeIx + 1;
|
|
825
|
-
return false;
|
|
911
|
+
rows = _.filter(parent.children, (childNode) => {
|
|
912
|
+
if (childNode.getBoundingClientRect().height === 0 && childNode.style.visibility !== 'hidden') {
|
|
913
|
+
return false; // Skip zero-height children
|
|
826
914
|
}
|
|
827
|
-
|
|
828
|
-
} else if (ix === all.length -1) {
|
|
829
|
-
// last row
|
|
830
|
-
if (currentY < compensatedTop + halfHeight) {
|
|
831
|
-
newIx = ix;
|
|
915
|
+
if (childNode === proxy) {
|
|
832
916
|
return false;
|
|
833
917
|
}
|
|
834
|
-
|
|
835
|
-
return false;
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
// all other rows
|
|
839
|
-
if (compensatedTop <= currentY && currentY < compensatedTop + halfHeight) {
|
|
840
|
-
newIx = ix;
|
|
841
|
-
return false;
|
|
842
|
-
} else if (currentY < compensatedBottom) {
|
|
843
|
-
newIx = ix +1;
|
|
844
|
-
return false;
|
|
845
|
-
}
|
|
846
|
-
});
|
|
847
|
-
|
|
848
|
-
let useBottom = false;
|
|
849
|
-
if (!rows[newIx] || rows[newIx] === proxy) {
|
|
850
|
-
newIx--;
|
|
851
|
-
useBottom = true;
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
// Render marker showing destination location
|
|
855
|
-
const
|
|
856
|
-
rowContainerRect = rows[newIx].getBoundingClientRect(),
|
|
857
|
-
top = (useBottom ? rowContainerRect.bottom : rowContainerRect.top) - parentRect.top - parseInt(parent.style.borderWidth), // get relative Y position
|
|
858
|
-
treeNodesContainer = treeRef.current._listRef._scrollRef.childNodes[0],
|
|
859
|
-
treeNodesContainerRect = treeNodesContainer.getBoundingClientRect(),
|
|
860
|
-
marker = document.createElement('div');
|
|
861
|
-
|
|
862
|
-
marker.style.position = 'absolute';
|
|
863
|
-
marker.style.top = top -4 + 'px'; // -4 so it's always visible
|
|
864
|
-
marker.style.height = '4px';
|
|
865
|
-
marker.style.width = treeNodesContainerRect.width + 'px';
|
|
866
|
-
marker.style.backgroundColor = '#f00';
|
|
867
|
-
|
|
868
|
-
treeNodesContainer.appendChild(marker);
|
|
869
|
-
|
|
870
|
-
setDragNodeSlot({ ix: newIx, marker, useBottom, });
|
|
871
|
-
},
|
|
872
|
-
onNodeReorderDrag = (info, e, proxy, node) => {
|
|
873
|
-
// console.log('onNodeReorderDrag', info, e, proxy, node);
|
|
874
|
-
const
|
|
875
|
-
proxyRect = proxy.getBoundingClientRect(),
|
|
876
|
-
row = node.parentElement.parentElement,
|
|
877
|
-
parent = row.parentElement,
|
|
878
|
-
parentRect = parent.getBoundingClientRect(),
|
|
879
|
-
rows = _.filter(row.parentElement.children, (childNode) => {
|
|
880
|
-
return childNode.getBoundingClientRect().height !== 0; // Skip zero-height children
|
|
918
|
+
return true;
|
|
881
919
|
}),
|
|
882
|
-
currentY = proxyRect.top - parentRect.top
|
|
883
|
-
headerNodeIx = showHeaders ? 0 : null,
|
|
884
|
-
firstActualNodeIx = showHeaders ? 1 : 0;
|
|
920
|
+
currentY = proxyRect.top - parentRect.top; // top position of pointer, relative to page
|
|
885
921
|
|
|
886
|
-
// Figure out which
|
|
887
|
-
let newIx = 0;
|
|
922
|
+
// Figure out which row the user wants as a parentId
|
|
923
|
+
let newIx = 0; // default to root being new parentId
|
|
888
924
|
_.each(rows, (child, ix, all) => {
|
|
889
925
|
const
|
|
890
926
|
rect = child.getBoundingClientRect(), // rect of the row of this iteration
|
|
@@ -893,117 +929,89 @@ function TreeComponent(props) {
|
|
|
893
929
|
bottom,
|
|
894
930
|
height,
|
|
895
931
|
} = rect,
|
|
896
|
-
|
|
897
|
-
compensatedBottom = bottom - parentRect.top,
|
|
898
|
-
halfHeight = height / 2;
|
|
932
|
+
compensatedBottom = bottom - parentRect.top;
|
|
899
933
|
|
|
900
|
-
if (
|
|
934
|
+
if (child === proxy) {
|
|
901
935
|
return;
|
|
902
936
|
}
|
|
903
|
-
if (ix ===
|
|
937
|
+
if (ix === 0) {
|
|
904
938
|
// first row
|
|
905
|
-
if (currentY <
|
|
906
|
-
newIx =
|
|
907
|
-
return false;
|
|
908
|
-
} else if (currentY < compensatedBottom) {
|
|
909
|
-
newIx = firstActualNodeIx + 1;
|
|
939
|
+
if (currentY < compensatedBottom) {
|
|
940
|
+
newIx = 0;
|
|
910
941
|
return false;
|
|
911
942
|
}
|
|
912
943
|
return;
|
|
913
944
|
} else if (ix === all.length -1) {
|
|
914
945
|
// last row
|
|
915
|
-
if (currentY <
|
|
946
|
+
if (currentY < compensatedBottom) {
|
|
916
947
|
newIx = ix;
|
|
917
948
|
return false;
|
|
918
949
|
}
|
|
919
|
-
|
|
920
|
-
return false;
|
|
950
|
+
return;
|
|
921
951
|
}
|
|
922
952
|
|
|
923
953
|
// all other rows
|
|
924
|
-
if (
|
|
954
|
+
if (currentY < compensatedBottom) {
|
|
925
955
|
newIx = ix;
|
|
926
956
|
return false;
|
|
927
|
-
} else if (currentY < compensatedBottom) {
|
|
928
|
-
newIx = ix +1;
|
|
929
|
-
return false;
|
|
930
957
|
}
|
|
931
958
|
});
|
|
932
959
|
|
|
933
|
-
let useBottom = false;
|
|
934
|
-
if (!rows[newIx] || rows[newIx] === proxy) {
|
|
935
|
-
newIx--;
|
|
936
|
-
useBottom = true;
|
|
937
|
-
}
|
|
938
960
|
|
|
939
|
-
// Render marker showing destination location (can't use regular render cycle because this div is absolutely positioned on page)
|
|
940
961
|
const
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
962
|
+
dragDatum = getDatumById(dragNodeId),
|
|
963
|
+
dragDatumChildIds = getDatumChildIds(dragDatum),
|
|
964
|
+
dropRowDatum = rowToDatumMap[newIx],
|
|
965
|
+
dropRowRecord = dropRowDatum.item,
|
|
966
|
+
dropNodeId = dropRowRecord.id,
|
|
967
|
+
dragNodeContainsDropNode = inArray(dropNodeId, dragDatumChildIds) || dropRowRecord.id === dragNodeId;
|
|
968
|
+
|
|
969
|
+
if (dragNodeContainsDropNode) {
|
|
970
|
+
// the node can be a child of any node except itself or its own descendants
|
|
971
|
+
setDropRowIx(null);
|
|
972
|
+
setHighlitedDatum(null);
|
|
973
|
+
|
|
974
|
+
} else {
|
|
975
|
+
console.log('setDropRowIx', newIx);
|
|
976
|
+
setDropRowIx(newIx);
|
|
947
977
|
|
|
948
|
-
|
|
949
|
-
|
|
978
|
+
// highlight the drop node
|
|
979
|
+
setHighlitedDatum(dropRowDatum);
|
|
950
980
|
|
|
981
|
+
// shift proxy's depth
|
|
982
|
+
const depth = (dropRowRecord.id === dragNodeId) ? dropRowRecord.depth : dropRowRecord.depth + 1;
|
|
983
|
+
proxy.style.left = (depth * DEPTH_INDENT_PX) + 'px';
|
|
984
|
+
}
|
|
951
985
|
},
|
|
952
|
-
|
|
953
|
-
// console.log('
|
|
954
|
-
const
|
|
955
|
-
dropIx = dragNodeSlot.ix,
|
|
956
|
-
compensatedDragIx = showHeaders ? dragNodeIx -1 : dragNodeIx, // ix, without taking header row into account
|
|
957
|
-
compensatedDropIx = showHeaders ? dropIx -1 : dropIx, // // ix, without taking header row into account
|
|
958
|
-
dropPosition = dragNodeSlot.useBottom ? DROP_POSITION_AFTER : DROP_POSITION_BEFORE;
|
|
986
|
+
onDragStop = async (delta, e, config) => {
|
|
987
|
+
// console.log('onDragStop', delta, e, config);
|
|
959
988
|
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
if (dropPosition === DROP_POSITION_BEFORE) {
|
|
964
|
-
if (dragNodeIx === dropIx || dragNodeIx === dropIx -1) { // basically before or after the drag row's origin
|
|
965
|
-
// Same as origin; don't do anything
|
|
966
|
-
shouldMove = false;
|
|
967
|
-
} else {
|
|
968
|
-
// Actually move it
|
|
969
|
-
if (!Repository) { // If we're just going to be switching rows, rather than telling server to reorder rows, so maybe adjust finalDropIx...
|
|
970
|
-
if (finalDropIx > compensatedDragIx) { // if we're dropping *before* the origin ix
|
|
971
|
-
finalDropIx = finalDropIx -1; // Because we're using BEFORE, we want to switch with the row *prior to* the ix we're dropping before
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
} else if (dropPosition === DROP_POSITION_AFTER) {
|
|
976
|
-
// Only happens on the very last row. Everything else is BEFORE...
|
|
977
|
-
if (dragNodeIx === dropIx) {
|
|
978
|
-
// Same as origin; don't do anything
|
|
979
|
-
shouldMove = false;
|
|
980
|
-
}
|
|
989
|
+
if (_.isNil(dropRowIx)) {
|
|
990
|
+
return;
|
|
981
991
|
}
|
|
992
|
+
|
|
993
|
+
const
|
|
994
|
+
dragDatum = getDatumById(dragNodeId),
|
|
995
|
+
dragRowRecord = dragDatum.item,
|
|
996
|
+
dropRowDatum = rowToDatumMap[dropRowIx],
|
|
997
|
+
dropRowRecord = dropRowDatum.item;
|
|
982
998
|
|
|
983
|
-
if (
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
dragRecord = Repository.getByIx(compensatedDragIx);
|
|
989
|
-
dropRecord = Repository.getByIx(finalDropIx);
|
|
990
|
-
|
|
991
|
-
Repository.reorder(dragRecord, dropRecord, dropPosition);
|
|
999
|
+
if (Repository) {
|
|
1000
|
+
|
|
1001
|
+
const commonAncestorId = await Repository.moveTreeNode(dragRowRecord, dropRowRecord.id);
|
|
1002
|
+
const commonAncestorDatum = getDatumById(commonAncestorId);
|
|
1003
|
+
reloadNode(commonAncestorDatum.item);
|
|
992
1004
|
|
|
993
|
-
|
|
994
|
-
function arrayMove(arr, fromIndex, toIndex) {
|
|
995
|
-
var element = arr[fromIndex];
|
|
996
|
-
arr.splice(fromIndex, 1);
|
|
997
|
-
arr.splice(toIndex, 0, element);
|
|
998
|
-
}
|
|
999
|
-
arrayMove(data, compensatedDragIx, finalDropIx);
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1005
|
+
} else {
|
|
1002
1006
|
|
|
1003
|
-
|
|
1004
|
-
|
|
1007
|
+
throw Error('Not yet implemented');
|
|
1008
|
+
// function arrayMove(arr, fromIndex, toIndex) {
|
|
1009
|
+
// var element = arr[fromIndex];
|
|
1010
|
+
// arr.splice(fromIndex, 1);
|
|
1011
|
+
// arr.splice(toIndex, 0, element);
|
|
1012
|
+
// }
|
|
1013
|
+
// arrayMove(data, dragNodeIx, finalDropIx);
|
|
1005
1014
|
}
|
|
1006
|
-
setDragNodeSlot(null);
|
|
1007
1015
|
};
|
|
1008
1016
|
|
|
1009
1017
|
useEffect(() => {
|
|
@@ -1070,8 +1078,8 @@ function TreeComponent(props) {
|
|
|
1070
1078
|
});
|
|
1071
1079
|
|
|
1072
1080
|
const
|
|
1073
|
-
headerToolbarItemComponents = useMemo(() => getHeaderToolbarItems(), [treeSearchValue, getTreeNodeData()]),
|
|
1074
|
-
footerToolbarItemComponents = useMemo(() => getFooterToolbarItems(), [additionalToolbarButtons,
|
|
1081
|
+
headerToolbarItemComponents = useMemo(() => getHeaderToolbarItems(), [treeSearchValue, isDragMode, getTreeNodeData()]),
|
|
1082
|
+
footerToolbarItemComponents = useMemo(() => getFooterToolbarItems(), [additionalToolbarButtons, isDragMode, getTreeNodeData()]);
|
|
1075
1083
|
|
|
1076
1084
|
if (!isReady) {
|
|
1077
1085
|
return null;
|
|
@@ -1088,7 +1096,17 @@ function TreeComponent(props) {
|
|
|
1088
1096
|
treeFooterComponent = <Toolbar>{footerToolbarItemComponents}</Toolbar>;
|
|
1089
1097
|
}
|
|
1090
1098
|
}
|
|
1091
|
-
|
|
1099
|
+
|
|
1100
|
+
const borderProps = {};
|
|
1101
|
+
if (isDragMode) {
|
|
1102
|
+
borderProps.borderWidth = isDragMode ? styles.REORDER_BORDER_WIDTH : 0;
|
|
1103
|
+
borderProps.borderColor = isDragMode ? styles.REORDER_BORDER_COLOR : null;
|
|
1104
|
+
borderProps.borderStyle = styles.REORDER_BORDER_STYLE;
|
|
1105
|
+
} else {
|
|
1106
|
+
borderProps.borderTopWidth = isLoading ? 2 : 1;
|
|
1107
|
+
borderProps.borderTopColor = isLoading ? '#f00' : 'trueGray.300';
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1092
1110
|
return <>
|
|
1093
1111
|
<Column
|
|
1094
1112
|
{...testProps('Tree')}
|
|
@@ -1098,11 +1116,18 @@ function TreeComponent(props) {
|
|
|
1098
1116
|
{topToolbar}
|
|
1099
1117
|
{headerToolbarItemComponents?.length && <Row>{headerToolbarItemComponents}</Row>}
|
|
1100
1118
|
|
|
1101
|
-
<Column
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1119
|
+
<Column
|
|
1120
|
+
ref={treeRef}
|
|
1121
|
+
w="100%"
|
|
1122
|
+
flex={1}
|
|
1123
|
+
p={2}
|
|
1124
|
+
{...borderProps}
|
|
1125
|
+
onClick={() => {
|
|
1126
|
+
if (!isDragMode) {
|
|
1127
|
+
deselectAll();
|
|
1128
|
+
}
|
|
1129
|
+
}}
|
|
1130
|
+
>
|
|
1106
1131
|
{!treeNodes?.length ? <NoRecordsFound text={noneFoundText} onRefresh={reloadTree} /> :
|
|
1107
1132
|
treeNodes}
|
|
1108
1133
|
</Column>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useMemo, } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Box,
|
|
4
4
|
Icon,
|
|
@@ -6,9 +6,6 @@ import {
|
|
|
6
6
|
Spinner,
|
|
7
7
|
Text,
|
|
8
8
|
} from 'native-base';
|
|
9
|
-
import {
|
|
10
|
-
VERTICAL,
|
|
11
|
-
} from '../../Constants/Directions.js';
|
|
12
9
|
import UiGlobals from '../../UiGlobals.js';
|
|
13
10
|
import withDraggable from '../Hoc/withDraggable.js';
|
|
14
11
|
import IconButton from '../Buttons/IconButton.js';
|
|
@@ -22,6 +19,8 @@ export default function TreeNode(props) {
|
|
|
22
19
|
bg,
|
|
23
20
|
datum,
|
|
24
21
|
onToggle,
|
|
22
|
+
isDragMode,
|
|
23
|
+
isHighlighted,
|
|
25
24
|
...propsToPass
|
|
26
25
|
} = props,
|
|
27
26
|
styles = UiGlobals.styles,
|
|
@@ -37,7 +36,9 @@ export default function TreeNode(props) {
|
|
|
37
36
|
iconLeaf = datum.iconLeaf,
|
|
38
37
|
hash = item?.hash || item;
|
|
39
38
|
|
|
40
|
-
const
|
|
39
|
+
const
|
|
40
|
+
icon = hasChildren ? (isExpanded ? iconExpanded : iconCollapsed) : iconLeaf,
|
|
41
|
+
adjustedBg = isHighlighted ? styles.TREE_NODE_HIGHLIGHTED_BG : bg;
|
|
41
42
|
|
|
42
43
|
return useMemo(() => {
|
|
43
44
|
|
|
@@ -45,14 +46,14 @@ export default function TreeNode(props) {
|
|
|
45
46
|
alignItems="center"
|
|
46
47
|
flexGrow={1}
|
|
47
48
|
{...nodeProps}
|
|
48
|
-
bg={
|
|
49
|
+
bg={adjustedBg}
|
|
49
50
|
key={hash}
|
|
50
51
|
>
|
|
51
52
|
{isPhantom && <Box position="absolute" bg="#f00" h={2} w={2} t={0} l={0} />}
|
|
52
53
|
|
|
53
54
|
{isLoading ?
|
|
54
55
|
<Spinner px={2} /> :
|
|
55
|
-
(hasChildren ? <IconButton icon={icon} onPress={() => onToggle(datum)} /> : <Icon as={icon} px={2} />)}
|
|
56
|
+
(hasChildren && !isDragMode ? <IconButton icon={icon} onPress={() => onToggle(datum)} /> : <Icon as={icon} px={2} />)}
|
|
56
57
|
|
|
57
58
|
<Text
|
|
58
59
|
overflow="hidden"
|
|
@@ -70,7 +71,7 @@ export default function TreeNode(props) {
|
|
|
70
71
|
</Row>;
|
|
71
72
|
}, [
|
|
72
73
|
nodeProps,
|
|
73
|
-
|
|
74
|
+
adjustedBg,
|
|
74
75
|
item,
|
|
75
76
|
isPhantom,
|
|
76
77
|
hash, // this is an easy way to determine if the data has changed and the item needs to be rerendered
|
|
@@ -81,16 +82,9 @@ export default function TreeNode(props) {
|
|
|
81
82
|
icon,
|
|
82
83
|
onToggle,
|
|
83
84
|
isLoading,
|
|
85
|
+
isDragMode,
|
|
86
|
+
isHighlighted,
|
|
84
87
|
]);
|
|
85
88
|
}
|
|
86
89
|
|
|
87
|
-
|
|
88
|
-
return (props) => {
|
|
89
|
-
return <WrappedComponent
|
|
90
|
-
mode={VERTICAL}
|
|
91
|
-
{...props}
|
|
92
|
-
/>;
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export const ReorderableTreeNode = withAdditionalProps(withDraggable(TreeNode));
|
|
90
|
+
export const DraggableTreeNode = withDraggable(TreeNode);
|
package/src/Constants/Styles.js
CHANGED
|
@@ -76,6 +76,9 @@ const defaults = {
|
|
|
76
76
|
PANEL_HEADER_TEXT_FONTSIZE: 15,
|
|
77
77
|
PANEL_HEADER_PX: 3,
|
|
78
78
|
PANEL_HEADER_PY: 1,
|
|
79
|
+
REORDER_BORDER_COLOR: '#23d9ea',
|
|
80
|
+
REORDER_BORDER_WIDTH: 4,
|
|
81
|
+
REORDER_BORDER_STYLE: 'dashed',
|
|
79
82
|
TAB_ACTIVE_BG: 'trueGray.200',
|
|
80
83
|
TAB_ACTIVE_HOVER_BG: 'trueGray.200',
|
|
81
84
|
TAB_ACTIVE_COLOR: 'primary.800',
|
|
@@ -94,6 +97,7 @@ const defaults = {
|
|
|
94
97
|
TREE_NODE_HOVER_BG: 'hover',
|
|
95
98
|
TREE_NODE_SELECTED_BG: 'selected',
|
|
96
99
|
TREE_NODE_SELECTED_HOVER_BG: 'selectedHover',
|
|
100
|
+
TREE_NODE_HIGHLIGHTED_BG: '#ffa',
|
|
97
101
|
TOOLBAR_ITEMS_COLOR: 'trueGray.800',
|
|
98
102
|
TOOLBAR_ITEMS_DISABLED_COLOR: 'trueGray.400',
|
|
99
103
|
TOOLBAR_ITEMS_ICON_SIZE: 'sm',
|
package/src/Constants/Tree.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export const SORT_ASCENDING = 'ASC'; // This is what RestTrait expects
|
|
2
2
|
export const SORT_DESCENDING = 'DESC'; // This is what RestTrait expects
|
|
3
3
|
export const DROP_POSITION_BEFORE = 'before'; // This is what RestTrait expects
|
|
4
|
-
export const DROP_POSITION_AFTER = 'after'; // This is what RestTrait expects
|
|
4
|
+
export const DROP_POSITION_AFTER = 'after'; // This is what RestTrait expects
|
|
5
|
+
export const COLLAPSED = 'COLLAPSED';
|
|
6
|
+
export const EXPANDED = 'EXPANDED';
|
|
7
|
+
export const LEAF = 'LEAF';
|
package/src/Constants/UiModes.js
CHANGED
|
@@ -5,8 +5,8 @@ export const UI_MODE_WEB = 'Web';
|
|
|
5
5
|
export const UI_MODE_REACT_NATIVE = 'ReactNative';
|
|
6
6
|
|
|
7
7
|
export let CURRENT_MODE;
|
|
8
|
-
if (
|
|
9
|
-
CURRENT_MODE = UI_MODE_REACT_NATIVE;
|
|
10
|
-
} else if (isBrowser || isWebWorker) {
|
|
8
|
+
if (isBrowser || isWebWorker) {
|
|
11
9
|
CURRENT_MODE = UI_MODE_WEB;
|
|
10
|
+
} else if (isReactNative) {
|
|
11
|
+
CURRENT_MODE = UI_MODE_REACT_NATIVE;
|
|
12
12
|
}
|