@onehat/ui 0.4.68 → 0.4.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 +1 -1
- package/src/Components/Accordion/Accordion.js +1 -1
- package/src/Components/Form/Field/Json.js +1 -0
- package/src/Components/Form/Field/Tag/Tag.js +3 -3
- package/src/Components/Form/Form.js +1 -1
- package/src/Components/Grid/Grid.js +5 -4
- package/src/Components/Grid/GridHeaderRow.js +4 -4
- package/src/Components/Grid/GridRow.js +66 -19
- package/src/Components/Hoc/Secondary/withSecondaryEditor.js +1 -1
- package/src/Components/Hoc/Secondary/withSecondarySelection.js +2 -2
- package/src/Components/Hoc/withEditor.js +1 -1
- package/src/Components/Hoc/withFilters.js +6 -6
- package/src/Components/Hoc/withSelection.js +2 -2
- package/src/Components/Tab/TabBar.js +2 -2
- package/src/Components/Tree/Tree.js +4 -4
- package/src/Components/Tree/TreeNode.js +10 -1
- package/src/Hooks/useAsyncRenderers.js +53 -0
package/package.json
CHANGED
|
@@ -84,7 +84,7 @@ export default function Accordion(props) {
|
|
|
84
84
|
if (isActive) {
|
|
85
85
|
newActiveSections = _.without(activeSections, ix);
|
|
86
86
|
} else {
|
|
87
|
-
newActiveSections =
|
|
87
|
+
newActiveSections = [...activeSections]; // clone
|
|
88
88
|
newActiveSections.push(ix);
|
|
89
89
|
}
|
|
90
90
|
}
|
|
@@ -144,7 +144,7 @@ function TagComponent(props) {
|
|
|
144
144
|
|
|
145
145
|
|
|
146
146
|
// add new value
|
|
147
|
-
const newValue =
|
|
147
|
+
const newValue = [...value]; // clone, so we trigger a re-render
|
|
148
148
|
newValue.push({
|
|
149
149
|
id,
|
|
150
150
|
text: displayValue,
|
|
@@ -165,7 +165,7 @@ function TagComponent(props) {
|
|
|
165
165
|
const
|
|
166
166
|
entity = selection[0],
|
|
167
167
|
id = entity.id,
|
|
168
|
-
newValue =
|
|
168
|
+
newValue = [...valueRef.current]; // clone
|
|
169
169
|
newValue.push({
|
|
170
170
|
id,
|
|
171
171
|
text: entity.displayValue,
|
|
@@ -190,7 +190,7 @@ function TagComponent(props) {
|
|
|
190
190
|
return;
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
const newValue =
|
|
193
|
+
const newValue = [...valueRef.current]; // clone
|
|
194
194
|
newValue[ix] = {
|
|
195
195
|
id,
|
|
196
196
|
text: entity.displayValue,
|
|
@@ -1358,7 +1358,7 @@ function Form(props) {
|
|
|
1358
1358
|
icon={getEditorMode() === EDITOR_MODE__ADD ? Plus : FloppyDiskRegular}
|
|
1359
1359
|
isDisabled={isSaveDisabled}
|
|
1360
1360
|
className="text-white"
|
|
1361
|
-
text={getEditorMode() === EDITOR_MODE__ADD ? 'Add' : 'Save'}
|
|
1361
|
+
text={(getEditorMode() === EDITOR_MODE__ADD ? 'Add' : 'Save') + (props.record?.length > 1 ? ` (${props.record.length})` : '')}
|
|
1362
1362
|
/>}
|
|
1363
1363
|
|
|
1364
1364
|
{showSubmitBtn &&
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useState, useEffect, useRef, useMemo, useCallback, } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Box,
|
|
4
4
|
FlatList,
|
|
@@ -194,6 +194,7 @@ function GridComponent(props) {
|
|
|
194
194
|
onView,
|
|
195
195
|
onContextMenu,
|
|
196
196
|
isAdding,
|
|
197
|
+
isEditorViewOnly,
|
|
197
198
|
|
|
198
199
|
// withData
|
|
199
200
|
Repository,
|
|
@@ -266,7 +267,7 @@ function GridComponent(props) {
|
|
|
266
267
|
},
|
|
267
268
|
setLocalColumnsConfig = (config) => {
|
|
268
269
|
if (localColumnsConfigKey) {
|
|
269
|
-
const localConfig =
|
|
270
|
+
const localConfig = [...config]; // clone it so we don't alter the original
|
|
270
271
|
if (hasUnserializableColumns) {
|
|
271
272
|
// just save the data needed to later reconstruct the columns
|
|
272
273
|
const usedIds = [];
|
|
@@ -432,7 +433,7 @@ function GridComponent(props) {
|
|
|
432
433
|
} else {
|
|
433
434
|
let canDoEdit = false,
|
|
434
435
|
canDoView = false;
|
|
435
|
-
if (onEdit && canUser && canUser(EDIT) && (!canRecordBeEdited || canRecordBeEdited(selection)) && !props.disableEdit) {
|
|
436
|
+
if (onEdit && canUser && canUser(EDIT) && (!canRecordBeEdited || canRecordBeEdited(selection)) && !props.disableEdit && !isEditorViewOnly) {
|
|
436
437
|
canDoEdit = true;
|
|
437
438
|
} else
|
|
438
439
|
if (onView && canUser && canUser(VIEW) && !props.disableView) {
|
|
@@ -1166,7 +1167,7 @@ function GridComponent(props) {
|
|
|
1166
1167
|
|
|
1167
1168
|
// Actual data to show in the grid
|
|
1168
1169
|
const entities = Repository ? (Repository.isRemote ? Repository.entities : Repository.getEntitiesOnPage()) : data;
|
|
1169
|
-
let rowData =
|
|
1170
|
+
let rowData = [...entities]; // don't use the original array, make a new one so alterations to it are temporary
|
|
1170
1171
|
if (showHeaders) {
|
|
1171
1172
|
rowData.unshift({ id: 'headerRow' });
|
|
1172
1173
|
}
|
|
@@ -86,7 +86,7 @@ export default function GridHeaderRow(props) {
|
|
|
86
86
|
if (isDragging) {
|
|
87
87
|
return;
|
|
88
88
|
}
|
|
89
|
-
const columnsConfig =
|
|
89
|
+
const columnsConfig = [...localColumnsConfig]; // work with a copy, so that setter forces rerender
|
|
90
90
|
columnsConfig[ix].isOver = true;
|
|
91
91
|
setLocalColumnsConfig(columnsConfig);
|
|
92
92
|
},
|
|
@@ -94,7 +94,7 @@ export default function GridHeaderRow(props) {
|
|
|
94
94
|
if (isDragging) {
|
|
95
95
|
return;
|
|
96
96
|
}
|
|
97
|
-
const columnsConfig =
|
|
97
|
+
const columnsConfig = [...localColumnsConfig]; // work with a copy, so that setter forces rerender
|
|
98
98
|
columnsConfig[ix].isOver = false;
|
|
99
99
|
setLocalColumnsConfig(columnsConfig);
|
|
100
100
|
},
|
|
@@ -240,7 +240,7 @@ export default function GridHeaderRow(props) {
|
|
|
240
240
|
setDragColumnSlot({ ix: newIx, marker, });
|
|
241
241
|
},
|
|
242
242
|
onColumnReorderDragStop = (delta, e, config) => {
|
|
243
|
-
const columnsConfig =
|
|
243
|
+
const columnsConfig = [...localColumnsConfig]; // work with a copy, so that setter forces rerender
|
|
244
244
|
|
|
245
245
|
_.pull(columnsConfig, config);
|
|
246
246
|
|
|
@@ -256,7 +256,7 @@ export default function GridHeaderRow(props) {
|
|
|
256
256
|
setDragColumnSlot(null);
|
|
257
257
|
},
|
|
258
258
|
onColumnResize = (delta, e, node, config) => {
|
|
259
|
-
const columnsConfig =
|
|
259
|
+
const columnsConfig = [...localColumnsConfig]; // work with a copy, so that setter forces rerender
|
|
260
260
|
if (config.w) {
|
|
261
261
|
config.w = Math.round(config.w + delta);
|
|
262
262
|
} else if (config.flex) {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { useMemo, useEffect, } from 'react';
|
|
1
|
+
import { useMemo, useState, useEffect, } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Box,
|
|
4
4
|
HStack,
|
|
5
5
|
HStackNative,
|
|
6
6
|
Icon,
|
|
7
|
+
Text,
|
|
7
8
|
TextNative,
|
|
8
9
|
} from '@project-components/Gluestack';
|
|
9
10
|
import {
|
|
@@ -11,17 +12,28 @@ import {
|
|
|
11
12
|
UI_MODE_NATIVE,
|
|
12
13
|
CURRENT_MODE,
|
|
13
14
|
} from '../../Constants/UiModes.js';
|
|
14
|
-
import { getEmptyImage } from 'react-dnd-html5-backend';
|
|
15
15
|
import * as colourMixer from '@k-renwick/colour-mixer';
|
|
16
16
|
import getComponentFromType from '../../Functions/getComponentFromType.js';
|
|
17
17
|
import UiGlobals from '../../UiGlobals.js';
|
|
18
18
|
import { withDragSource, withDropTarget } from '../Hoc/withDnd.js';
|
|
19
19
|
import testProps from '../../Functions/testProps.js';
|
|
20
|
+
import Loading from '../Messages/Loading.js';
|
|
20
21
|
import AngleRight from '../Icons/AngleRight.js';
|
|
21
22
|
import RowDragHandle from './RowDragHandle.js';
|
|
22
23
|
import RowSelectHandle from './RowSelectHandle.js';
|
|
24
|
+
import useAsyncRenderers from '../../Hooks/useAsyncRenderers.js';
|
|
23
25
|
import _ from 'lodash';
|
|
24
26
|
|
|
27
|
+
// Conditional import for web only
|
|
28
|
+
let getEmptyImage;
|
|
29
|
+
if (CURRENT_MODE === UI_MODE_WEB) {
|
|
30
|
+
import('react-dnd-html5-backend').then((module) => {
|
|
31
|
+
getEmptyImage = module.getEmptyImage;
|
|
32
|
+
}).catch(() => {
|
|
33
|
+
getEmptyImage = null;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
25
37
|
// This was broken out from Grid simply so we can memoize it
|
|
26
38
|
|
|
27
39
|
function GridRow(props) {
|
|
@@ -55,7 +67,11 @@ function GridRow(props) {
|
|
|
55
67
|
dropTargetRef,
|
|
56
68
|
...propsToPass
|
|
57
69
|
} = props,
|
|
58
|
-
styles = UiGlobals.styles
|
|
70
|
+
styles = UiGlobals.styles,
|
|
71
|
+
{
|
|
72
|
+
results: asyncResults,
|
|
73
|
+
loading: asyncLoading,
|
|
74
|
+
} = useAsyncRenderers(columnsConfig, item);
|
|
59
75
|
|
|
60
76
|
if (item.isDestroyed) {
|
|
61
77
|
return null;
|
|
@@ -150,21 +166,25 @@ function GridRow(props) {
|
|
|
150
166
|
}
|
|
151
167
|
if (_.isPlainObject(config)) {
|
|
152
168
|
if (config.renderer) {
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
169
|
+
const
|
|
170
|
+
asyncResult = asyncResults.get(key),
|
|
171
|
+
isLoading = asyncLoading.has(key),
|
|
172
|
+
extraProps = _.omit(config, [
|
|
173
|
+
'header',
|
|
174
|
+
'fieldName',
|
|
175
|
+
'type',
|
|
176
|
+
'isEditable',
|
|
177
|
+
'editor',
|
|
178
|
+
'format',
|
|
179
|
+
'renderer',
|
|
180
|
+
'isAsync',
|
|
181
|
+
'isReorderable',
|
|
182
|
+
'isResizable',
|
|
183
|
+
'isSortable',
|
|
184
|
+
'w',
|
|
185
|
+
'flex',
|
|
186
|
+
'isOver',
|
|
187
|
+
]);
|
|
168
188
|
|
|
169
189
|
if (!extraProps._web) {
|
|
170
190
|
extraProps._web = {};
|
|
@@ -176,6 +196,31 @@ function GridRow(props) {
|
|
|
176
196
|
// userSelect: 'none',
|
|
177
197
|
};
|
|
178
198
|
|
|
199
|
+
let content = null;
|
|
200
|
+
if (config.isAsync) {
|
|
201
|
+
// Async renderer
|
|
202
|
+
if (isLoading) {
|
|
203
|
+
content = <Loading />;
|
|
204
|
+
} else if (asyncResult) {
|
|
205
|
+
if (asyncResult.error) {
|
|
206
|
+
content = <Text>Render Error: {asyncResult.error.message || String(asyncResult.error)}</Text>;
|
|
207
|
+
} else {
|
|
208
|
+
content = asyncResult.result;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
} else {
|
|
212
|
+
// Synchronous renderer
|
|
213
|
+
try {
|
|
214
|
+
const result = config.renderer(item);
|
|
215
|
+
if (result && typeof result.then === 'function') {
|
|
216
|
+
content = <Text>Error: Async renderer not properly configured</Text>;
|
|
217
|
+
} else {
|
|
218
|
+
content = result;
|
|
219
|
+
}
|
|
220
|
+
} catch (error) {
|
|
221
|
+
content = <Text>Render Error: {error}</Text>;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
179
224
|
return <HStackNative
|
|
180
225
|
key={key}
|
|
181
226
|
{...testProps('rendererCol-' + key)}
|
|
@@ -183,7 +228,7 @@ function GridRow(props) {
|
|
|
183
228
|
{...propsToPass}
|
|
184
229
|
{...extraProps}
|
|
185
230
|
style={colStyle}
|
|
186
|
-
>{
|
|
231
|
+
>{content}</HStackNative>;
|
|
187
232
|
}
|
|
188
233
|
if (config.fieldName) {
|
|
189
234
|
|
|
@@ -381,6 +426,8 @@ function GridRow(props) {
|
|
|
381
426
|
>{rowContents}</HStackNative>;
|
|
382
427
|
}, [
|
|
383
428
|
columnsConfig,
|
|
429
|
+
asyncResults,
|
|
430
|
+
asyncLoading,
|
|
384
431
|
columnProps,
|
|
385
432
|
fields,
|
|
386
433
|
rowProps,
|
|
@@ -169,7 +169,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
169
169
|
if (secondaryDefaultValues) {
|
|
170
170
|
_.merge(defaultValuesToUse, secondaryDefaultValues);
|
|
171
171
|
}
|
|
172
|
-
addValues =
|
|
172
|
+
addValues = [...defaultValuesToUse];
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
if (secondarySelectorId && !_.isEmpty(secondarySelectorSelected)) {
|
|
@@ -111,7 +111,7 @@ export default function withSelection(WrappedComponent) {
|
|
|
111
111
|
}
|
|
112
112
|
},
|
|
113
113
|
secondaryAddToSelection = (item) => {
|
|
114
|
-
const newSelection =
|
|
114
|
+
const newSelection = [...secondaryGetSelection()]; // so we get a new object, so descendants rerender
|
|
115
115
|
newSelection.push(item);
|
|
116
116
|
secondarySetSelection(newSelection);
|
|
117
117
|
},
|
|
@@ -175,7 +175,7 @@ export default function withSelection(WrappedComponent) {
|
|
|
175
175
|
const
|
|
176
176
|
currentSelectionLength = secondaryGetSelection().length,
|
|
177
177
|
index = getIndexOfSelectedItem(item);
|
|
178
|
-
let newSelection =
|
|
178
|
+
let newSelection = [...secondaryGetSelection()]; // so we get a new object, so descendants rerender
|
|
179
179
|
|
|
180
180
|
if (currentSelectionLength) {
|
|
181
181
|
const { items, max, min, } = getMaxMinSelectionIndices();
|
|
@@ -179,7 +179,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
179
179
|
if (defaultValues) {
|
|
180
180
|
_.merge(defaultValuesToUse, defaultValues);
|
|
181
181
|
}
|
|
182
|
-
addValues =
|
|
182
|
+
addValues = [...defaultValuesToUse];
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
if (selectorId && !_.isEmpty(selectorSelected)) {
|
|
@@ -228,7 +228,7 @@ export default function withFilters(WrappedComponent) {
|
|
|
228
228
|
filterById = (id, cb) => {
|
|
229
229
|
onClearFilters();
|
|
230
230
|
filterCallbackRef.current = cb; // store the callback, so we can call it the next time this HOC renders with new filters
|
|
231
|
-
const newFilters =
|
|
231
|
+
const newFilters = [...filters];
|
|
232
232
|
_.remove(newFilters, (filter) => {
|
|
233
233
|
return filter.field === 'q';
|
|
234
234
|
});
|
|
@@ -410,8 +410,8 @@ export default function withFilters(WrappedComponent) {
|
|
|
410
410
|
data,
|
|
411
411
|
onChange: (value) => {
|
|
412
412
|
const
|
|
413
|
-
newFilters =
|
|
414
|
-
newSlots =
|
|
413
|
+
newFilters = [...modalFilters],
|
|
414
|
+
newSlots = [...modalSlots],
|
|
415
415
|
i = ix;//searchAllText ? ixPlusOne : ix; // compensate for 'q' filter's possible presence
|
|
416
416
|
|
|
417
417
|
newFilters[i] = getFormattedFilter(value);
|
|
@@ -447,7 +447,7 @@ export default function withFilters(WrappedComponent) {
|
|
|
447
447
|
if (!canAddSlot) {
|
|
448
448
|
return;
|
|
449
449
|
}
|
|
450
|
-
const newSlots =
|
|
450
|
+
const newSlots = [...modalSlots];
|
|
451
451
|
newSlots.push(null);
|
|
452
452
|
rebuildModalBody(modalFilters, newSlots);
|
|
453
453
|
},
|
|
@@ -456,8 +456,8 @@ export default function withFilters(WrappedComponent) {
|
|
|
456
456
|
return;
|
|
457
457
|
}
|
|
458
458
|
const
|
|
459
|
-
newFilters =
|
|
460
|
-
newSlots =
|
|
459
|
+
newFilters = [...modalFilters],
|
|
460
|
+
newSlots = [...modalSlots];
|
|
461
461
|
newFilters.pop();
|
|
462
462
|
newSlots.pop();
|
|
463
463
|
rebuildModalBody(newFilters, newSlots);
|
|
@@ -109,7 +109,7 @@ export default function withSelection(WrappedComponent) {
|
|
|
109
109
|
}
|
|
110
110
|
},
|
|
111
111
|
addToSelection = (item) => {
|
|
112
|
-
const newSelection =
|
|
112
|
+
const newSelection = [...getSelection()]; // so we get a new object, so descendants rerender
|
|
113
113
|
newSelection.push(item);
|
|
114
114
|
setSelection(newSelection);
|
|
115
115
|
},
|
|
@@ -175,7 +175,7 @@ export default function withSelection(WrappedComponent) {
|
|
|
175
175
|
const
|
|
176
176
|
currentSelectionLength = getSelection().length,
|
|
177
177
|
index = getIndexOfSelectedItem(item);
|
|
178
|
-
let newSelection =
|
|
178
|
+
let newSelection = [...getSelection()]; // so we get a new object, so descendants rerender
|
|
179
179
|
|
|
180
180
|
if (currentSelectionLength) {
|
|
181
181
|
const { items, max, min, } = getMaxMinSelectionIndices();
|
|
@@ -244,7 +244,7 @@ function TabBar(props) {
|
|
|
244
244
|
const
|
|
245
245
|
isCurrentTab = ix === getCurrentTab(),
|
|
246
246
|
useIconTab = (isCollapsed || !tab.title),
|
|
247
|
-
tabIcon = tab._icon ?
|
|
247
|
+
tabIcon = tab._icon ? [...tab._icon] : {};
|
|
248
248
|
if (tabIcon.as && _.isString(tabIcon.as)) {
|
|
249
249
|
const Type = getComponentFromType(tabIcon.as);
|
|
250
250
|
if (Type) {
|
|
@@ -307,7 +307,7 @@ function TabBar(props) {
|
|
|
307
307
|
|
|
308
308
|
const
|
|
309
309
|
useIconTab = (isCollapsed || !additionalButton.text),
|
|
310
|
-
additionalButtonIcon =
|
|
310
|
+
additionalButtonIcon = [...additionalButton._icon];
|
|
311
311
|
|
|
312
312
|
if (additionalButtonIcon.as && _.isString(additionalButtonIcon.as)) {
|
|
313
313
|
const Type = getComponentFromType(additionalButtonIcon.as);
|
|
@@ -384,13 +384,13 @@ function TreeComponent(props) {
|
|
|
384
384
|
forceUpdate();
|
|
385
385
|
},
|
|
386
386
|
onCollapseAll = () => {
|
|
387
|
-
const newTreeNodeData =
|
|
387
|
+
const newTreeNodeData = [...getTreeNodeData()];
|
|
388
388
|
collapseNodes(newTreeNodeData);
|
|
389
389
|
setTreeNodeData(newTreeNodeData);
|
|
390
390
|
},
|
|
391
391
|
onExpandAll = () => {
|
|
392
392
|
confirm('Are you sure you want to expand the whole tree? This may take a while.', async () => {
|
|
393
|
-
const newTreeNodeData =
|
|
393
|
+
const newTreeNodeData = [...getTreeNodeData()];
|
|
394
394
|
await expandNodes(newTreeNodeData);
|
|
395
395
|
setTreeNodeData(newTreeNodeData);
|
|
396
396
|
});
|
|
@@ -678,7 +678,7 @@ function TreeComponent(props) {
|
|
|
678
678
|
// NOTE: This is only for 'data', not for Repositories!
|
|
679
679
|
// 'data' is essentially an Adjacency List, not a ClosureTable.
|
|
680
680
|
|
|
681
|
-
const clonedData =
|
|
681
|
+
const clonedData = [...data];
|
|
682
682
|
|
|
683
683
|
// Reset all parent/child relationships
|
|
684
684
|
_.each(clonedData, (treeNode) => {
|
|
@@ -831,7 +831,7 @@ function TreeComponent(props) {
|
|
|
831
831
|
},
|
|
832
832
|
expandPath = async (cPath, highlight = true) => {
|
|
833
833
|
// First, close the whole tree.
|
|
834
|
-
let newTreeNodeData =
|
|
834
|
+
let newTreeNodeData = [...getTreeNodeData()];
|
|
835
835
|
collapseNodes(newTreeNodeData);
|
|
836
836
|
|
|
837
837
|
// As it navigates down, it will expand the appropriate branches,
|
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
UI_MODE_WEB,
|
|
12
12
|
CURRENT_MODE,
|
|
13
13
|
} from '../../Constants/UiModes.js';
|
|
14
|
-
import { getEmptyImage } from 'react-dnd-html5-backend';
|
|
15
14
|
import UiGlobals from '../../UiGlobals.js';
|
|
16
15
|
import withDraggable from '../Hoc/withDraggable.js';
|
|
17
16
|
import IconButton from '../Buttons/IconButton.js';
|
|
@@ -22,6 +21,16 @@ import ChevronRight from '../Icons/ChevronRight.js';
|
|
|
22
21
|
import ChevronDown from '../Icons/ChevronDown.js';
|
|
23
22
|
import _ from 'lodash';
|
|
24
23
|
|
|
24
|
+
// Conditional import for web only
|
|
25
|
+
let getEmptyImage;
|
|
26
|
+
if (CURRENT_MODE === UI_MODE_WEB) {
|
|
27
|
+
import('react-dnd-html5-backend').then((module) => {
|
|
28
|
+
getEmptyImage = module.getEmptyImage;
|
|
29
|
+
}).catch(() => {
|
|
30
|
+
getEmptyImage = null;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
25
34
|
// This was broken out from Tree simply so we can memoize it
|
|
26
35
|
|
|
27
36
|
export default function TreeNode(props) {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { useState, useEffect, } from 'react';
|
|
2
|
+
|
|
3
|
+
export default function useAsyncRenderers(columnsConfig, item) {
|
|
4
|
+
const
|
|
5
|
+
[results, setResults] = useState(new Map()),
|
|
6
|
+
[loading, setLoading] = useState(new Set());
|
|
7
|
+
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
const asyncConfigs = columnsConfig.filter(config =>
|
|
10
|
+
config.renderer && typeof config.renderer === 'function'
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
if (asyncConfigs.length === 0) {
|
|
14
|
+
setResults(new Map());
|
|
15
|
+
setLoading(new Set());
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const newLoading = new Set();
|
|
20
|
+
asyncConfigs.forEach((config, index) => {
|
|
21
|
+
newLoading.add(index);
|
|
22
|
+
});
|
|
23
|
+
setLoading(newLoading);
|
|
24
|
+
|
|
25
|
+
const promises = asyncConfigs.map(async (config, configIndex) => {
|
|
26
|
+
try {
|
|
27
|
+
const result = await config.renderer(item);
|
|
28
|
+
return { configIndex, result, error: null };
|
|
29
|
+
} catch (error) {
|
|
30
|
+
return { configIndex, result: null, error };
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
Promise.allSettled(promises).then(settled => {
|
|
35
|
+
const
|
|
36
|
+
newResults = new Map(),
|
|
37
|
+
newLoading = new Set();
|
|
38
|
+
|
|
39
|
+
settled.forEach((outcome, index) => {
|
|
40
|
+
if (outcome.status === 'fulfilled') {
|
|
41
|
+
const { configIndex, result, error } = outcome.value;
|
|
42
|
+
newResults.set(configIndex, { result, error });
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
setResults(newResults);
|
|
47
|
+
setLoading(newLoading);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
}, [columnsConfig, item]);
|
|
51
|
+
|
|
52
|
+
return { results, loading };
|
|
53
|
+
}
|