@onehat/ui 0.3.264 → 0.3.265
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
|
@@ -17,7 +17,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
17
17
|
return (props) => {
|
|
18
18
|
|
|
19
19
|
if (props.secondaryDisableWithEditor) {
|
|
20
|
-
return <WrappedComponent {...props} />;
|
|
20
|
+
return <WrappedComponent {...props} isTree={isTree} />;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
let [secondaryEditorMode, secondarySetEditorMode] = useState(EDITOR_MODE__VIEW); // Can change below, so use 'let'
|
|
@@ -560,6 +560,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
560
560
|
secondaryDisableDuplicate={secondaryDisableDuplicate}
|
|
561
561
|
secondaryDisableView ={secondaryDisableView}
|
|
562
562
|
secondarySetSelection={secondarySetSelectionDecorated}
|
|
563
|
+
isTree={isTree}
|
|
563
564
|
/>;
|
|
564
565
|
};
|
|
565
566
|
}
|
|
@@ -15,7 +15,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
15
15
|
return (props) => {
|
|
16
16
|
|
|
17
17
|
if (props.disableWithEditor) {
|
|
18
|
-
return <WrappedComponent {...props} />;
|
|
18
|
+
return <WrappedComponent {...props} isTree={isTree} />;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
let [editorMode, setEditorMode] = useState(EDITOR_MODE__VIEW); // Can change below, so use 'let'
|
|
@@ -569,6 +569,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
569
569
|
disableDuplicate={disableDuplicate}
|
|
570
570
|
disableView ={disableView}
|
|
571
571
|
setSelection={setSelectionDecorated}
|
|
572
|
+
isTree={isTree}
|
|
572
573
|
/>;
|
|
573
574
|
};
|
|
574
575
|
}
|
|
@@ -51,6 +51,7 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
|
|
|
51
51
|
// for local use
|
|
52
52
|
isEditor = false,
|
|
53
53
|
isTree = false,
|
|
54
|
+
canDeleteRootNode = false,
|
|
54
55
|
isSideEditor = false,
|
|
55
56
|
canEditorViewOnly = false,
|
|
56
57
|
disableAdd = !isEditor,
|
|
@@ -192,6 +193,12 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
|
|
|
192
193
|
if (verifyCanDelete && !verifyCanDelete(selection)) {
|
|
193
194
|
isDisabled = true;
|
|
194
195
|
}
|
|
196
|
+
if (isTree) {
|
|
197
|
+
const isRootNode = !!_.find(selection, { isRoot: true, });
|
|
198
|
+
if (isRootNode && !canDeleteRootNode) {
|
|
199
|
+
isDisabled = true;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
195
202
|
break;
|
|
196
203
|
case 'view':
|
|
197
204
|
key = 'viewBtn';
|
|
@@ -41,6 +41,7 @@ import nbToRgb from '../../Functions/nbToRgb.js';
|
|
|
41
41
|
import TreeNode, { DraggableTreeNode } from './TreeNode.js';
|
|
42
42
|
import FormPanel from '../Panel/FormPanel.js';
|
|
43
43
|
import Input from '../Form/Field/Input.js';
|
|
44
|
+
import Xmark from '../Icons/Xmark.js';
|
|
44
45
|
import Dot from '../Icons/Dot.js';
|
|
45
46
|
import Collapse from '../Icons/Collapse.js';
|
|
46
47
|
import FolderClosed from '../Icons/FolderClosed.js';
|
|
@@ -53,7 +54,7 @@ import NoRecordsFound from '../Grid/NoRecordsFound.js';
|
|
|
53
54
|
import Toolbar from '../Toolbar/Toolbar.js';
|
|
54
55
|
import _ from 'lodash';
|
|
55
56
|
|
|
56
|
-
const DEPTH_INDENT_PX =
|
|
57
|
+
const DEPTH_INDENT_PX = 25;
|
|
57
58
|
|
|
58
59
|
function TreeComponent(props) {
|
|
59
60
|
const {
|
|
@@ -96,6 +97,14 @@ function TreeComponent(props) {
|
|
|
96
97
|
additionalToolbarButtons = [],
|
|
97
98
|
reload = null, // Whenever this value changes after initial render, the tree will reload from scratch
|
|
98
99
|
parentIdIx,
|
|
100
|
+
|
|
101
|
+
// withComponent
|
|
102
|
+
self,
|
|
103
|
+
|
|
104
|
+
// withAlert
|
|
105
|
+
alert,
|
|
106
|
+
confirm,
|
|
107
|
+
showInfo,
|
|
99
108
|
|
|
100
109
|
// withEditor
|
|
101
110
|
onAdd,
|
|
@@ -138,7 +147,7 @@ function TreeComponent(props) {
|
|
|
138
147
|
treeNodeData = useRef(),
|
|
139
148
|
[isReady, setIsReady] = useState(false),
|
|
140
149
|
[isLoading, setIsLoading] = useState(false),
|
|
141
|
-
[
|
|
150
|
+
[isModalShown, setIsModalShown] = useState(false),
|
|
142
151
|
[rowToDatumMap, setRowToDatumMap] = useState({}),
|
|
143
152
|
[searchResults, setSearchResults] = useState([]),
|
|
144
153
|
[searchFormData, setSearchFormData] = useState([]),
|
|
@@ -222,6 +231,10 @@ function TreeComponent(props) {
|
|
|
222
231
|
},
|
|
223
232
|
onBeforeAdd = async () => {
|
|
224
233
|
// Load children before adding the new node
|
|
234
|
+
if (_.isEmpty(selection)) {
|
|
235
|
+
alert('Please select a parent node first.')
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
225
238
|
const
|
|
226
239
|
parent = selection[0],
|
|
227
240
|
parentDatum = getNodeData(parent.id);
|
|
@@ -229,16 +242,17 @@ function TreeComponent(props) {
|
|
|
229
242
|
if (parent.hasChildren && !parent.areChildrenLoaded) {
|
|
230
243
|
await loadChildren(parentDatum);
|
|
231
244
|
}
|
|
245
|
+
if (!parentDatum.isExpanded) {
|
|
246
|
+
parentDatum.isExpanded = true;
|
|
247
|
+
}
|
|
248
|
+
forceUpdate();
|
|
232
249
|
},
|
|
233
|
-
onAfterAdd = async (
|
|
250
|
+
onAfterAdd = async (entities) => {
|
|
234
251
|
// Expand the parent before showing the new node
|
|
235
252
|
const
|
|
236
|
-
|
|
237
|
-
parentDatum = getNodeData(
|
|
253
|
+
entity = entities[0],
|
|
254
|
+
parentDatum = getNodeData(entity.parentId);
|
|
238
255
|
|
|
239
|
-
if (!parentDatum.isExpanded) {
|
|
240
|
-
parentDatum.isExpanded = true;
|
|
241
|
-
}
|
|
242
256
|
|
|
243
257
|
// Add the entity to the tree
|
|
244
258
|
const entityDatum = buildTreeNodeDatum(entity);
|
|
@@ -322,29 +336,43 @@ function TreeComponent(props) {
|
|
|
322
336
|
buildRowToDatumMap();
|
|
323
337
|
return newTreeNodeData;
|
|
324
338
|
},
|
|
325
|
-
onSearchTree = async (
|
|
339
|
+
onSearchTree = async (q) => {
|
|
326
340
|
let found = [];
|
|
341
|
+
if (q === '') {
|
|
342
|
+
setHighlitedDatum(null);
|
|
343
|
+
alert('Please enter a search query.');
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
327
347
|
if (Repository?.isRemote) {
|
|
328
348
|
// Search tree on server
|
|
329
|
-
found = await Repository.searchNodes(
|
|
349
|
+
found = await Repository.searchNodes(q);
|
|
330
350
|
} else {
|
|
331
351
|
// Search local tree data
|
|
332
|
-
found = findTreeNodesByText(
|
|
352
|
+
found = findTreeNodesByText(q);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (_.isEmpty(found)) {
|
|
356
|
+
deselectAll();
|
|
357
|
+
setHighlitedDatum(null);
|
|
358
|
+
alert('No matches found.');
|
|
359
|
+
return;
|
|
333
360
|
}
|
|
334
361
|
|
|
335
362
|
const isMultipleHits = found.length > 1;
|
|
336
363
|
if (!isMultipleHits) {
|
|
337
|
-
expandPath(found[0].path);
|
|
364
|
+
expandPath(found[0].path); // highlights and selects the last node in the path
|
|
338
365
|
return;
|
|
339
366
|
}
|
|
340
367
|
|
|
368
|
+
// Show modal so user can select which node to go to
|
|
341
369
|
const searchFormData = [];
|
|
342
370
|
_.each(found, (item) => {
|
|
343
371
|
searchFormData.push([item.id, getNodeText(item)]);
|
|
344
372
|
});
|
|
345
373
|
setSearchFormData(searchFormData);
|
|
346
374
|
setSearchResults(found);
|
|
347
|
-
|
|
375
|
+
setIsModalShown(true);
|
|
348
376
|
},
|
|
349
377
|
|
|
350
378
|
// utilities
|
|
@@ -677,7 +705,31 @@ function TreeComponent(props) {
|
|
|
677
705
|
|
|
678
706
|
// TODO: This will probably need different methods in web and mobile
|
|
679
707
|
|
|
680
|
-
|
|
708
|
+
// From Github Copliot:
|
|
709
|
+
// In React, if you want to scroll individual DOM nodes into view, you would typically assign a ref to each of them. However, managing a large number of refs can be cumbersome and may lead to performance issues.
|
|
710
|
+
// An alternative approach is to assign a unique id to each DOM node and use the document.getElementById(id).scrollIntoView() method to scroll to a specific node. This way, you don't need to manage a large number of refs.
|
|
711
|
+
// Here's an example:
|
|
712
|
+
// const MyComponent = () => {
|
|
713
|
+
// const scrollTo = (id) => {
|
|
714
|
+
// document.getElementById(id).scrollIntoView();
|
|
715
|
+
// };
|
|
716
|
+
// return (
|
|
717
|
+
// <div>
|
|
718
|
+
// {Array.from({ length: 100 }).map((_, index) => (
|
|
719
|
+
// <div id={`item-${index}`} key={index}>
|
|
720
|
+
// Item {index}
|
|
721
|
+
// </div>
|
|
722
|
+
// ))}
|
|
723
|
+
// <button onClick={() => scrollTo('item-50')}>Scroll to item 50</button>
|
|
724
|
+
// </div>
|
|
725
|
+
// );
|
|
726
|
+
// };
|
|
727
|
+
// In this example, we're creating 100 divs each with a unique id. We also have a button that, when clicked, scrolls to the div with the id 'item-50'.
|
|
728
|
+
// Please note that this approach uses the DOM API directly, which is generally discouraged in React. It's recommended to use refs when you need to interact with DOM nodes directly. However, in cases where you need to manage a large number of DOM nodes, using ids can be a more practical solution.
|
|
729
|
+
// Also, keep in mind that document.getElementById(id).scrollIntoView() might not work as expected in all situations, especially in complex layouts or when using certain CSS properties. Always test your code thoroughly to make sure it works as expected.
|
|
730
|
+
|
|
731
|
+
// ... Not sure how to do this with NativeBase, as I've had trouble assigning IDs
|
|
732
|
+
// Maybe I first collapse the tree, then expand just the path?
|
|
681
733
|
},
|
|
682
734
|
|
|
683
735
|
// render
|
|
@@ -687,7 +739,7 @@ function TreeComponent(props) {
|
|
|
687
739
|
{
|
|
688
740
|
key: 'searchBtn',
|
|
689
741
|
text: 'Search tree',
|
|
690
|
-
handler: onSearchTree,
|
|
742
|
+
handler: () => onSearchTree(treeSearchValue),
|
|
691
743
|
icon: MagnifyingGlass,
|
|
692
744
|
isDisabled: !treeSearchValue.length,
|
|
693
745
|
},
|
|
@@ -704,32 +756,44 @@ function TreeComponent(props) {
|
|
|
704
756
|
key: 'reorderBtn',
|
|
705
757
|
text: (isDragMode ? 'Exit' : 'Enter') + ' reorder mode',
|
|
706
758
|
handler: () => {
|
|
707
|
-
setIsDragMode(!isDragMode)
|
|
759
|
+
setIsDragMode(!isDragMode);
|
|
708
760
|
},
|
|
709
761
|
icon: isDragMode ? NoReorderRows : ReorderRows,
|
|
710
762
|
isDisabled: false,
|
|
711
763
|
});
|
|
712
764
|
}
|
|
713
|
-
const items = _.map(buttons, getIconButtonFromConfig);
|
|
765
|
+
const items = _.map(buttons, (config, ix) => getIconButtonFromConfig(config, ix, self));
|
|
714
766
|
|
|
715
767
|
items.unshift(<Input // Add text input to beginning of header items
|
|
716
768
|
key="searchNodes"
|
|
717
769
|
flex={1}
|
|
718
770
|
placeholder="Find tree node"
|
|
719
771
|
onChangeText={(val) => setTreeSearchValue(val)}
|
|
720
|
-
onKeyPress={(e
|
|
772
|
+
onKeyPress={(e) => {
|
|
721
773
|
if (e.key === 'Enter') {
|
|
722
|
-
onSearchTree(
|
|
774
|
+
onSearchTree(treeSearchValue);
|
|
723
775
|
}
|
|
724
776
|
}}
|
|
725
777
|
value={treeSearchValue}
|
|
726
778
|
autoSubmit={false}
|
|
727
779
|
/>);
|
|
728
780
|
|
|
781
|
+
if (treeSearchValue.length) {
|
|
782
|
+
// Add 'X' button to clear search
|
|
783
|
+
items.unshift(getIconButtonFromConfig({
|
|
784
|
+
key: 'xBtn',
|
|
785
|
+
handler: () => {
|
|
786
|
+
setHighlitedDatum(null);
|
|
787
|
+
setTreeSearchValue('');
|
|
788
|
+
},
|
|
789
|
+
icon: Xmark,
|
|
790
|
+
}, 0, self));
|
|
791
|
+
}
|
|
792
|
+
|
|
729
793
|
return items;
|
|
730
794
|
},
|
|
731
795
|
getFooterToolbarItems = () => {
|
|
732
|
-
return _.map(additionalToolbarButtons, getIconButtonFromConfig);
|
|
796
|
+
return _.map(additionalToolbarButtons, (config, ix) => getIconButtonFromConfig(config, ix, self));
|
|
733
797
|
},
|
|
734
798
|
renderTreeNode = (datum) => {
|
|
735
799
|
if (!datum.isVisible) {
|
|
@@ -743,7 +807,6 @@ function TreeComponent(props) {
|
|
|
743
807
|
|
|
744
808
|
let nodeProps = getNodeProps ? getNodeProps(item) : {},
|
|
745
809
|
isSelected = isInSelection(item);
|
|
746
|
-
|
|
747
810
|
return <Pressable
|
|
748
811
|
// {...testProps(Repository ? Repository.schema.name + '-' + item.id : item.id)}
|
|
749
812
|
key={item.hash}
|
|
@@ -1115,6 +1178,7 @@ function TreeComponent(props) {
|
|
|
1115
1178
|
w="100%"
|
|
1116
1179
|
>
|
|
1117
1180
|
{topToolbar}
|
|
1181
|
+
|
|
1118
1182
|
{headerToolbarItemComponents?.length && <Row>{headerToolbarItemComponents}</Row>}
|
|
1119
1183
|
|
|
1120
1184
|
<Column
|
|
@@ -1129,16 +1193,18 @@ function TreeComponent(props) {
|
|
|
1129
1193
|
}
|
|
1130
1194
|
}}
|
|
1131
1195
|
>
|
|
1132
|
-
{!treeNodes?.length ?
|
|
1196
|
+
{!treeNodes?.length ?
|
|
1197
|
+
<NoRecordsFound text={noneFoundText} onRefresh={reloadTree} /> :
|
|
1133
1198
|
treeNodes}
|
|
1134
1199
|
</Column>
|
|
1135
1200
|
|
|
1136
1201
|
{treeFooterComponent}
|
|
1202
|
+
|
|
1137
1203
|
</Column>
|
|
1138
1204
|
|
|
1139
1205
|
<Modal
|
|
1140
|
-
isOpen={
|
|
1141
|
-
onClose={() =>
|
|
1206
|
+
isOpen={isModalShown}
|
|
1207
|
+
onClose={() => setIsModalShown(false)}
|
|
1142
1208
|
>
|
|
1143
1209
|
<Column bg="#fff" w={300}>
|
|
1144
1210
|
<FormPanel
|
|
@@ -1161,8 +1227,8 @@ function TreeComponent(props) {
|
|
|
1161
1227
|
},
|
|
1162
1228
|
]}
|
|
1163
1229
|
onCancel={(e) => {
|
|
1164
|
-
|
|
1165
|
-
|
|
1230
|
+
setHighlitedDatum(null);
|
|
1231
|
+
setIsModalShown(false);
|
|
1166
1232
|
}}
|
|
1167
1233
|
onSave={(data, e) => {
|
|
1168
1234
|
const
|
|
@@ -1173,7 +1239,7 @@ function TreeComponent(props) {
|
|
|
1173
1239
|
expandPath(path);
|
|
1174
1240
|
|
|
1175
1241
|
// Close the modal
|
|
1176
|
-
|
|
1242
|
+
setIsModalShown(false);
|
|
1177
1243
|
}}
|
|
1178
1244
|
/>
|
|
1179
1245
|
</Column>
|
package/src/Constants/Styles.js
CHANGED
|
@@ -116,7 +116,7 @@ const defaults = {
|
|
|
116
116
|
TREE_NODE_HOVER_BG: 'hover',
|
|
117
117
|
TREE_NODE_SELECTED_BG: 'selected',
|
|
118
118
|
TREE_NODE_SELECTED_HOVER_BG: 'selectedHover',
|
|
119
|
-
TREE_NODE_HIGHLIGHTED_BG: '
|
|
119
|
+
TREE_NODE_HIGHLIGHTED_BG: 'highlighted',
|
|
120
120
|
TOOLBAR_ITEMS_COLOR: 'trueGray.800',
|
|
121
121
|
TOOLBAR_ITEMS_DISABLED_COLOR: 'trueGray.400',
|
|
122
122
|
TOOLBAR_ITEMS_ICON_SIZE: 'sm',
|