@onehat/ui 0.2.68 → 0.2.70
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onehat/ui",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.70",
|
|
4
4
|
"description": "Base UI for OneHat apps",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"license": "UNLICENSED",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@onehat/data": "^1.18.
|
|
28
|
+
"@onehat/data": "^1.18.7",
|
|
29
29
|
"@hookform/resolvers": "^3.1.1",
|
|
30
30
|
"@k-renwick/colour-mixer": "^1.2.1",
|
|
31
31
|
"js-cookie": "^3.0.5",
|
|
@@ -881,7 +881,8 @@ export const WindowedGridEditor = withAlert(
|
|
|
881
881
|
withPresetButtons(
|
|
882
882
|
withContextMenu(
|
|
883
883
|
Grid
|
|
884
|
-
)
|
|
884
|
+
),
|
|
885
|
+
true // isGrid
|
|
885
886
|
)
|
|
886
887
|
)
|
|
887
888
|
)
|
|
@@ -901,7 +902,8 @@ export const InlineGridEditor = withAlert(
|
|
|
901
902
|
withContextMenu(
|
|
902
903
|
withFilters(
|
|
903
904
|
Grid
|
|
904
|
-
)
|
|
905
|
+
),
|
|
906
|
+
true // isGrid
|
|
905
907
|
)
|
|
906
908
|
)
|
|
907
909
|
)
|
|
@@ -22,7 +22,7 @@ const presetButtons = [
|
|
|
22
22
|
// 'print',
|
|
23
23
|
];
|
|
24
24
|
|
|
25
|
-
export default function withPresetButtons(WrappedComponent) {
|
|
25
|
+
export default function withPresetButtons(WrappedComponent, isGrid = false) {
|
|
26
26
|
return (props) => {
|
|
27
27
|
const {
|
|
28
28
|
// extract and pass
|
|
@@ -37,10 +37,10 @@ export default function withPresetButtons(WrappedComponent) {
|
|
|
37
37
|
disableAdd = false,
|
|
38
38
|
disableEdit = false,
|
|
39
39
|
disableDelete = false,
|
|
40
|
-
disableView =
|
|
41
|
-
disableCopy =
|
|
42
|
-
disableDuplicate =
|
|
43
|
-
disablePrint =
|
|
40
|
+
disableView = !isGrid,
|
|
41
|
+
disableCopy = !isGrid,
|
|
42
|
+
disableDuplicate = !isGrid,
|
|
43
|
+
disablePrint = !isGrid,
|
|
44
44
|
|
|
45
45
|
// withEditor
|
|
46
46
|
userCanEdit = true,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import Svg, { G, Path } from "react-native-svg"
|
|
3
|
+
import { Icon } from 'native-base';
|
|
4
|
+
|
|
5
|
+
function SvgComponent(props) {
|
|
6
|
+
return (
|
|
7
|
+
<Icon
|
|
8
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
9
|
+
viewBox="0 0 508.3 508.87"
|
|
10
|
+
{...props}
|
|
11
|
+
>
|
|
12
|
+
<Path d="M253.87 387.44c73.46 0 133-59.55 133-133s-59.55-133-133-133-133 59.55-133 133 59.55 133 133 133z" />
|
|
13
|
+
<Path d="M0 0H508.3V508.87H0z" fill="none" />
|
|
14
|
+
</Icon>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default SvgComponent
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
DROP_POSITION_BEFORE,
|
|
20
20
|
DROP_POSITION_AFTER,
|
|
21
21
|
} from '../../Constants/Tree.js';
|
|
22
|
+
import sleep from '@onehat/ui/src/Functions/sleep.js';
|
|
22
23
|
import * as colourMixer from '@k-renwick/colour-mixer'
|
|
23
24
|
import UiGlobals from '../../UiGlobals.js';
|
|
24
25
|
import useForceUpdate from '../../Hooks/useForceUpdate.js';
|
|
@@ -38,7 +39,7 @@ import TreeNode, { ReorderableTreeNode } from './TreeNode.js';
|
|
|
38
39
|
import FormPanel from '../Panel/FormPanel.js';
|
|
39
40
|
import Input from '../Form/Field/Input.js';
|
|
40
41
|
import IconButton from '../Buttons/IconButton.js';
|
|
41
|
-
import
|
|
42
|
+
import Dot from '../Icons/Dot.js';
|
|
42
43
|
import Collapse from '../Icons/Collapse.js';
|
|
43
44
|
import FolderClosed from '../Icons/FolderClosed.js';
|
|
44
45
|
import FolderOpen from '../Icons/FolderOpen.js';
|
|
@@ -51,54 +52,27 @@ import Toolbar from '../Toolbar/Toolbar.js';
|
|
|
51
52
|
import _ from 'lodash';
|
|
52
53
|
|
|
53
54
|
|
|
54
|
-
// Tree requires the use of HOC withSelection() whenever it's used.
|
|
55
|
-
// The default export is *with* the HOC. A separate *raw* component is
|
|
56
|
-
// exported which can be combined with many HOCs for various functionality.
|
|
57
|
-
|
|
58
|
-
|
|
59
55
|
//////////////////////
|
|
60
56
|
//////////////////////
|
|
61
57
|
|
|
62
|
-
// I'm thinking if a repository senses that it's a tree, then at initial load
|
|
63
|
-
// it should get the root node +1 level of children.
|
|
64
|
-
//
|
|
65
|
-
// How would it then subsequently get the proper children?
|
|
66
|
-
// i.e. When a node gets its children, how will it do this
|
|
67
|
-
// while maintaining the nodes that already exist there?
|
|
68
|
-
// We don't want it to *replace* all exisitng nodes!
|
|
69
|
-
//
|
|
70
|
-
// And if the repository does a reload, should it just get root+1 again?
|
|
71
|
-
// Changing filters would potentially change the tree structure.
|
|
72
|
-
// Changing sorting would only change the ordering, not what is expanded/collapsed or visible/invisible.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
58
|
// Need to take into account whether using Repository or data.
|
|
77
59
|
// If using data, everything exists at once. What format will data be in?
|
|
78
60
|
// How does this interface with Repository?
|
|
79
61
|
// Maybe if Repository is not AjaxRepository, everything needs to be present at once!
|
|
80
62
|
|
|
81
|
-
|
|
82
|
-
// isRootVisible
|
|
83
|
-
|
|
84
63
|
//////////////////////
|
|
85
64
|
//////////////////////
|
|
86
65
|
|
|
87
66
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
67
|
export function Tree(props) {
|
|
93
68
|
const {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return {};
|
|
97
|
-
},
|
|
69
|
+
areRootsVisible = true,
|
|
70
|
+
extraParams = {}, // Additional params to send with each request ( e.g. { order: 'Categories.name ASC' })
|
|
98
71
|
getNodeText = (item) => { // extracts model/data and decides what the row text should be
|
|
99
72
|
return item.displayValue;
|
|
100
73
|
},
|
|
101
74
|
getNodeIcon = (item, isExpanded) => { // decides what icon to show for this node
|
|
75
|
+
// TODO: Allow for dynamic props on the icon (e.g. special color for some icons)
|
|
102
76
|
let icon;
|
|
103
77
|
if (item.hasChildren) {
|
|
104
78
|
if (isExpanded) {
|
|
@@ -107,11 +81,11 @@ export function Tree(props) {
|
|
|
107
81
|
icon = FolderClosed;
|
|
108
82
|
}
|
|
109
83
|
} else {
|
|
110
|
-
icon =
|
|
84
|
+
icon = Dot;
|
|
111
85
|
}
|
|
112
86
|
return icon;
|
|
113
87
|
},
|
|
114
|
-
|
|
88
|
+
getNodeProps = (item) => {
|
|
115
89
|
return {};
|
|
116
90
|
},
|
|
117
91
|
noneFoundText,
|
|
@@ -170,7 +144,12 @@ export function Tree(props) {
|
|
|
170
144
|
[searchFormData, setSearchFormData] = useState([]),
|
|
171
145
|
[dragNodeSlot, setDragNodeSlot] = useState(null),
|
|
172
146
|
[dragNodeIx, setDragNodeIx] = useState(),
|
|
147
|
+
[treeSearchValue, setTreeSearchValue] = useState(''),
|
|
173
148
|
onNodeClick = (item, e) => {
|
|
149
|
+
if (!setSelection) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
174
153
|
const
|
|
175
154
|
{
|
|
176
155
|
shiftKey,
|
|
@@ -243,13 +222,12 @@ export function Tree(props) {
|
|
|
243
222
|
getHeaderToolbarItems = () => {
|
|
244
223
|
const
|
|
245
224
|
buttons = [
|
|
246
|
-
|
|
247
225
|
{
|
|
248
226
|
key: 'searchBtn',
|
|
249
227
|
text: 'Search tree',
|
|
250
228
|
handler: onSearchTree,
|
|
251
229
|
icon: MagnifyingGlass,
|
|
252
|
-
isDisabled:
|
|
230
|
+
isDisabled: !treeSearchValue.length,
|
|
253
231
|
},
|
|
254
232
|
{
|
|
255
233
|
key: 'collapseBtn',
|
|
@@ -262,7 +240,7 @@ export function Tree(props) {
|
|
|
262
240
|
if (canNodesReorder) {
|
|
263
241
|
buttons.push({
|
|
264
242
|
key: 'reorderBtn',
|
|
265
|
-
text: '
|
|
243
|
+
text: 'Enter reorder mode',
|
|
266
244
|
handler: () => setIsReorderMode(!isReorderMode),
|
|
267
245
|
icon: isReorderMode ? NoReorderRows : ReorderRows,
|
|
268
246
|
isDisabled: false,
|
|
@@ -271,10 +249,16 @@ export function Tree(props) {
|
|
|
271
249
|
const items = _.map(buttons, getIconFromConfig);
|
|
272
250
|
|
|
273
251
|
items.unshift(<Input // Add text input to beginning of header items
|
|
274
|
-
key="
|
|
252
|
+
key="searchNodes"
|
|
275
253
|
flex={1}
|
|
276
|
-
placeholder="
|
|
277
|
-
|
|
254
|
+
placeholder="Find tree node"
|
|
255
|
+
onChangeText={(val) => setTreeSearchValue(val)}
|
|
256
|
+
onKeyPress={(e, value) => {
|
|
257
|
+
if (e.key === 'Enter') {
|
|
258
|
+
onSearchTree(value);
|
|
259
|
+
}
|
|
260
|
+
}}
|
|
261
|
+
value={treeSearchValue}
|
|
278
262
|
autoSubmit={false}
|
|
279
263
|
/>);
|
|
280
264
|
|
|
@@ -292,11 +276,12 @@ export function Tree(props) {
|
|
|
292
276
|
mx: 1,
|
|
293
277
|
px: 3,
|
|
294
278
|
},
|
|
295
|
-
|
|
279
|
+
_icon = {
|
|
296
280
|
alignSelf: 'center',
|
|
297
281
|
size: styles.TREE_TOOLBAR_ITEMS_ICON_SIZE,
|
|
298
282
|
h: 20,
|
|
299
283
|
w: 20,
|
|
284
|
+
color: isDisabled ? styles.TREE_TOOLBAR_ITEMS_DISABLED_COLOR : styles.TREE_TOOLBAR_ITEMS_COLOR,
|
|
300
285
|
};
|
|
301
286
|
let {
|
|
302
287
|
key,
|
|
@@ -305,16 +290,11 @@ export function Tree(props) {
|
|
|
305
290
|
icon = null,
|
|
306
291
|
isDisabled = false,
|
|
307
292
|
} = config;
|
|
308
|
-
if (icon) {
|
|
309
|
-
const thisIconProps = {
|
|
310
|
-
color: isDisabled ? styles.TREE_TOOLBAR_ITEMS_DISABLED_COLOR : styles.TREE_TOOLBAR_ITEMS_COLOR,
|
|
311
|
-
};
|
|
312
|
-
icon = React.cloneElement(icon, {...iconProps, ...thisIconProps});
|
|
313
|
-
}
|
|
314
293
|
return <IconButton
|
|
315
294
|
key={key || ix}
|
|
316
295
|
onPress={handler}
|
|
317
296
|
icon={icon}
|
|
297
|
+
_icon={_icon}
|
|
318
298
|
isDisabled={isDisabled}
|
|
319
299
|
tooltip={text}
|
|
320
300
|
{...iconButtonProps}
|
|
@@ -326,16 +306,17 @@ export function Tree(props) {
|
|
|
326
306
|
// renderTreeNode uses this to render the nodes.
|
|
327
307
|
const
|
|
328
308
|
isRoot = treeNode.isRoot,
|
|
329
|
-
|
|
309
|
+
children = buildTreeNodeData(treeNode.children), // recursively get data for children
|
|
330
310
|
datum = {
|
|
331
311
|
item: treeNode,
|
|
332
312
|
text: getNodeText(treeNode),
|
|
333
|
-
iconCollapsed:
|
|
334
|
-
iconExpanded:
|
|
335
|
-
iconLeaf:
|
|
313
|
+
iconCollapsed: getNodeIcon(treeNode, false),
|
|
314
|
+
iconExpanded: getNodeIcon(treeNode, true),
|
|
315
|
+
iconLeaf: getNodeIcon(treeNode),
|
|
336
316
|
isExpanded: isRoot, // all non-root treeNodes are not expanded by default
|
|
337
|
-
isVisible: isRoot ?
|
|
338
|
-
|
|
317
|
+
isVisible: isRoot ? areRootsVisible : true,
|
|
318
|
+
isLoading: false,
|
|
319
|
+
children,
|
|
339
320
|
};
|
|
340
321
|
|
|
341
322
|
return datum;
|
|
@@ -348,7 +329,9 @@ export function Tree(props) {
|
|
|
348
329
|
return data;
|
|
349
330
|
},
|
|
350
331
|
renderTreeNode = (datum) => {
|
|
351
|
-
const
|
|
332
|
+
const
|
|
333
|
+
item = datum.item,
|
|
334
|
+
depth = item.depth;
|
|
352
335
|
if (item.isDestroyed) {
|
|
353
336
|
return null;
|
|
354
337
|
}
|
|
@@ -391,7 +374,11 @@ export function Tree(props) {
|
|
|
391
374
|
e.preventDefault();
|
|
392
375
|
}
|
|
393
376
|
if (isReorderMode) {
|
|
394
|
-
return
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (!setSelection) {
|
|
381
|
+
return;
|
|
395
382
|
}
|
|
396
383
|
|
|
397
384
|
// context menu
|
|
@@ -402,7 +389,7 @@ export function Tree(props) {
|
|
|
402
389
|
}
|
|
403
390
|
}}
|
|
404
391
|
flexDirection="row"
|
|
405
|
-
|
|
392
|
+
ml={((areRootsVisible ? depth : depth -1) * 20) + 'px'}
|
|
406
393
|
>
|
|
407
394
|
{({
|
|
408
395
|
isHovered,
|
|
@@ -455,122 +442,125 @@ export function Tree(props) {
|
|
|
455
442
|
</Pressable>;
|
|
456
443
|
},
|
|
457
444
|
renderTreeNodes = (data) => {
|
|
458
|
-
|
|
445
|
+
let nodes = [];
|
|
459
446
|
_.each(data, (datum) => {
|
|
460
|
-
nodes.push(renderTreeNode(datum));
|
|
461
|
-
});
|
|
462
|
-
return nodes;
|
|
463
|
-
},
|
|
464
|
-
renderAllTreeNodes = () => {
|
|
465
|
-
const nodes = [];
|
|
466
|
-
_.each(treeNodeData, (datum) => {
|
|
467
447
|
const node = renderTreeNode(datum);
|
|
468
|
-
if (_.isEmpty(node)) {
|
|
469
|
-
return;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
448
|
nodes.push(node);
|
|
473
449
|
|
|
474
|
-
if (
|
|
475
|
-
|
|
450
|
+
if (datum.children.length && datum.isExpanded) {
|
|
451
|
+
const childTreeNodes = renderTreeNodes(datum.children); // recursion
|
|
452
|
+
nodes = nodes.concat(childTreeNodes);
|
|
476
453
|
}
|
|
477
|
-
|
|
478
|
-
const children = renderTreeNodes(datum.children);
|
|
479
|
-
if (_.isEmpty(children)) {
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
nodes.concat(children);
|
|
484
454
|
});
|
|
485
455
|
return nodes;
|
|
486
456
|
},
|
|
457
|
+
getDatumChildIds = (datum) => {
|
|
458
|
+
let ids = [];
|
|
459
|
+
_.each(datum.children, (childDatum) => {
|
|
460
|
+
ids.push(childDatum.item.id);
|
|
461
|
+
if (childDatum.children.length) {
|
|
462
|
+
const childIds = getDatumChildIds(childDatum);
|
|
463
|
+
ids = ids.concat(childIds);
|
|
464
|
+
const t = true;
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
return ids;
|
|
468
|
+
},
|
|
469
|
+
datumContainsSelection = (datum) => {
|
|
470
|
+
if (_.isEmpty(selection)) {
|
|
471
|
+
return false;
|
|
472
|
+
}
|
|
473
|
+
const
|
|
474
|
+
selectionIds = _.map(selection, (item) => item.id),
|
|
475
|
+
datumIds = getDatumChildIds(datum),
|
|
476
|
+
intersection = selectionIds.filter(x => datumIds.includes(x));
|
|
477
|
+
|
|
478
|
+
return !_.isEmpty(intersection);
|
|
479
|
+
},
|
|
487
480
|
|
|
488
481
|
// Button handlers
|
|
489
482
|
onToggle = (datum) => {
|
|
483
|
+
if (datum.isLoading) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
|
|
490
487
|
datum.isExpanded = !datum.isExpanded;
|
|
491
|
-
forceUpdate();
|
|
492
488
|
|
|
493
|
-
if (datum.item?.
|
|
489
|
+
if (datum.isExpanded && datum.item.repository?.isRemote && datum.item.hasChildren && !datum.item.areChildrenLoaded) {
|
|
494
490
|
loadChildren(datum, 1);
|
|
491
|
+
return;
|
|
495
492
|
}
|
|
493
|
+
|
|
494
|
+
if (!datum.isExpanded && datumContainsSelection(datum)) {
|
|
495
|
+
deselectAll();
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
forceUpdate();
|
|
496
499
|
},
|
|
497
500
|
loadChildren = async (datum, depth) => {
|
|
498
|
-
//
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
// Show loading indicator (red bar at top? Spinner underneath current node?)
|
|
502
|
-
|
|
501
|
+
// Show loading indicator (spinner underneath current node?)
|
|
502
|
+
datum.isLoading = true;
|
|
503
|
+
forceUpdate();
|
|
503
504
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
505
|
+
try {
|
|
506
|
+
|
|
507
|
+
const children = await datum.item.loadChildren(1);
|
|
508
|
+
const tnd = buildTreeNodeData(children);
|
|
509
|
+
datum.children = tnd;
|
|
510
|
+
|
|
511
|
+
} catch (err) {
|
|
512
|
+
// TODO: how do I handle errors?
|
|
513
|
+
// Color parent node red
|
|
514
|
+
// Modal alert box?
|
|
515
|
+
// Inline error msg? I'm concerned about modals not stacking correctly, but if we put it inline, it'll work.
|
|
516
|
+
datum.isExpanded = false;
|
|
517
|
+
}
|
|
508
518
|
|
|
509
519
|
// Hide loading indicator
|
|
510
|
-
|
|
520
|
+
datum.isLoading = false;
|
|
521
|
+
forceUpdate();
|
|
511
522
|
},
|
|
512
523
|
onCollapseAll = (setNewTreeNodeData = true) => {
|
|
513
524
|
// Go through whole tree and collapse all nodes
|
|
514
525
|
const newTreeNodeData = _.clone(treeNodeData);
|
|
515
|
-
|
|
516
|
-
// Recursive method to collapse all children
|
|
517
|
-
function collapseChildren(children) {
|
|
518
|
-
_.each(children, (child) => {
|
|
519
|
-
child.isExpanded = true;
|
|
520
|
-
if (!_.isEmpty(child.children)) {
|
|
521
|
-
collapseChildren(child.children);
|
|
522
|
-
}
|
|
523
|
-
});
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
collapseChildren(newTreeNodeData);
|
|
526
|
+
collapseNodes(newTreeNodeData);
|
|
527
527
|
|
|
528
528
|
if (setNewTreeNodeData) {
|
|
529
529
|
setTreeNodeData(newTreeNodeData);
|
|
530
530
|
}
|
|
531
531
|
return newTreeNodeData;
|
|
532
532
|
},
|
|
533
|
+
collapseNodes = (nodes) => {
|
|
534
|
+
_.each(nodes, (node) => {
|
|
535
|
+
node.isExpanded = false;
|
|
536
|
+
if (!_.isEmpty(node.children)) {
|
|
537
|
+
collapseNodes(node.children);
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
},
|
|
533
541
|
onSearchTree = async (value) => {
|
|
534
542
|
|
|
535
543
|
let found = [];
|
|
536
544
|
if (Repository?.isRemote) {
|
|
537
545
|
// Search tree on server
|
|
538
|
-
found = await Repository.
|
|
546
|
+
found = await Repository.searchNodes(value);
|
|
539
547
|
} else {
|
|
540
548
|
// Search local tree data
|
|
541
549
|
found = findTreeNodesByText(value);
|
|
542
550
|
}
|
|
543
551
|
|
|
544
|
-
|
|
545
552
|
const isMultipleHits = found.length > 1;
|
|
546
553
|
let path = '';
|
|
547
554
|
let searchFormData = [];
|
|
548
555
|
|
|
549
|
-
if (
|
|
550
|
-
|
|
551
|
-
// 'found' is the results from the server. Use these to show the modal and choose which node you want to select
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
} else {
|
|
557
|
-
// Search local tree data
|
|
558
|
-
found = findTreeNodesByText(value);
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
// TODO: create searchFormData based on 'found' array
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
setSearchFormData(searchFormData);
|
|
568
|
-
setIsSearchModalShown(true);
|
|
569
|
-
|
|
570
|
-
} else {
|
|
571
|
-
// Expand that one path immediately
|
|
556
|
+
if (!isMultipleHits) {
|
|
557
|
+
path = found[0].path;
|
|
572
558
|
expandPath(path);
|
|
559
|
+
return;
|
|
573
560
|
}
|
|
561
|
+
|
|
562
|
+
setSearchFormData(searchFormData);
|
|
563
|
+
setIsSearchModalShown(true);
|
|
574
564
|
},
|
|
575
565
|
findTreeNodesByText = (text) => {
|
|
576
566
|
// Helper for onSearchTree
|
|
@@ -625,14 +615,12 @@ export function Tree(props) {
|
|
|
625
615
|
return nodes.join('/');
|
|
626
616
|
|
|
627
617
|
},
|
|
628
|
-
expandPath = (path) => {
|
|
618
|
+
expandPath = async (path) => {
|
|
629
619
|
// Helper for onSearchTree
|
|
630
620
|
|
|
631
|
-
//
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
let newTreeNodeData = collapseAll(false); // false = don't set new treeNodeData
|
|
621
|
+
// First, close thw whole tree.
|
|
622
|
+
let newTreeNodeData = _.clone(treeNodeData);
|
|
623
|
+
collapseNodes(newTreeNodeData);
|
|
636
624
|
|
|
637
625
|
// As it navigates down, it will expand the appropriate branches,
|
|
638
626
|
// and then finally highlight & select the node in question
|
|
@@ -650,6 +638,13 @@ export function Tree(props) {
|
|
|
650
638
|
currentDatum = _.find(currentLevelData, (treeNodeDatum) => {
|
|
651
639
|
return treeNodeDatum.item.id === id;
|
|
652
640
|
});
|
|
641
|
+
|
|
642
|
+
if (!currentDatum) {
|
|
643
|
+
// datum is not currently loaded, so load it
|
|
644
|
+
|
|
645
|
+
// LEFT OFF HERE
|
|
646
|
+
debugger;
|
|
647
|
+
}
|
|
653
648
|
|
|
654
649
|
currentNode = currentDatum.item;
|
|
655
650
|
|
|
@@ -935,7 +930,9 @@ export function Tree(props) {
|
|
|
935
930
|
|
|
936
931
|
let rootNodes;
|
|
937
932
|
if (Repository) {
|
|
938
|
-
|
|
933
|
+
if (!Repository.areRootNodesLoaded) {
|
|
934
|
+
rootNodes = await Repository.getRootNodes(1);
|
|
935
|
+
}
|
|
939
936
|
} else {
|
|
940
937
|
// TODO: Make this work for data array
|
|
941
938
|
|
|
@@ -945,7 +942,15 @@ export function Tree(props) {
|
|
|
945
942
|
setTreeNodeData(treeNodeData);
|
|
946
943
|
}
|
|
947
944
|
|
|
945
|
+
function reloadTreeData() {
|
|
946
|
+
Repository.areRootNodesLoaded = false;
|
|
947
|
+
return buildAndSetTreeNodeData();
|
|
948
|
+
}
|
|
949
|
+
|
|
948
950
|
if (!isReady) {
|
|
951
|
+
if (Repository) {
|
|
952
|
+
Repository.setBaseParams(extraParams);
|
|
953
|
+
}
|
|
949
954
|
(async () => {
|
|
950
955
|
await buildAndSetTreeNodeData();
|
|
951
956
|
setIsReady(true);
|
|
@@ -955,29 +960,18 @@ export function Tree(props) {
|
|
|
955
960
|
if (!Repository) {
|
|
956
961
|
return () => {};
|
|
957
962
|
}
|
|
958
|
-
|
|
963
|
+
|
|
959
964
|
// set up @onehat/data repository
|
|
960
965
|
const
|
|
961
966
|
setTrue = () => setIsLoading(true),
|
|
962
|
-
setFalse = () => setIsLoading(false)
|
|
963
|
-
|
|
964
|
-
if (!Repository.isAutoLoad) {
|
|
965
|
-
Repository.reload();
|
|
966
|
-
}
|
|
967
|
-
},
|
|
968
|
-
onChangeSorters = () => {
|
|
969
|
-
if (!Repository.isAutoLoad) {
|
|
970
|
-
Repository.reload();
|
|
971
|
-
}
|
|
972
|
-
};
|
|
973
|
-
|
|
967
|
+
setFalse = () => setIsLoading(false);
|
|
968
|
+
|
|
974
969
|
Repository.on('beforeLoad', setTrue);
|
|
975
970
|
Repository.on('load', setFalse);
|
|
976
971
|
Repository.ons(['changePage', 'changePageSize',], deselectAll);
|
|
977
972
|
Repository.ons(['changeData', 'change'], buildAndSetTreeNodeData);
|
|
978
|
-
Repository.on('changeFilters',
|
|
979
|
-
Repository.on('changeSorters',
|
|
980
|
-
|
|
973
|
+
Repository.on('changeFilters', reloadTreeData);
|
|
974
|
+
Repository.on('changeSorters', reloadTreeData);
|
|
981
975
|
|
|
982
976
|
return () => {
|
|
983
977
|
Repository.off('beforeLoad', setTrue);
|
|
@@ -1004,15 +998,13 @@ export function Tree(props) {
|
|
|
1004
998
|
}, [selectorId, selectorSelected]);
|
|
1005
999
|
|
|
1006
1000
|
const
|
|
1007
|
-
headerToolbarItemComponents = useMemo(() => getHeaderToolbarItems(), []),
|
|
1008
|
-
footerToolbarItemComponents = useMemo(() => getFooterToolbarItems(), [additionalToolbarButtons, isReorderMode]);
|
|
1001
|
+
headerToolbarItemComponents = useMemo(() => getHeaderToolbarItems(), [treeSearchValue, treeNodeData]),
|
|
1002
|
+
footerToolbarItemComponents = useMemo(() => getFooterToolbarItems(), [additionalToolbarButtons, isReorderMode, treeNodeData]);
|
|
1009
1003
|
|
|
1010
1004
|
if (!isReady) {
|
|
1011
1005
|
return null;
|
|
1012
1006
|
}
|
|
1013
|
-
|
|
1014
|
-
// Actual TreeNodes
|
|
1015
|
-
const treeNodes = renderAllTreeNodes();
|
|
1007
|
+
const treeNodes = renderTreeNodes(treeNodeData);
|
|
1016
1008
|
|
|
1017
1009
|
// headers & footers
|
|
1018
1010
|
let treeFooterComponent = null;
|
|
@@ -1031,21 +1023,21 @@ export function Tree(props) {
|
|
|
1031
1023
|
w="100%"
|
|
1032
1024
|
>
|
|
1033
1025
|
{topToolbar}
|
|
1034
|
-
{headerToolbarItemComponents}
|
|
1026
|
+
{headerToolbarItemComponents?.length && <Row>{headerToolbarItemComponents}</Row>}
|
|
1035
1027
|
|
|
1036
|
-
<Column w="100%" flex={1} borderTopWidth={isLoading ? 2 : 1} borderTopColor={isLoading ? '#f00' : 'trueGray.300'} onClick={() => {
|
|
1028
|
+
<Column w="100%" flex={1} p={2} borderTopWidth={isLoading ? 2 : 1} borderTopColor={isLoading ? '#f00' : 'trueGray.300'} onClick={() => {
|
|
1037
1029
|
if (!isReorderMode) {
|
|
1038
1030
|
deselectAll();
|
|
1039
1031
|
}
|
|
1040
1032
|
}}>
|
|
1041
|
-
{!treeNodes
|
|
1033
|
+
{!treeNodes?.length ? <NoRecordsFound text={noneFoundText} onRefresh={onRefresh} /> :
|
|
1042
1034
|
treeNodes}
|
|
1043
1035
|
</Column>
|
|
1044
1036
|
|
|
1045
1037
|
{treeFooterComponent}
|
|
1046
1038
|
</Column>
|
|
1047
1039
|
|
|
1048
|
-
<Modal
|
|
1040
|
+
{/* <Modal
|
|
1049
1041
|
isOpen={isSearchModalShown}
|
|
1050
1042
|
onClose={() => setIsSearchModalShown(false)}
|
|
1051
1043
|
>
|
|
@@ -1095,7 +1087,7 @@ export function Tree(props) {
|
|
|
1095
1087
|
}}
|
|
1096
1088
|
/>
|
|
1097
1089
|
</Column>
|
|
1098
|
-
</Modal>
|
|
1090
|
+
</Modal> */}
|
|
1099
1091
|
</>;
|
|
1100
1092
|
|
|
1101
1093
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { useState, useMemo, } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Box,
|
|
4
|
+
Icon,
|
|
4
5
|
Row,
|
|
6
|
+
Spinner,
|
|
5
7
|
Text,
|
|
6
8
|
} from 'native-base';
|
|
7
9
|
import {
|
|
@@ -16,24 +18,26 @@ import _ from 'lodash';
|
|
|
16
18
|
|
|
17
19
|
export default function TreeNode(props) {
|
|
18
20
|
const {
|
|
19
|
-
nodeProps,
|
|
21
|
+
nodeProps = {},
|
|
20
22
|
bg,
|
|
21
23
|
datum,
|
|
22
24
|
onToggle,
|
|
25
|
+
...propsToPass
|
|
23
26
|
} = props,
|
|
24
27
|
styles = UiGlobals.styles,
|
|
25
28
|
item = datum.item,
|
|
26
29
|
isPhantom = item.isPhantom,
|
|
27
30
|
isExpanded = datum.isExpanded,
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
isLoading = datum.isLoading,
|
|
32
|
+
hasChildren = item.hasChildren,
|
|
33
|
+
depth = item.depth,
|
|
30
34
|
text = datum.text,
|
|
31
35
|
iconCollapsed = datum.iconCollapsed,
|
|
32
36
|
iconExpanded = datum.iconExpanded,
|
|
33
37
|
iconLeaf = datum.iconLeaf,
|
|
34
38
|
hash = item?.hash || item;
|
|
35
39
|
|
|
36
|
-
const icon = hasChildren ? (isExpanded ?
|
|
40
|
+
const icon = hasChildren ? (isExpanded ? iconExpanded : iconCollapsed) : iconLeaf;
|
|
37
41
|
|
|
38
42
|
return useMemo(() => {
|
|
39
43
|
|
|
@@ -43,17 +47,14 @@ export default function TreeNode(props) {
|
|
|
43
47
|
{...nodeProps}
|
|
44
48
|
bg={bg}
|
|
45
49
|
key={hash}
|
|
46
|
-
pl={(depth * 10) + 'px'}
|
|
47
50
|
>
|
|
48
51
|
{isPhantom && <Box position="absolute" bg="#f00" h={2} w={2} t={0} l={0} />}
|
|
49
52
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
onPress={onToggle}
|
|
53
|
-
/>
|
|
53
|
+
{isLoading ?
|
|
54
|
+
<Spinner px={2} /> :
|
|
55
|
+
(hasChildren ? <IconButton icon={icon} onPress={() => onToggle(datum)} /> : <Icon as={icon} px={2} />)}
|
|
54
56
|
|
|
55
57
|
<Text
|
|
56
|
-
key={key}
|
|
57
58
|
overflow="hidden"
|
|
58
59
|
textOverflow="ellipsis"
|
|
59
60
|
alignSelf="center"
|
|
@@ -79,6 +80,7 @@ export default function TreeNode(props) {
|
|
|
79
80
|
text,
|
|
80
81
|
icon,
|
|
81
82
|
onToggle,
|
|
83
|
+
isLoading,
|
|
82
84
|
]);
|
|
83
85
|
}
|
|
84
86
|
|