@onehat/ui 0.3.125 → 0.3.128

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.3.125",
3
+ "version": "0.3.128",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -474,7 +474,9 @@ function Form(props) {
474
474
  editorTypeProps.selectorId = selectorId;
475
475
  }
476
476
  if (propsToPass.selectorId || editorTypeProps.selectorId) { // editorTypeProps.selectorId causes just this one field to use selectorId
477
- editorTypeProps.selectorSelected = record;
477
+ if (_.isNil(propsToPass.selectorSelected)) {
478
+ editorTypeProps.selectorSelected = record;
479
+ }
478
480
  }
479
481
  let dynamicProps = {};
480
482
  if (getDynamicProps) {
@@ -0,0 +1,117 @@
1
+ import { useState, useEffect, } from 'react';
2
+ import oneHatData from '@onehat/data';
3
+ import _ from 'lodash';
4
+
5
+ // NOTE: This is a modified version of @onehat/ui/src/Hoc/withData
6
+ // This HOC will eventually get out of sync with that one, and may need to be updated.
7
+
8
+ export default function withSecondaryData(WrappedComponent) {
9
+ return (props) => {
10
+
11
+ if (props.secondaryDisableWithData) {
12
+ return <WrappedComponent {...props} />;
13
+ }
14
+
15
+ const
16
+ {
17
+ // For @onehat/data repositories
18
+ SecondaryRepository,
19
+ setSecondaryRepository,
20
+ uniqueSecondaryRepository = false,
21
+ secondaryModel,
22
+ secondaryAutoLoad, // bool
23
+ secondaryPageSize,
24
+ secondaryBaseParams,
25
+
26
+ // For plain JS data
27
+ secondaryData,
28
+ secondaryFields = ['id', 'value'],
29
+ secondaryIdField = 'id',
30
+ secondaryDisplayField = 'value',
31
+ secondaryIdIx,
32
+ secondaryDisplayIx,
33
+
34
+ // withComponent
35
+ self,
36
+ } = props,
37
+ propsToPass = _.omit(props, ['secondaryModel']), // passing 'secondaryModel' would mess things up if withData gets called twice (e.g. withData(...withData(...)) ), as we'd be trying to recreate SecondaryRepository twice
38
+ localIdIx = secondaryIdIx || (secondaryFields && secondaryIdField ? secondaryFields.indexOf(secondaryIdField) : null),
39
+ localDisplayIx = secondaryDisplayIx || (secondaryFields && secondaryDisplayField ? secondaryFields?.indexOf(secondaryDisplayField) : null),
40
+ [LocalSecondaryRepository, setLocalSecondaryRepository] = useState(SecondaryRepository || null), // simply pass on SecondaryRepository if it's already supplied
41
+ [isReady, setIsReady] = useState(!!LocalSecondaryRepository || !!secondaryData); // It's already ready if a LocalSecondaryRepository or secondaryData array is already set. Otherwise, we need to create the repository
42
+
43
+ // Create LocalSecondaryRepository
44
+ // If SecondaryRepository was submitted to this withData(), the useEffect has no effect.
45
+ // If it's empty, it tries to create a LocalSecondaryRepository
46
+ useEffect(() => {
47
+ if (!!LocalSecondaryRepository || !!secondaryData) {
48
+ return () => {};
49
+ }
50
+
51
+ let repositoryId;
52
+
53
+ (async () => {
54
+ let SecondaryRepository;
55
+ if (uniqueSecondaryRepository) {
56
+ const schema = oneHatData.getSchema(secondaryModel);
57
+ SecondaryRepository = await oneHatData.createRepository({ schema });
58
+ repositoryId = SecondaryRepository.id;
59
+ } else {
60
+ SecondaryRepository = oneHatData.getRepository(secondaryModel);
61
+ }
62
+
63
+ if (secondaryPageSize) {
64
+ SecondaryRepository.setPageSize(secondaryPageSize);
65
+ }
66
+
67
+ if (secondaryBaseParams) {
68
+ SecondaryRepository.setBaseParams(secondaryBaseParams);
69
+ }
70
+
71
+
72
+ if (SecondaryRepository && !SecondaryRepository.isLoaded && SecondaryRepository.isRemote && !SecondaryRepository.isAutoLoad && !SecondaryRepository.isLoading) {
73
+ let doAutoLoad = SecondaryRepository.autoLoad;
74
+ if (!_.isNil(secondaryAutoLoad)) { // prop can override schema setting for secondaryAutoLoad
75
+ doAutoLoad = secondaryAutoLoad;
76
+ }
77
+ if (doAutoLoad) {
78
+ await SecondaryRepository.load();
79
+ }
80
+ }
81
+
82
+ setLocalSecondaryRepository(SecondaryRepository);
83
+ if (setSecondaryRepository) { // pass it on up to higher components
84
+ setSecondaryRepository(SecondaryRepository);
85
+ }
86
+ if (self) {
87
+ self.repository = SecondaryRepository;
88
+ }
89
+ setIsReady(true);
90
+ })();
91
+
92
+ return () => {
93
+ if (repositoryId) {
94
+ oneHatData.deleteRepository(repositoryId);
95
+ }
96
+ }
97
+
98
+ }, []);
99
+
100
+ if (!isReady) {
101
+ return null;
102
+ }
103
+
104
+ return <WrappedComponent
105
+ {...propsToPass}
106
+ secondaryDisableWithData={false}
107
+ SecondaryRepository={LocalSecondaryRepository}
108
+ secondaryModel={secondaryModel}
109
+ secondaryData={secondaryData}
110
+ secondaryFields={secondaryFields}
111
+ secondaryIdField={secondaryIdField}
112
+ secondaryDisplayField={secondaryDisplayField}
113
+ secondaryIdIx={localIdIx}
114
+ secondaryDisplayIx={localDisplayIx}
115
+ />;
116
+ };
117
+ }
@@ -0,0 +1,455 @@
1
+ import { useEffect, useState, useRef, } from 'react';
2
+ import {
3
+ Button,
4
+ } from 'native-base';
5
+ import {
6
+ EDITOR_MODE__VIEW,
7
+ EDITOR_MODE__ADD,
8
+ EDITOR_MODE__EDIT,
9
+ } from '../../../Constants/Editor.js';
10
+ import _ from 'lodash';
11
+
12
+ // NOTE: This is a modified version of @onehat/ui/src/Hoc/withEditor
13
+ // This HOC will eventually get out of sync with that one, and may need to be updated.
14
+
15
+ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
16
+ return (props) => {
17
+
18
+ if (props.secondaryDisableWithEditor) {
19
+ return <WrappedComponent {...props} />;
20
+ }
21
+
22
+ let [secondaryEditorMode, secondarySetEditorMode] = useState(EDITOR_MODE__VIEW); // Can change below, so use 'let'
23
+ const {
24
+ secondaryUserCanEdit = true,
25
+ secondaryUserCanView = true,
26
+ secondaryCanEditorViewOnly = false, // whether the editor can *ever* change state out of 'View' mode
27
+ secondaryDisableAdd = false,
28
+ secondaryDisableEdit = false,
29
+ secondaryDisableDelete = false,
30
+ secondaryDisableDuplicate = false,
31
+ secondaryDisableView = false,
32
+ secondaryUseRemoteDuplicate = false, // call specific copyToNew function on server, rather than simple duplicate on client
33
+ secondaryGetRecordIdentifier = (secondarySelection) => {
34
+ if (secondarySelection.length > 1) {
35
+ return 'records?';
36
+ }
37
+ return 'record' + (secondarySelection[0].displayValue ? ' "' + secondarySelection[0].displayValue + '"' : '') + '?';
38
+ },
39
+ secondaryRecord,
40
+ secondaryOnChange,
41
+ secondaryOnSave,
42
+ secondaryNewEntityDisplayValue,
43
+
44
+ // withComponent
45
+ self,
46
+
47
+ // parent container
48
+ secondarySelectorId,
49
+ secondarySelectorSelected,
50
+
51
+ // withSecondaryData
52
+ SecondaryRepository,
53
+
54
+ // withSecondarySelection
55
+ secondarySelection,
56
+ secondarySetSelection,
57
+
58
+ // withAlert
59
+ alert,
60
+ confirm,
61
+ hideAlert,
62
+ } = props,
63
+ secondaryListeners = useRef({}),
64
+ secondaryEditorStateRef = useRef(),
65
+ secondaryNewEntityDisplayValueRef = useRef(),
66
+ [secondaryCurrentRecord, secondarySetCurrentRecord] = useState(null),
67
+ [secondaryIsAdding, setIsAdding] = useState(false),
68
+ [secondaryIsSaving, setIsSaving] = useState(false),
69
+ [secondaryIsEditorShown, secondarySetIsEditorShown] = useState(false),
70
+ [secondaryIsEditorViewOnly, setIsEditorViewOnly] = useState(secondaryCanEditorViewOnly), // current state of whether editor is in view-only mode
71
+ [secondaryLastSelection, setLastSelection] = useState(),
72
+ secondarySetSelectionDecorated = (newSelection) => {
73
+ function doIt() {
74
+ secondarySetSelection(newSelection);
75
+ }
76
+ const formState = secondaryEditorStateRef.current;
77
+ if (!_.isEmpty(formState?.dirtyFields) && newSelection !== secondarySelection && secondaryEditorMode === EDITOR_MODE__EDIT) {
78
+ confirm('This record has unsaved changes. Are you sure you want to cancel editing? Changes will be lost.', doIt);
79
+ } else {
80
+ doIt();
81
+ }
82
+ },
83
+ getListeners = () => {
84
+ return secondaryListeners.current;
85
+ },
86
+ setListeners = (obj) => {
87
+ secondaryListeners.current = obj;
88
+ // forceUpdate(); // we don't want to get into an infinite loop of renders. Simply directly assign the secondaryListeners in every child render
89
+ },
90
+ getNewEntityDisplayValue = () => {
91
+ return secondaryNewEntityDisplayValueRef.current;
92
+ },
93
+ secondaryOnAdd = async (e, values) => {
94
+ const defaultValues = SecondaryRepository.getSchema().getDefaultValues();
95
+ let addValues = values || _.clone(defaultValues);
96
+
97
+ if (secondarySelectorId && !_.isEmpty(secondarySelectorSelected)) {
98
+ addValues[secondarySelectorId] = secondarySelectorSelected.id;
99
+ }
100
+
101
+ if (!values && getNewEntityDisplayValue()) {
102
+ const displayPropertyName = SecondaryRepository.getSchema().model.displayProperty;
103
+ addValues[displayPropertyName] = getNewEntityDisplayValue();
104
+ }
105
+
106
+ if (getListeners().onBeforeAdd) {
107
+ const listenerResult = await getListeners().onBeforeAdd();
108
+ if (listenerResult === false) {
109
+ return;
110
+ }
111
+ }
112
+
113
+ if (isTree) {
114
+ if (!secondarySelection[0]) {
115
+ throw Error('Must select a parent node.');
116
+ }
117
+ addValues.parentId = secondarySelection[0].id;
118
+ } else {
119
+ // Set repository to sort by id DESC and switch to page 1, so this new entity is guaranteed to show up on the current page, even after saving
120
+ const currentSorter = SecondaryRepository.sorters[0];
121
+ if (currentSorter.name !== SecondaryRepository.schema.model.idProperty || currentSorter.direction !== 'DESC') {
122
+ SecondaryRepository.pauseEvents();
123
+ SecondaryRepository.sort(SecondaryRepository.schema.model.idProperty, 'DESC');
124
+ SecondaryRepository.setPage(1);
125
+ SecondaryRepository.resumeEvents();
126
+ await SecondaryRepository.reload();
127
+ }
128
+ }
129
+
130
+ // Unmap the values, so we can input true originalData
131
+ addValues = SecondaryRepository.unmapData(addValues);
132
+
133
+
134
+ setIsAdding(true);
135
+ setIsSaving(true);
136
+ const entity = await SecondaryRepository.add(addValues, false, true);
137
+ setIsSaving(false);
138
+ secondarySetSelection([entity]);
139
+ setIsEditorViewOnly(false);
140
+ secondarySetEditorMode(EDITOR_MODE__ADD);
141
+ secondarySetIsEditorShown(true);
142
+
143
+ if (getListeners().onAfterAdd) {
144
+ await getListeners().onAfterAdd(entity);
145
+ }
146
+ if (secondaryOnChange) {
147
+ secondaryOnChange();
148
+ }
149
+ },
150
+ secondaryOnEdit = async () => {
151
+ if (_.isEmpty(secondarySelection) || (_.isArray(secondarySelection) && (secondarySelection.length > 1 || secondarySelection[0]?.isDestroyed))) {
152
+ return;
153
+ }
154
+ if (getListeners().onBeforeEdit) {
155
+ const listenerResult = await getListeners().onBeforeEdit();
156
+ if (listenerResult === false) {
157
+ return;
158
+ }
159
+ }
160
+ setIsEditorViewOnly(false);
161
+ secondarySetEditorMode(EDITOR_MODE__EDIT);
162
+ secondarySetIsEditorShown(true);
163
+ },
164
+ secondaryOnDelete = async (args) => {
165
+ let cb = null;
166
+ if (_.isFunction(args)) {
167
+ cb = args;
168
+ }
169
+ if (_.isEmpty(secondarySelection) || (_.isArray(secondarySelection) && (secondarySelection.length > 1 || secondarySelection[0]?.isDestroyed))) {
170
+ return;
171
+ }
172
+ if (getListeners().onBeforeDelete) {
173
+ const listenerResult = await getListeners().onBeforeDelete();
174
+ if (listenerResult === false) {
175
+ return;
176
+ }
177
+ }
178
+ const
179
+ isSingle = secondarySelection.length === 1,
180
+ firstSelection = secondarySelection[0],
181
+ isTree = firstSelection?.isTree,
182
+ hasChildren = isTree ? firstSelection?.hasChildren : false,
183
+ isPhantom = firstSelection?.isPhantom;
184
+
185
+ if (isSingle && isTree && hasChildren) {
186
+ alert({
187
+ title: 'Move up children?',
188
+ message: 'The node you have selected for deletion has children. ' +
189
+ 'Should these children be moved up to this node\'s parent, or be deleted?',
190
+ buttons: [
191
+ <Button colorScheme="danger" onPress={() => secondaryOnMoveChildren(cb)} key="moveBtn">
192
+ Move Children
193
+ </Button>,
194
+ <Button colorScheme="danger" onPress={() => secondaryOnDeleteChildren(cb)} key="deleteBtn">
195
+ Delete Children
196
+ </Button>
197
+ ],
198
+ includeCancel: true,
199
+ });
200
+ } else
201
+ if (isSingle && isPhantom) {
202
+ deleteRecord(cb);
203
+ } else {
204
+ const identifier = secondaryGetRecordIdentifier(secondarySelection);
205
+ confirm('Are you sure you want to delete the ' + identifier, () => deleteRecord(null, cb));
206
+ }
207
+ },
208
+ secondaryOnMoveChildren = (cb) => {
209
+ hideAlert();
210
+ deleteRecord(true, cb);
211
+ },
212
+ secondaryOnDeleteChildren = (cb) => {
213
+ hideAlert();
214
+ deleteRecord(false, cb);
215
+ },
216
+ deleteRecord = async (moveSubtreeUp, cb) => {
217
+ if (getListeners().onBeforeDeleteSave) {
218
+ await getListeners().onBeforeDeleteSave(secondarySelection);
219
+ }
220
+
221
+ await SecondaryRepository.delete(secondarySelection, moveSubtreeUp);
222
+ if (!SecondaryRepository.isAutoSave) {
223
+ await SecondaryRepository.save();
224
+ }
225
+ if (getListeners().onAfterDelete) {
226
+ await getListeners().onAfterDelete(secondarySelection);
227
+ }
228
+ secondarySetSelection([]);
229
+ if (cb) {
230
+ cb();
231
+ }
232
+ if (secondaryOnChange) {
233
+ secondaryOnChange();
234
+ }
235
+ },
236
+ secondaryOnView = async () => {
237
+ if (!secondaryUserCanView) {
238
+ return;
239
+ }
240
+ if (secondarySelection.length !== 1) {
241
+ return;
242
+ }
243
+ setIsEditorViewOnly(true);
244
+ secondarySetEditorMode(EDITOR_MODE__VIEW);
245
+ secondarySetIsEditorShown(true);
246
+
247
+ if (getListeners().onAfterView) {
248
+ await getListeners().onAfterView();
249
+ }
250
+ },
251
+ secondaryOnDuplicate = async () => {
252
+ if (!secondaryUserCanEdit || secondaryDisableDuplicate) {
253
+ return;
254
+ }
255
+ if (secondarySelection.length !== 1) {
256
+ return;
257
+ }
258
+ if (secondaryUseRemoteDuplicate) {
259
+ return onRemoteDuplicate();
260
+ }
261
+ const
262
+ entity = secondarySelection[0],
263
+ idProperty = SecondaryRepository.getSchema().model.idProperty,
264
+ rawValues = _.omit(entity.rawValues, idProperty),
265
+ duplicate = await SecondaryRepository.add(rawValues, false, true);
266
+ secondarySetSelection([duplicate]);
267
+ secondarySetEditorMode(EDITOR_MODE__EDIT);
268
+ secondarySetIsEditorShown(true);
269
+ },
270
+ onRemoteDuplicate = async () => {
271
+ const
272
+ entity = secondarySelection[0],
273
+ duplicateEntity = await SecondaryRepository.remoteDuplicate(entity);
274
+
275
+ secondarySetSelection([duplicateEntity]);
276
+ secondaryOnEdit();
277
+ },
278
+ secondaryOnEditorSave = async (data, e) => {
279
+ const
280
+ what = secondaryRecord || secondarySelection,
281
+ isSingle = what.length === 1;
282
+ if (isSingle) {
283
+ // just update this one entity
284
+ what[0].setValues(data);
285
+
286
+ } else if (secondarySelection.length > 1) {
287
+ // Edit multiple entities
288
+
289
+ // Loop through all entities and change fields that are not null
290
+ const propertyNames = Object.getOwnPropertyNames(data);
291
+ _.each(propertyNames, (propertyName) => {
292
+ if (!_.isNil(data[propertyName])) {
293
+ _.each(what, (rec) => {
294
+ rec[propertyName] = data[propertyName]
295
+ });
296
+ }
297
+ });
298
+ }
299
+
300
+ if (getListeners().onBeforeEditSave) {
301
+ await getListeners().onBeforeEditSave(what);
302
+ }
303
+
304
+ setIsSaving(true);
305
+ let success;
306
+ try {
307
+ await SecondaryRepository.save();
308
+ success = true;
309
+ } catch (e) {
310
+ success = false;
311
+ }
312
+ setIsSaving(false);
313
+
314
+ if (success) {
315
+ setIsAdding(false);
316
+
317
+ secondarySetEditorMode(EDITOR_MODE__EDIT);
318
+ // secondarySetIsEditorShown(false);
319
+
320
+ if (getListeners().onAfterEdit) {
321
+ await getListeners().onAfterEdit(what);
322
+ }
323
+ if (secondaryOnChange) {
324
+ secondaryOnChange();
325
+ }
326
+ if (secondaryOnSave) {
327
+ secondaryOnSave(what);
328
+ }
329
+ }
330
+
331
+ return success;
332
+ },
333
+ secondaryOnEditorCancel = () => {
334
+ async function doIt() {
335
+ const
336
+ isSingle = secondarySelection.length === 1,
337
+ isPhantom = secondarySelection[0] && !secondarySelection[0]?.isDestroyed && secondarySelection[0].isPhantom;
338
+ if (isSingle && isPhantom) {
339
+ await deleteRecord();
340
+ }
341
+
342
+ setIsAdding(false);
343
+ secondarySetIsEditorShown(false);
344
+ }
345
+ const formState = secondaryEditorStateRef.current;
346
+ if (!_.isEmpty(formState.dirtyFields)) {
347
+ confirm('This record has unsaved changes. Are you sure you want to cancel editing? Changes will be lost.', doIt);
348
+ } else {
349
+ doIt();
350
+ }
351
+ },
352
+ secondaryOnEditorClose = () => {
353
+ if (secondaryIsAdding) {
354
+ secondaryOnEditorCancel();
355
+ }
356
+ secondarySetIsEditorShown(false);
357
+ },
358
+ secondaryOnEditorDelete = async () => {
359
+ secondaryOnDelete(() => {
360
+ secondarySetEditorMode(EDITOR_MODE__VIEW);
361
+ secondarySetIsEditorShown(false);
362
+ });
363
+ },
364
+ calculateEditorMode = () => {
365
+ let mode = secondaryEditorMode;
366
+ if (!secondaryCanEditorViewOnly && secondaryUserCanEdit) {
367
+ if (secondarySelection.length > 1) {
368
+ if (!secondaryDisableEdit) {
369
+ // For multiple entities selected, change it to edit multiple mode
370
+ mode = EDITOR_MODE__EDIT;
371
+ }
372
+ } else if (secondarySelection.length === 1 && !secondarySelection[0].isDestroyed && secondarySelection[0].isPhantom) {
373
+ if (!secondaryDisableAdd) {
374
+ // When a phantom entity is selected, change it to add mode.
375
+ mode = EDITOR_MODE__ADD;
376
+ }
377
+ }
378
+ }
379
+ return mode;
380
+ },
381
+ secondaryOnEditMode = () => {
382
+ secondarySetEditorMode(EDITOR_MODE__EDIT);
383
+ },
384
+ secondaryOnViewMode = () => {
385
+ function doIt() {
386
+ secondarySetEditorMode(EDITOR_MODE__VIEW);
387
+ }
388
+ const formState = secondaryEditorStateRef.current;
389
+ if (!_.isEmpty(formState.dirtyFields)) {
390
+ confirm('This record has unsaved changes. Are you sure you want to switch to "View" mode? Changes will be lost.', doIt);
391
+ } else {
392
+ doIt();
393
+ }
394
+ };
395
+
396
+ useEffect(() => {
397
+ // When secondarySelection changes, set the mode appropriately
398
+ const mode = calculateEditorMode();
399
+ secondarySetEditorMode(mode);
400
+
401
+ setLastSelection(secondarySelection);
402
+ }, [secondarySelection]);
403
+
404
+ if (self) {
405
+ self.secondaryAdd = secondaryOnAdd;
406
+ self.secondaryEdit = secondaryOnEdit;
407
+ self.secondaryDelete = secondaryOnDelete;
408
+ self.secondarnMoveChildren = secondaryOnMoveChildren;
409
+ self.secondaryDeleteChildren = secondaryOnDeleteChildren;
410
+ self.secondaryDuplicate = secondaryOnDuplicate;
411
+ }
412
+ secondaryNewEntityDisplayValueRef.current = secondaryNewEntityDisplayValue;
413
+
414
+ if (secondaryLastSelection !== secondarySelection) {
415
+ // NOTE: If I don't calculate this on the fly for secondarySelection changes,
416
+ // we see a flash of the previous state, since useEffect hasn't yet run.
417
+ secondaryEditorMode = calculateEditorMode();
418
+ }
419
+
420
+ return <WrappedComponent
421
+ {...props}
422
+ secondaryDisableWithEditor={false}
423
+ secondaryCurrentRecord={secondaryCurrentRecord}
424
+ secondarySetCurrentRecord={secondarySetCurrentRecord}
425
+ secondaryIsEditorShown={secondaryIsEditorShown}
426
+ secondaryIsEditorViewOnly={secondaryIsEditorViewOnly}
427
+ secondaryIsAdding={secondaryIsAdding}
428
+ secondaryIsSaving={secondaryIsSaving}
429
+ secondaryEditorMode={secondaryEditorMode}
430
+ secondaryOnEditMode={secondaryOnEditMode}
431
+ secondaryOnViewMode={secondaryOnViewMode}
432
+ secondaryEditorStateRef={secondaryEditorStateRef}
433
+ secondarySetIsEditorShown={secondarySetIsEditorShown}
434
+ secondaryOnAdd={(!secondaryUserCanEdit || secondaryDisableAdd) ? null : secondaryOnAdd}
435
+ secondaryOnEdit={(!secondaryUserCanEdit || secondaryDisableEdit) ? null : secondaryOnEdit}
436
+ secondaryOnDelete={(!secondaryUserCanEdit || secondaryDisableDelete) ? null : secondaryOnDelete}
437
+ secondaryOnView={secondaryOnView}
438
+ secondaryOnDuplicate={secondaryOnDuplicate}
439
+ secondaryOnEditorSave={secondaryOnEditorSave}
440
+ secondaryOnEditorCancel={secondaryOnEditorCancel}
441
+ secondaryOnEditorDelete={(!secondaryUserCanEdit || secondaryDisableDelete) ? null : secondaryOnEditorDelete}
442
+ secondaryOnEditorClose={secondaryOnEditorClose}
443
+ secondarySetWithEditListeners={setListeners}
444
+ secondaryIsEditor={true}
445
+ secondaryUserCanEdit={secondaryUserCanEdit}
446
+ secondaryUserCanView={secondaryUserCanView}
447
+ secondaryDisableAdd={secondaryDisableAdd}
448
+ secondaryDisableEdit={secondaryDisableEdit}
449
+ secondaryDisableDelete={secondaryDisableDelete}
450
+ secondaryDisableDuplicate={secondaryDisableDuplicate}
451
+ secondaryDisableView ={secondaryDisableView}
452
+ secondarySetSelection={secondarySetSelectionDecorated}
453
+ />;
454
+ };
455
+ }
@@ -0,0 +1,377 @@
1
+ import { useState, useEffect, } from 'react';
2
+ import {
3
+ SELECTION_MODE_SINGLE,
4
+ SELECTION_MODE_MULTI,
5
+ SELECT_UP,
6
+ SELECT_DOWN,
7
+ } from '../../../Constants/Selection.js';
8
+ import inArray from '../../../Functions/inArray.js';
9
+ import _ from 'lodash';
10
+
11
+ // NOTE: This is a modified version of @onehat/ui/src/Hoc/withSelection
12
+ // This HOC will eventually get out of sync with that one, and may need to be updated.
13
+
14
+ export default function withSelection(WrappedComponent) {
15
+ return (props) => {
16
+
17
+ if (props.secondaryDisableWithSelection) {
18
+ return <WrappedComponent {...props} />;
19
+ }
20
+
21
+ if (props.secondarySetSelection) {
22
+ // bypass everything, since we're already using withSelection() in hierarchy.
23
+ // For example, Combo has withSelection(), and intenally it uses Grid which also has withSelection(),
24
+ // but we only need it defined once for the whole thing.
25
+ return <WrappedComponent {...props} />;
26
+ }
27
+
28
+ const
29
+ {
30
+ secondarySelection,
31
+ secondaryDefaultSelection,
32
+ secondaryOnChangeSelection,
33
+ secondarySelectionMode = SELECTION_MODE_SINGLE, // SELECTION_MODE_MULTI, SELECTION_MODE_SINGLE
34
+ secondaryAutoSelectFirstItem = false,
35
+ fireEvent,
36
+
37
+ // withComponent
38
+ self,
39
+
40
+ // withSecondaryValue
41
+ secondaryValue,
42
+ secondarySetValue,
43
+
44
+ // withSecondaryData
45
+ SecondaryRepository,
46
+ secondaryData,
47
+ secondaryIdIx,
48
+ secondaryDisplayIx,
49
+ } = props,
50
+ usesWithValue = !!secondarySetValue,
51
+ initialSelection = secondarySelection || secondaryDefaultSelection || [],
52
+ [secondaryLocalSelection, setLocalSelection] = useState(initialSelection),
53
+ [isReady, setIsReady] = useState(secondarySelection || false), // if secondarySelection is already defined, or secondaryValue is not null and we don't need to load repository, it's ready
54
+ secondarySetSelection = (secondarySelection) => {
55
+ if (_.isEqual(secondarySelection, secondaryLocalSelection)) {
56
+ return;
57
+ }
58
+
59
+ setLocalSelection(secondarySelection);
60
+ if (secondaryOnChangeSelection) {
61
+ secondaryOnChangeSelection(secondarySelection);
62
+ }
63
+ if (fireEvent) {
64
+ fireEvent('secondaryChangeSelection', secondarySelection);
65
+ }
66
+ },
67
+ secondarySelectNext = () => {
68
+ secondarySelectDirection(SELECT_DOWN);
69
+ },
70
+ secondarySelectPrev = () => {
71
+ secondarySelectDirection(SELECT_UP);
72
+ },
73
+ secondarySelectDirection = (which) => {
74
+ const { items, max, min, noSelection, } = getMaxMinSelectionIndices();
75
+ let newIx;
76
+ if (which === SELECT_DOWN) {
77
+ if (noSelection || max === items.length -1) {
78
+ // select first
79
+ newIx = 0;
80
+ } else {
81
+ newIx = max +1;
82
+ }
83
+ } else if (which === SELECT_UP) {
84
+ if (noSelection || min === 0) {
85
+ // select last
86
+ newIx = items.length -1;
87
+ } else {
88
+ newIx = min -1;
89
+ }
90
+ }
91
+ if (items[newIx]) {
92
+ secondarySetSelection([items[newIx]]);
93
+ }
94
+ },
95
+ secondaryAddToSelection = (item) => {
96
+ let newSelection = [];
97
+ newSelection = _.clone(secondaryLocalSelection); // so we get a new object, so descendants rerender
98
+ newSelection.push(item);
99
+ secondarySetSelection(newSelection);
100
+ },
101
+ secondaryRemoveFromSelection = (item) => {
102
+ let newSelection = [];
103
+ if (SecondaryRepository) {
104
+ newSelection = _.remove(secondaryLocalSelection, (sel) => sel !== item);
105
+ } else {
106
+ newSelection = _.remove(secondaryLocalSelection, (sel) => sel[secondaryIdIx] !== item[secondaryIdIx]);
107
+ }
108
+ secondarySetSelection(newSelection);
109
+ },
110
+ secondaryDeselectAll = () => {
111
+ if (!_.isEmpty(secondaryLocalSelection)) {
112
+ secondarySetSelection([]);
113
+ }
114
+ },
115
+ getMaxMinSelectionIndices = () => {
116
+ let items,
117
+ currentlySelectedRowIndices = [];
118
+ if (SecondaryRepository) {
119
+ items = SecondaryRepository.getEntitiesOnPage();
120
+ } else {
121
+ items = secondaryData;
122
+ }
123
+ _.each(items, (item, ix) => {
124
+ if (secondaryIsInSelection(item)) {
125
+ currentlySelectedRowIndices.push(ix);
126
+ }
127
+ });
128
+ if (currentlySelectedRowIndices.length === 0) {
129
+ return { items, noSelection: true, };
130
+ }
131
+ const
132
+ max = Math.max(...currentlySelectedRowIndices),
133
+ min = Math.min(...currentlySelectedRowIndices);
134
+
135
+ return { items, max, min, noSelection: false, };
136
+ },
137
+ secondarySelectRangeTo = (item) => {
138
+ // Select above max or below min to this one
139
+ const
140
+ currentSelectionLength = secondaryLocalSelection.length,
141
+ index = getIndexOfSelectedItem(item);
142
+ let newSelection = _.clone(secondaryLocalSelection); // so we get a new object, so descendants rerender
143
+
144
+ if (currentSelectionLength) {
145
+ const { items, max, min, } = getMaxMinSelectionIndices();
146
+ let i,
147
+ itemAtIx;
148
+ if (max < index) {
149
+ // all other secondarySelections are below the current;
150
+ // Range is from max+1 up to index
151
+ for (i = max +1; i < index; i++) {
152
+ itemAtIx = items[i];
153
+ newSelection.push(itemAtIx);
154
+ }
155
+ } else if (min > index) {
156
+ // all other secondarySelections are above the current;
157
+ // Range is from min-1 down to index
158
+ for (i = min -1; i > index; i--) {
159
+ itemAtIx = items[i];
160
+ newSelection.push(itemAtIx);
161
+ }
162
+ }
163
+ }
164
+ newSelection.push(item);
165
+ secondarySetSelection(newSelection);
166
+ },
167
+ secondaryIsInSelection = (item) => {
168
+ if (SecondaryRepository) {
169
+ return inArray(item, secondaryLocalSelection);
170
+ }
171
+
172
+ const found = _.find(secondaryLocalSelection, (selectedItem) => {
173
+ return selectedItem[secondaryIdIx] === item[secondaryIdIx];
174
+ });
175
+ return !!found;
176
+ },
177
+ getIndexOfSelectedItem = (item) => {
178
+ // Gets ix of entity on page, or element in secondaryData array
179
+ if (SecondaryRepository) {
180
+ const entities = SecondaryRepository.getEntitiesOnPage();
181
+ return entities.indexOf(item);
182
+ }
183
+
184
+ let found;
185
+ _.each(secondaryData, (datum, ix) => {
186
+ if (datum[secondaryIdIx] === item[secondaryIdIx]) {
187
+ found = ix;
188
+ return false; // break loop
189
+ }
190
+ });
191
+ return found;
192
+ },
193
+ secondaryGetIdsFromLocalSelection = () => {
194
+ if (!secondaryLocalSelection[0]) {
195
+ return null;
196
+ }
197
+ const secondaryValues = _.map(secondaryLocalSelection, (item) => {
198
+ if (SecondaryRepository) {
199
+ return item.id;
200
+ }
201
+ return item[secondaryIdIx];
202
+ });
203
+ if (secondaryValues.length === 1) {
204
+ return secondaryValues[0];
205
+ }
206
+ return secondaryValues;
207
+ },
208
+ secondaryGetDisplayValuesFromLocalSelection = (secondarySelection) => {
209
+ if (!secondarySelection[0]) {
210
+ return '';
211
+ }
212
+
213
+ return _.map(secondarySelection, (item) => {
214
+ if (SecondaryRepository) {
215
+ return item.displayValue;
216
+ }
217
+ return item[secondaryDisplayIx];
218
+ })
219
+ .join(', ');
220
+ },
221
+ conformValueToLocalSelection = () => {
222
+ if (!secondarySetValue) {
223
+ return;
224
+ }
225
+
226
+ const localValue = secondaryGetIdsFromLocalSelection();
227
+ if (!_.isEqual(localValue, secondaryValue)) {
228
+ secondarySetValue(localValue);
229
+ }
230
+ },
231
+ conformSelectionToValue = async () => {
232
+ let newSelection = [];
233
+ if (SecondaryRepository) {
234
+ if (SecondaryRepository.isLoading) {
235
+ await SecondaryRepository.waitUntilDoneLoading();
236
+ }
237
+ // Get entity or entities that match secondaryValue
238
+ if ((_.isArray(secondaryValue) && !_.isEmpty(secondaryValue)) || !!secondaryValue) {
239
+ if (_.isArray(secondaryValue)) {
240
+ newSelection = SecondaryRepository.getBy((entity) => inArray(entity.id, secondaryValue));
241
+ } else {
242
+ let found = SecondaryRepository.getById(secondaryValue);
243
+ if (found) {
244
+ newSelection.push(found);
245
+ // } else if (SecondaryRepository?.isRemote && SecondaryRepository?.entities.length) {
246
+
247
+ // // Value cannot be found in SecondaryRepository, but actually exists on server
248
+ // // Try to get this secondaryValue from the server directly
249
+ // SecondaryRepository.filter(SecondaryRepository.schema.model.idProperty, secondaryValue);
250
+ // await SecondaryRepository.load();
251
+ // found = SecondaryRepository.getById(secondaryValue);
252
+ // if (found) {
253
+ // newSelection.push(found);
254
+ // }
255
+
256
+ }
257
+ }
258
+ }
259
+ } else {
260
+ // Get secondaryData item or items that match secondaryValue
261
+ if (!_.isNil(secondaryValue) && (_.isBoolean(secondaryValue) || _.isNumber(secondaryValue) || !_.isEmpty(secondaryValue))) {
262
+ let currentValue = secondaryValue;
263
+ if (!_.isArray(currentValue)) {
264
+ currentValue = [currentValue];
265
+ }
266
+ _.each(currentValue, (val) => {
267
+ // Search through secondaryData
268
+ const found = _.find(secondaryData, (item) => {
269
+ if (_.isString(item[secondaryIdIx]) && _.isString(val)) {
270
+ return item[secondaryIdIx].toLowerCase() === val.toLowerCase();
271
+ }
272
+ return item[secondaryIdIx] === val;
273
+ });
274
+ if (found) {
275
+ newSelection.push(found);
276
+ }
277
+ });
278
+ }
279
+ }
280
+
281
+ if (!_.isEqual(newSelection, secondaryLocalSelection)) {
282
+ secondarySetSelection(newSelection);
283
+ }
284
+ };
285
+
286
+ useEffect(() => {
287
+
288
+ (async () => {
289
+
290
+ if (usesWithValue && SecondaryRepository?.isRemote
291
+ && !SecondaryRepository.isAutoLoad && !SecondaryRepository.isLoaded && !SecondaryRepository.isLoading && (!_.isNil(secondaryValue) || !_.isEmpty(secondarySelection)) || secondaryAutoSelectFirstItem) {
292
+ // on initialization, we can't conformSelectionToValue if the repository is not yet loaded,
293
+ // so first load repo, then conform to secondaryValue
294
+ await SecondaryRepository.load();
295
+ }
296
+
297
+ if (!_.isNil(secondaryValue)) {
298
+
299
+ await conformSelectionToValue();
300
+
301
+ } else if (!_.isEmpty(secondarySelection)) {
302
+
303
+ conformValueToLocalSelection();
304
+
305
+ } else if (secondaryAutoSelectFirstItem) {
306
+ let newSelection = [];
307
+ if (SecondaryRepository) {
308
+ const entitiesOnPage = SecondaryRepository.getEntitiesOnPage();
309
+ newSelection = entitiesOnPage[0] ? [entitiesOnPage[0]] : [];
310
+ } else {
311
+ newSelection = secondaryData[0] ? [secondaryData[0]] : [];
312
+ }
313
+ secondarySetSelection(newSelection);
314
+ }
315
+
316
+ setIsReady(true);
317
+
318
+ })();
319
+
320
+ }, [secondaryValue]);
321
+
322
+ if (self) {
323
+ self.secondarySelection = secondaryLocalSelection;
324
+ self.secondarySetSelection = secondarySetSelection;
325
+ self.secondarySelectNext = secondarySelectNext;
326
+ self.secondarySelectPrev = secondarySelectPrev;
327
+ self.secondaryAddToSelection = secondaryAddToSelection;
328
+ self.secondaryRemoveFromSelection = secondaryRemoveFromSelection;
329
+ self.secondaryDeselectAll = secondaryDeselectAll;
330
+ self.secondarySelectRangeTo = secondarySelectRangeTo;
331
+ self.secondaryIsInSelection = secondaryIsInSelection;
332
+ self.secondaryGetIdsFromLocalSelection = secondaryGetIdsFromLocalSelection;
333
+ self.secondaryGetDisplayValuesFromSelection = secondaryGetDisplayValuesFromLocalSelection;
334
+ }
335
+
336
+ if (usesWithValue) {
337
+ useEffect(() => {
338
+ if (!isReady) {
339
+ return () => {};
340
+ }
341
+
342
+ conformSelectionToValue();
343
+
344
+ }, [secondaryValue]);
345
+
346
+ useEffect(() => {
347
+ if (!isReady) {
348
+ return () => {};
349
+ }
350
+
351
+ conformValueToLocalSelection();
352
+
353
+ }, [secondarySelection]);
354
+ }
355
+
356
+ if (!isReady) {
357
+ return null;
358
+ }
359
+
360
+ return <WrappedComponent
361
+ {...props}
362
+ secondaryDisableWithSelection={false}
363
+ secondarySelection={secondaryLocalSelection}
364
+ secondarySetSelection={secondarySetSelection}
365
+ secondarySelectionMode={secondarySelectionMode}
366
+ secondarySelectNext={secondarySelectNext}
367
+ secondarySelectPrev={secondarySelectPrev}
368
+ secondaryRemoveFromSelection={secondaryRemoveFromSelection}
369
+ secondaryAddToSelection={secondaryAddToSelection}
370
+ secondaryDeselectAll={secondaryDeselectAll}
371
+ secondarySelectRangeTo={secondarySelectRangeTo}
372
+ secondaryIsInSelection={secondaryIsInSelection}
373
+ secondaryGetIdsFromSelection={secondaryGetIdsFromLocalSelection}
374
+ secondaryGetDisplayValuesFromSelection={secondaryGetDisplayValuesFromLocalSelection}
375
+ />;
376
+ };
377
+ }
@@ -0,0 +1,50 @@
1
+ import {
2
+ EDITOR_TYPE__SIDE,
3
+ } from '@onehat/ui/src/Constants/Editor.js';
4
+ import Container from '../../Container/Container.js';
5
+ import withSecondaryEditor from './withSecondaryEditor.js';
6
+ import _ from 'lodash';
7
+
8
+ // NOTE: This is a modified version of @onehat/ui/src/Hoc/withSideEditor
9
+ // This HOC will eventually get out of sync with that one, and may need to be updated.
10
+
11
+ export default function withSideEditor(WrappedComponent, isTree = false) {
12
+ return withSecondaryEditor((props) => {
13
+ const {
14
+ SecondaryEditor,
15
+ secondaryEditorProps = {},
16
+ secondarySideFlex = 100,
17
+
18
+ // withComponent
19
+ self,
20
+
21
+ // pull these out, as we don't want them going to the Editor
22
+ secondarySelectorId,
23
+ secondarySelectorSelected,
24
+
25
+ ...propsToPass
26
+ } = props;
27
+
28
+ if (!SecondaryEditor) {
29
+ throw Error('SecondaryEditor is not defined');
30
+ }
31
+
32
+ return <Container
33
+ center={<WrappedComponent
34
+ isTree={isTree}
35
+ isSideEditor={true}
36
+ {...props}
37
+ />}
38
+ east={<Editor
39
+ {...propsToPass}
40
+ editorType={EDITOR_TYPE__SIDE}
41
+ flex={secondarySideFlex}
42
+ borderLeftWidth={1}
43
+ borderLeftColor="#ccc"
44
+ {...secondaryEditorProps}
45
+ parent={self}
46
+ reference="secondaryEditor"
47
+ />}
48
+ />;
49
+ });
50
+ }
@@ -0,0 +1,154 @@
1
+ import { useState, useEffect, useRef, useContext, useCallback, } from 'react';
2
+ import natsort from 'natsort';
3
+ import useForceUpdate from '../../../Hooks/useForceUpdate.js';
4
+ import FieldSetContext from '../../../Contexts/FieldSetContext.js';
5
+ import _ from 'lodash';
6
+
7
+ // NOTE: This is a modified version of @onehat/ui/src/Hoc/withValue
8
+ // This HOC will eventually get out of sync with that one, and may need to be updated.
9
+
10
+ export default function withSecondaryValue(WrappedComponent) {
11
+ return (props) => {
12
+
13
+ if (props.secondaryDisableWithValue) {
14
+ return <WrappedComponent {...props} />;
15
+ }
16
+
17
+ if (props.secondarySetValue) {
18
+ // bypass everything, since we're already using withSecondaryValue() in hierarchy.
19
+ // For example, Combo has withSecondaryValue(), and intenally it uses Input which also has withSecondaryValue(),
20
+ // but we only need it defined once for the whole thing.
21
+ return <WrappedComponent {...props} />;
22
+ }
23
+
24
+ const
25
+ {
26
+ secondaryOnChangeValue,
27
+ secondaryValue,
28
+ secondaryStartingValue = null,
29
+ secondaryIsValueAlwaysArray = false,
30
+ secondaryIsValueAsStringifiedJson = false,
31
+
32
+ // withComponent
33
+ self,
34
+
35
+ // withData
36
+ SecondaryRepository,
37
+ secondaryIdIx,
38
+ } = props,
39
+ forceUpdate = useForceUpdate(),
40
+ childRef = useRef({}),
41
+ secondaryOnChangeValueRef = useRef(),
42
+ localValueRef = useRef(secondaryStartingValue || secondaryValue),
43
+ fieldSetOnChangeValueRef = useRef(),
44
+ fieldSetContext = useContext(FieldSetContext),
45
+ fieldSetRegisterChild = fieldSetContext?.registerChild,
46
+ fieldSetOnChangeValue = fieldSetContext?.secondaryOnChangeValue,
47
+ getLocalValue = () => {
48
+ return localValueRef.current;
49
+ },
50
+ secondarySetLocalValue = (secondaryValue) => {
51
+ localValueRef.current = secondaryValue;
52
+ forceUpdate();
53
+ },
54
+ secondarySetValueRef = useRef((newValue) => {
55
+ // NOTE: We useRef so that this function stays current after renders
56
+ if (secondaryIsValueAlwaysArray && !_.isArray(newValue)) {
57
+ newValue = _.isNil(newValue) ? [] : [newValue];
58
+ }
59
+ if (_.isArray(newValue)) {
60
+ const sortFn = natsort.default || natsort; // was having trouble with webpack and this solves it
61
+
62
+ // TODO: sort by the sortProperty, whatever that is, instead of just value
63
+ newValue.sort(sortFn()); // Only sort if we're using id/text arrangement. Otherwise, keep sort order as specified in SecondaryRepository.
64
+ }
65
+ if (secondaryIsValueAsStringifiedJson) {
66
+ newValue = JSON.stringify(newValue);
67
+ }
68
+
69
+ if (newValue === getLocalValue()) {
70
+ return;
71
+ }
72
+
73
+ secondarySetLocalValue(newValue);
74
+
75
+ if (secondaryOnChangeValueRef.current) {
76
+ secondaryOnChangeValueRef.current(newValue, childRef.current);
77
+ }
78
+ if (fieldSetOnChangeValueRef.current) {
79
+ fieldSetOnChangeValueRef.current(newValue, childRef.current);
80
+ }
81
+ }),
82
+ secondarySetValue = (args) => {
83
+ secondarySetValueRef.current(args);
84
+ },
85
+ secondaryOnChangeSelection = (selection) => {
86
+ let secondaryValue = null,
87
+ secondaryValues;
88
+ if (selection.length) {
89
+ if (SecondaryRepository) {
90
+ if (selection.length === 1) {
91
+ secondaryValue = selection[0].id;
92
+ } else {
93
+ secondaryValues = _.map(selection, (entity) => entity.id);
94
+ }
95
+ } else {
96
+ if (selection.length === 1) {
97
+ secondaryValue = selection[0][secondaryIdIx];
98
+ } else {
99
+ secondaryValues = _.map(selection, (item) => item[secondaryIdIx]);
100
+ }
101
+ }
102
+ }
103
+ if (secondaryValues) {
104
+ secondaryValue = secondaryValues;
105
+ }
106
+ secondarySetValue(secondaryValue);
107
+ };
108
+
109
+ // Ensure these passed functions stay current after render
110
+ secondaryOnChangeValueRef.current = secondaryOnChangeValue;
111
+ fieldSetOnChangeValueRef.current = fieldSetOnChangeValue;
112
+
113
+ useEffect(() => {
114
+ if (!_.isEqual(secondaryValue, getLocalValue())) {
115
+ secondarySetLocalValue(secondaryValue);
116
+ }
117
+ }, [secondaryValue]);
118
+
119
+ if (fieldSetRegisterChild) {
120
+ useEffect(() => {
121
+ fieldSetRegisterChild({
122
+ childRef: childRef.current,
123
+ value: secondaryValue,
124
+ setValue: secondarySetValueRef.current,
125
+ });
126
+ }, []);
127
+ }
128
+
129
+ if (self) {
130
+ self.secondarySetValue = secondarySetValue;
131
+ self.secondaryValue = getLocalValue();
132
+ }
133
+
134
+
135
+ // Convert localValue to normal JS primitives for field components
136
+ let convertedValue = getLocalValue();
137
+ if (_.isString(convertedValue) && secondaryIsValueAsStringifiedJson && !_.isNil(convertedValue)) {
138
+ convertedValue = JSON.parse(convertedValue);
139
+ }
140
+ if (secondaryIsValueAlwaysArray) {
141
+ if (_.isEmpty(convertedValue) || _.isNil(convertedValue)) {
142
+ convertedValue = [];
143
+ }
144
+ }
145
+
146
+ return <WrappedComponent
147
+ {...props}
148
+ secondaryDisableWithValue={false}
149
+ secondaryValue={convertedValue}
150
+ secondarySetValue={secondarySetValue}
151
+ secondaryOnChangeSelection={secondaryOnChangeSelection}
152
+ />;
153
+ };
154
+ }
@@ -0,0 +1,71 @@
1
+ import {
2
+ Column,
3
+ Modal,
4
+ Text,
5
+ } from 'native-base';
6
+ import {
7
+ EDITOR_TYPE__WINDOWED,
8
+ } from '../../../Constants/Editor.js';
9
+ import withSecondaryEditor from './withSecondaryEditor.js';
10
+ // import withDraggable from './withDraggable.js';
11
+ import _ from 'lodash';
12
+
13
+ // NOTE: This is a modified version of @onehat/ui/src/Hoc/withWindowedEditor
14
+ // This HOC will eventually get out of sync with that one, and may need to be updated.
15
+
16
+ export default function withSecondaryWindowedEditor(WrappedComponent, isTree = false) {
17
+ return withSecondaryEditor((props) => {
18
+ const {
19
+ secondaryIsEditorShown = false,
20
+ secondarySetIsEditorShown,
21
+ SecondaryEditor,
22
+ secondaryEditorProps = {},
23
+
24
+ // withComponent
25
+ self,
26
+
27
+ // pull these out, as we don't want them going to the SecondaryEditor
28
+ secondarySelectorId,
29
+ secondarySelectorSelected,
30
+ h,
31
+
32
+ ...propsToPass
33
+ } = props;
34
+
35
+ if (!SecondaryEditor) {
36
+ throw Error('SecondaryEditor is not defined');
37
+ }
38
+
39
+ if (secondaryIsEditorShown) {
40
+ // Move the 'secondary' props over to primary
41
+ // for the sake of the Editor
42
+ function lcfirst(str) {
43
+ return str.charAt(0).toLowerCase() + str.slice(1);
44
+ }
45
+ _.each(props, (prop, ix) => {
46
+ if (ix.match(/^secondary/)) {
47
+ const name = lcfirst(ix.replace(/^secondary/, ''));
48
+ secondaryEditorProps[name] = prop;
49
+ }
50
+ });
51
+ secondaryEditorProps.Repository = props.SecondaryRepository;
52
+ }
53
+
54
+ return <>
55
+ <WrappedComponent {...props} />
56
+ {secondaryIsEditorShown &&
57
+ <Modal
58
+ isOpen={true}
59
+ onClose={() => secondarySetIsEditorShown(false)}
60
+ >
61
+ <SecondaryEditor
62
+ editorType={EDITOR_TYPE__WINDOWED}
63
+ {...propsToPass}
64
+ {...secondaryEditorProps}
65
+ parent={self}
66
+ reference="secondaryEditor"
67
+ />
68
+ </Modal>}
69
+ </>;
70
+ }, isTree);
71
+ }
@@ -242,7 +242,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
242
242
  setIsEditorShown(true);
243
243
 
244
244
  if (getListeners().onAfterView) {
245
- await getListeners().onAfterView(entity);
245
+ await getListeners().onAfterView();
246
246
  }
247
247
  },
248
248
  onDuplicate = async () => {
@@ -0,0 +1,20 @@
1
+ // Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc.
2
+ import * as React from "react"
3
+ import Svg, { Path } from "react-native-svg"
4
+ import { Icon } from 'native-base';
5
+
6
+ function SvgComponent(props) {
7
+ return (
8
+ <Icon
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ height={16}
11
+ width={14}
12
+ viewBox="0 0 448 512"
13
+ {...props}
14
+ >
15
+ <Path d="M8 256a56 56 0 11112 0 56 56 0 11-112 0zm160 0a56 56 0 11112 0 56 56 0 11-112 0zm216-56a56 56 0 110 112 56 56 0 110-112z" />
16
+ </Icon>
17
+ )
18
+ }
19
+
20
+ export default SvgComponent
@@ -0,0 +1,20 @@
1
+ // Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc.
2
+ import * as React from "react"
3
+ import Svg, { Path } from "react-native-svg"
4
+ import { Icon } from 'native-base';
5
+
6
+ function SvgComponent(props) {
7
+ return (
8
+ <Icon
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ height={16}
11
+ width={14}
12
+ viewBox="0 0 448 512"
13
+ {...props}
14
+ >
15
+ <Path d="M0 32v448h448V32H0zm243.8 349.4c0 43.6-25.6 63.5-62.9 63.5-33.7 0-53.2-17.4-63.2-38.5l34.3-20.7c6.6 11.7 12.6 21.6 27.1 21.6 13.8 0 22.6-5.4 22.6-26.5V237.7h42.1v143.7zm99.6 63.5c-39.1 0-64.4-18.6-76.7-43l34.3-19.8c9 14.7 20.8 25.6 41.5 25.6 17.4 0 28.6-8.7 28.6-20.8 0-14.4-11.4-19.5-30.7-28l-10.5-4.5c-30.4-12.9-50.5-29.2-50.5-63.5 0-31.6 24.1-55.6 61.6-55.6 26.8 0 46 9.3 59.8 33.7L368 290c-7.2-12.9-15-18-27.1-18-12.3 0-20.1 7.8-20.1 18 0 12.6 7.8 17.7 25.9 25.6l10.5 4.5c35.8 15.3 55.9 31 55.9 66.2 0 37.8-29.8 58.6-69.7 58.6z" />
16
+ </Icon>
17
+ )
18
+ }
19
+
20
+ export default SvgComponent
@@ -0,0 +1,20 @@
1
+ // Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc.
2
+ import * as React from "react"
3
+ import Svg, { Path } from "react-native-svg"
4
+ import { Icon } from 'native-base';
5
+
6
+ function SvgComponent(props) {
7
+ return (
8
+ <Icon
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ height={16}
11
+ width={16}
12
+ viewBox="0 0 512 512"
13
+ {...props}
14
+ >
15
+ <Path d="M64 256v-96h160v96H64zm0 64h160v96H64v-96zm224 96v-96h160v96H288zm160-160H288v-96h160v96zM64 32C28.7 32 0 60.7 0 96v320c0 35.3 28.7 64 64 64h384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64z" />
16
+ </Icon>
17
+ )
18
+ }
19
+
20
+ export default SvgComponent
@@ -60,6 +60,7 @@ import Copyright from '../Components/Icons/Copyright.js';
60
60
  import Dot from '../Components/Icons/Dot.js';
61
61
  import Duplicate from '../Components/Icons/Duplicate.js';
62
62
  import Edit from '../Components/Icons/Edit.js';
63
+ import EllipsisHorizontal from '../Components/Icons/EllipsisHorizontal.js';
63
64
  import EllipsisVertical from '../Components/Icons/EllipsisVertical.js';
64
65
  import Envelope from '../Components/Icons/Envelope.js';
65
66
  import EnvelopeRegular from '../Components/Icons/EnvelopeRegular.js';
@@ -90,6 +91,7 @@ import House from '../Components/Icons/House.js';
90
91
  import Images from '../Components/Icons/Images.js';
91
92
  import Info from '../Components/Icons/Info.js';
92
93
  import ItunesNote from '../Components/Icons/ItunesNote.js';
94
+ import Js from '../Components/Icons/Js.js';
93
95
  import Leaf from '../Components/Icons/Leaf.js';
94
96
  import List from '../Components/Icons/List.js';
95
97
  import ListCheck from '../Components/Icons/ListCheck.js';
@@ -161,6 +163,7 @@ import SquareCheckRegular from '../Components/Icons/SquareCheckRegular.js';
161
163
  import SquareMinus from '../Components/Icons/SquareMinus.js';
162
164
  import SquareRegular from '../Components/Icons/SquareRegular.js';
163
165
  import Store from '../Components/Icons/Store.js';
166
+ import Table from '../Components/Icons/Table.js';
164
167
  import ThumbsDown from '../Components/Icons/ThumbsDown.js';
165
168
  import ThumbsDownRegular from '../Components/Icons/ThumbsDownRegular.js';
166
169
  import ThumbsUp from '../Components/Icons/ThumbsUp.js';
@@ -287,6 +290,7 @@ const components = {
287
290
  Dot,
288
291
  Duplicate,
289
292
  Edit,
293
+ EllipsisHorizontal,
290
294
  EllipsisVertical,
291
295
  Envelope,
292
296
  EnvelopeRegular,
@@ -317,6 +321,7 @@ const components = {
317
321
  Images,
318
322
  Info,
319
323
  ItunesNote,
324
+ Js,
320
325
  Leaf,
321
326
  List,
322
327
  ListCheck,
@@ -388,6 +393,7 @@ const components = {
388
393
  SquareMinus,
389
394
  SquareRegular,
390
395
  Store,
396
+ Table,
391
397
  ThumbsDown,
392
398
  ThumbsDownRegular,
393
399
  ThumbsUp,