@onehat/ui 0.3.32 → 0.3.34
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/Form/Form.js +9 -3
- package/src/Components/Grid/Grid.js +3 -0
- package/src/Components/Hoc/withComponent.js +14 -1
- package/src/Components/Hoc/withData.js +6 -0
- package/src/Components/Hoc/withEditor.js +44 -6
- package/src/Components/Hoc/withFilters.js +35 -5
- package/src/Components/Hoc/withPdfButton.js +4 -2
- package/src/Components/Hoc/withSelection.js +16 -0
- package/src/Components/Viewer/Viewer.js +7 -1
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useState, isValidElement, } from 'react';
|
|
1
|
+
import { useEffect, useState, useRef, isValidElement, } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Box,
|
|
4
4
|
Column,
|
|
@@ -77,6 +77,7 @@ function Form(props) {
|
|
|
77
77
|
// sizing of outer container
|
|
78
78
|
h,
|
|
79
79
|
maxHeight,
|
|
80
|
+
minHeight = 0,
|
|
80
81
|
w,
|
|
81
82
|
maxWidth,
|
|
82
83
|
flex,
|
|
@@ -105,6 +106,7 @@ function Form(props) {
|
|
|
105
106
|
// withAlert
|
|
106
107
|
alert,
|
|
107
108
|
} = props,
|
|
109
|
+
formRef = useRef(),
|
|
108
110
|
styles = UiGlobals.styles,
|
|
109
111
|
record = props.record?.length === 1 ? props.record[0] : props.record,
|
|
110
112
|
isMultiple = _.isArray(record),
|
|
@@ -564,6 +566,10 @@ function Form(props) {
|
|
|
564
566
|
if (!_.isNil(editorStateRef)) {
|
|
565
567
|
editorStateRef.current = formState; // Update state so HOC can know what's going on
|
|
566
568
|
}
|
|
569
|
+
|
|
570
|
+
if (self) {
|
|
571
|
+
self.ref = formRef;
|
|
572
|
+
}
|
|
567
573
|
|
|
568
574
|
const sizeProps = {};
|
|
569
575
|
if (!flex && !h && !w) {
|
|
@@ -617,7 +623,7 @@ function Form(props) {
|
|
|
617
623
|
} else {
|
|
618
624
|
formComponents = buildFromItems();
|
|
619
625
|
const formAncillaryComponents = buildAncillary();
|
|
620
|
-
editor = <ScrollView _web={{
|
|
626
|
+
editor = <ScrollView _web={{ minHeight, }} width="100%" pb={1}>
|
|
621
627
|
<Column p={4}>{formComponents}</Column>
|
|
622
628
|
<Column m={2} pt={4}>{formAncillaryComponents}</Column>
|
|
623
629
|
</ScrollView>;
|
|
@@ -654,7 +660,7 @@ function Form(props) {
|
|
|
654
660
|
|
|
655
661
|
const additionalButtons = buildAdditionalButtons(additionalEditButtons);
|
|
656
662
|
|
|
657
|
-
return <Column {...sizeProps} onLayout={onLayout}>
|
|
663
|
+
return <Column {...sizeProps} onLayout={onLayout} ref={formRef}>
|
|
658
664
|
|
|
659
665
|
<Row px={4} pt={4} alignItems="center" justifyContent="flex-end">
|
|
660
666
|
{isSingle && editorMode === EDITOR_MODE__EDIT && onBack &&
|
|
@@ -10,6 +10,7 @@ export default function withComponent(WrappedComponent) {
|
|
|
10
10
|
const {
|
|
11
11
|
// self: parent,
|
|
12
12
|
parent,
|
|
13
|
+
componentMethods,
|
|
13
14
|
...propsToPass
|
|
14
15
|
} = props,
|
|
15
16
|
{ reference } = props,
|
|
@@ -17,6 +18,13 @@ export default function withComponent(WrappedComponent) {
|
|
|
17
18
|
selfRef = useRef({
|
|
18
19
|
parent,
|
|
19
20
|
reference,
|
|
21
|
+
hasChild: (childRef) => {
|
|
22
|
+
const {
|
|
23
|
+
reference,
|
|
24
|
+
} = childRef,
|
|
25
|
+
found = _.find(childrenRef.current, (ref, ix) => ix === reference);
|
|
26
|
+
return !!found;
|
|
27
|
+
},
|
|
20
28
|
registerChild: (childRef) => {
|
|
21
29
|
const {
|
|
22
30
|
reference,
|
|
@@ -38,7 +46,12 @@ export default function withComponent(WrappedComponent) {
|
|
|
38
46
|
});
|
|
39
47
|
|
|
40
48
|
useEffect(() => {
|
|
41
|
-
if (
|
|
49
|
+
if (componentMethods) {
|
|
50
|
+
_.each(componentMethods, (method, name) => {
|
|
51
|
+
selfRef.current[name] = method;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
if (parent && reference && !parent.hasChild(selfRef.current)) {
|
|
42
55
|
parent.registerChild(selfRef.current);
|
|
43
56
|
}
|
|
44
57
|
return () => {
|
|
@@ -28,6 +28,9 @@ export default function withData(WrappedComponent) {
|
|
|
28
28
|
displayField = 'value',
|
|
29
29
|
idIx,
|
|
30
30
|
displayIx,
|
|
31
|
+
|
|
32
|
+
// withComponent
|
|
33
|
+
self,
|
|
31
34
|
} = props,
|
|
32
35
|
propsToPass = _.omit(props, ['model']), // passing 'model' would mess things up if withData gets called twice (e.g. withData(...withData(...)) ), as we'd be trying to recreate Repository twice
|
|
33
36
|
localIdIx = idIx || (fields && idField ? fields.indexOf(idField) : null),
|
|
@@ -64,6 +67,9 @@ export default function withData(WrappedComponent) {
|
|
|
64
67
|
if (setRepository) { // pass it on up to higher components
|
|
65
68
|
setRepository(Repository);
|
|
66
69
|
}
|
|
70
|
+
if (self) {
|
|
71
|
+
self.repository = Repository;
|
|
72
|
+
}
|
|
67
73
|
setIsReady(true);
|
|
68
74
|
})();
|
|
69
75
|
|
|
@@ -22,6 +22,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
22
22
|
disableDelete = false,
|
|
23
23
|
disableDuplicate = false,
|
|
24
24
|
disableView = false,
|
|
25
|
+
useRemoteDuplicate = false, // call specific copyToNew function on server, rather than simple duplicate on client
|
|
25
26
|
getRecordIdentifier = (selection) => {
|
|
26
27
|
if (selection.length > 1) {
|
|
27
28
|
return 'records?';
|
|
@@ -224,6 +225,9 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
224
225
|
if (selection.length !== 1) {
|
|
225
226
|
return;
|
|
226
227
|
}
|
|
228
|
+
if (useRemoteDuplicate) {
|
|
229
|
+
return onRemoteDuplicate();
|
|
230
|
+
}
|
|
227
231
|
const
|
|
228
232
|
entity = selection[0],
|
|
229
233
|
idProperty = Repository.getSchema().model.idProperty,
|
|
@@ -233,6 +237,40 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
233
237
|
setEditorMode(EDITOR_MODE__EDIT);
|
|
234
238
|
setIsEditorShown(true);
|
|
235
239
|
},
|
|
240
|
+
onRemoteDuplicate = async () => {
|
|
241
|
+
|
|
242
|
+
// Call /duplicate on server
|
|
243
|
+
const
|
|
244
|
+
Model = Repository.getSchema().name,
|
|
245
|
+
entity = selection[0],
|
|
246
|
+
id = entity.id;
|
|
247
|
+
const result = await Repository._send('POST', Model + '/duplicate', { id });
|
|
248
|
+
const {
|
|
249
|
+
root,
|
|
250
|
+
success,
|
|
251
|
+
total,
|
|
252
|
+
message
|
|
253
|
+
} = Repository._processServerResponse(result);
|
|
254
|
+
|
|
255
|
+
if (!success) {
|
|
256
|
+
throw Error(message);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const duplicateId = root.id;
|
|
260
|
+
|
|
261
|
+
// Filter the grid with only the duplicate's ID, and open it for editing.
|
|
262
|
+
self.filterById(duplicateId, () => { // because of the way useFilters is made, we have to use a callback, not await a Promise.
|
|
263
|
+
|
|
264
|
+
// Select the only node
|
|
265
|
+
const duplicateEntity = Repository.getById(duplicateId);
|
|
266
|
+
self.setSelection([duplicateEntity]);
|
|
267
|
+
|
|
268
|
+
// edit it
|
|
269
|
+
onEdit();
|
|
270
|
+
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
},
|
|
236
274
|
onEditorSave = async (data, e) => {
|
|
237
275
|
const
|
|
238
276
|
what = record || selection,
|
|
@@ -343,12 +381,12 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
343
381
|
}, [selection]);
|
|
344
382
|
|
|
345
383
|
if (self) {
|
|
346
|
-
self.
|
|
347
|
-
self.
|
|
348
|
-
self.
|
|
349
|
-
self.
|
|
350
|
-
self.
|
|
351
|
-
self.
|
|
384
|
+
self.add = onAdd;
|
|
385
|
+
self.edit = onEdit;
|
|
386
|
+
self.delete = onDelete;
|
|
387
|
+
self.moveChildren = onMoveChildren;
|
|
388
|
+
self.deleteChildren = onDeleteChildren;
|
|
389
|
+
self.duplicate = onDuplicate;
|
|
352
390
|
}
|
|
353
391
|
|
|
354
392
|
if (lastSelection !== selection) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useEffect, useId, } from 'react';
|
|
1
|
+
import { useState, useEffect, useId, useRef, } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Column,
|
|
4
4
|
Modal,
|
|
@@ -46,6 +46,9 @@ export default function withFilters(WrappedComponent) {
|
|
|
46
46
|
|
|
47
47
|
// withData
|
|
48
48
|
Repository,
|
|
49
|
+
|
|
50
|
+
// withComponent
|
|
51
|
+
self,
|
|
49
52
|
} = props;
|
|
50
53
|
|
|
51
54
|
let modal = null,
|
|
@@ -59,7 +62,7 @@ export default function withFilters(WrappedComponent) {
|
|
|
59
62
|
defaultFilters: modelDefaultFilters,
|
|
60
63
|
ancillaryFilters: modelAncillaryFilters,
|
|
61
64
|
} = Repository.getSchema().model,
|
|
62
|
-
id = props.id || useId(),
|
|
65
|
+
id = props.id || props.reference || useId(),
|
|
63
66
|
|
|
64
67
|
// determine the starting filters
|
|
65
68
|
startingFilters = !_.isEmpty(customFilters) ? customFilters : // custom filters override component filters
|
|
@@ -124,6 +127,7 @@ export default function withFilters(WrappedComponent) {
|
|
|
124
127
|
}
|
|
125
128
|
|
|
126
129
|
const
|
|
130
|
+
filterCallbackRef = useRef(),
|
|
127
131
|
[filters, setFiltersRaw] = useState(formattedStartingFilters), // array of formatted filters
|
|
128
132
|
[slots, setSlots] = useState(startingSlots), // array of field names user is currently filtering on; blank slots have a null entry in array
|
|
129
133
|
[modalFilters, setModalFilters] = useState([]),
|
|
@@ -230,6 +234,21 @@ export default function withFilters(WrappedComponent) {
|
|
|
230
234
|
}
|
|
231
235
|
return inArray(filterType, ['NumberRange', 'DateRange']);
|
|
232
236
|
},
|
|
237
|
+
filterById = (id, cb) => {
|
|
238
|
+
onClearFilters();
|
|
239
|
+
filterCallbackRef.current = cb; // store the callback, so we can call it the next time this HOC renders with new filters
|
|
240
|
+
const newFilters = _.clone(filters);
|
|
241
|
+
_.remove(newFilters, (filter) => {
|
|
242
|
+
return filter.field === 'q';
|
|
243
|
+
});
|
|
244
|
+
newFilters.unshift({
|
|
245
|
+
field: 'q',
|
|
246
|
+
title: 'Search all text fields',
|
|
247
|
+
type: 'Input',
|
|
248
|
+
value: 'id:' + id,
|
|
249
|
+
});
|
|
250
|
+
setFilters(newFilters, false, false);
|
|
251
|
+
},
|
|
233
252
|
renderFilters = () => {
|
|
234
253
|
const
|
|
235
254
|
filterProps = {
|
|
@@ -347,7 +366,7 @@ export default function withFilters(WrappedComponent) {
|
|
|
347
366
|
} else {
|
|
348
367
|
const
|
|
349
368
|
isAncillary = type === FILTER_TYPE_ANCILLARY,
|
|
350
|
-
filterName =
|
|
369
|
+
filterName = (isAncillary ? 'ancillary___' : '') + field;
|
|
351
370
|
newFilterNames.push(filterName);
|
|
352
371
|
newRepoFilters.push({ name: filterName, value, });
|
|
353
372
|
}
|
|
@@ -362,12 +381,17 @@ export default function withFilters(WrappedComponent) {
|
|
|
362
381
|
setPreviousFilterNames(newFilterNames);
|
|
363
382
|
}
|
|
364
383
|
|
|
365
|
-
Repository.filter(newRepoFilters, null, false); // false so other filters remain
|
|
366
|
-
|
|
367
384
|
if (searchAllText && Repository.searchAncillary && !Repository.hasBaseParam('searchAncillary')) {
|
|
368
385
|
Repository.setBaseParam('searchAncillary', true);
|
|
369
386
|
}
|
|
370
387
|
|
|
388
|
+
await Repository.filter(newRepoFilters, null, false); // false so other filters remain
|
|
389
|
+
|
|
390
|
+
if (filterCallbackRef.current) {
|
|
391
|
+
filterCallbackRef.current(); // call the callback
|
|
392
|
+
filterCallbackRef.current = null; // clear the callback
|
|
393
|
+
}
|
|
394
|
+
|
|
371
395
|
if (!isReady) {
|
|
372
396
|
setIsReady(true);
|
|
373
397
|
}
|
|
@@ -378,6 +402,11 @@ export default function withFilters(WrappedComponent) {
|
|
|
378
402
|
return null;
|
|
379
403
|
}
|
|
380
404
|
|
|
405
|
+
if (self) {
|
|
406
|
+
self.filterById = filterById;
|
|
407
|
+
self.setFilters = setFilters;
|
|
408
|
+
}
|
|
409
|
+
|
|
381
410
|
const
|
|
382
411
|
renderedFilters = renderFilters(),
|
|
383
412
|
hasFilters = !!renderedFilters.length;
|
|
@@ -499,6 +528,7 @@ export default function withFilters(WrappedComponent) {
|
|
|
499
528
|
editorType={EDITOR_TYPE__PLAIN}
|
|
500
529
|
flex={1}
|
|
501
530
|
startingValues={formStartingValues}
|
|
531
|
+
minHeight={minHeight}
|
|
502
532
|
items={[
|
|
503
533
|
{
|
|
504
534
|
type: 'Column',
|
|
@@ -158,8 +158,10 @@ export default function withPdfButton(WrappedComponent) {
|
|
|
158
158
|
setIsModalShown(true);
|
|
159
159
|
},
|
|
160
160
|
};
|
|
161
|
-
|
|
162
|
-
|
|
161
|
+
if (!_.find(additionalEditButtons, btn => button.key === btn.key)) {
|
|
162
|
+
additionalEditButtons.push(button);
|
|
163
|
+
}
|
|
164
|
+
if (!_.find(additionalViewButtons, btn => button.key === btn.key)) {
|
|
163
165
|
additionalViewButtons.push(button);
|
|
164
166
|
}
|
|
165
167
|
|
|
@@ -27,6 +27,9 @@ export default function withSelection(WrappedComponent) {
|
|
|
27
27
|
autoSelectFirstItem = false,
|
|
28
28
|
fireEvent,
|
|
29
29
|
|
|
30
|
+
// withComponent
|
|
31
|
+
self,
|
|
32
|
+
|
|
30
33
|
// withValue
|
|
31
34
|
value,
|
|
32
35
|
setValue,
|
|
@@ -309,6 +312,19 @@ export default function withSelection(WrappedComponent) {
|
|
|
309
312
|
|
|
310
313
|
}, []);
|
|
311
314
|
|
|
315
|
+
if (self) {
|
|
316
|
+
self.setSelection = setSelection;
|
|
317
|
+
self.selectNext = selectNext;
|
|
318
|
+
self.selectPrev = selectPrev;
|
|
319
|
+
self.addToSelection = addToSelection;
|
|
320
|
+
self.removeFromSelection = removeFromSelection;
|
|
321
|
+
self.deselectAll = deselectAll;
|
|
322
|
+
self.selectRangeTo = selectRangeTo;
|
|
323
|
+
self.isInSelection = isInSelection;
|
|
324
|
+
self.getIdsFromLocalSelection = getIdsFromLocalSelection;
|
|
325
|
+
self.getDisplayValuesFromSelection = getDisplayValuesFromLocalSelection;
|
|
326
|
+
}
|
|
327
|
+
|
|
312
328
|
if (usesWithValue) {
|
|
313
329
|
useEffect(() => {
|
|
314
330
|
if (!isReady) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useRef, } from 'react';
|
|
1
2
|
import {
|
|
2
3
|
Column,
|
|
3
4
|
Icon,
|
|
@@ -45,6 +46,7 @@ function Viewer(props) {
|
|
|
45
46
|
selectorSelected,
|
|
46
47
|
|
|
47
48
|
} = props,
|
|
49
|
+
scrollViewRef = useRef(),
|
|
48
50
|
isMultiple = _.isArray(record),
|
|
49
51
|
isSideEditor = editorType === EDITOR_TYPE__SIDE,
|
|
50
52
|
styles = UiGlobals.styles,
|
|
@@ -205,13 +207,17 @@ function Viewer(props) {
|
|
|
205
207
|
return additionalButtons;
|
|
206
208
|
};
|
|
207
209
|
|
|
210
|
+
if (self) {
|
|
211
|
+
self.ref = scrollViewRef;
|
|
212
|
+
}
|
|
213
|
+
|
|
208
214
|
const
|
|
209
215
|
showDeleteBtn = onDelete && viewerCanDelete,
|
|
210
216
|
showCloseBtn = !isSideEditor,
|
|
211
217
|
additionalButtons = buildAdditionalButtons();
|
|
212
218
|
|
|
213
219
|
return <Column flex={flex} {...props}>
|
|
214
|
-
<ScrollView width="100%" _web={{ height: 1 }}>
|
|
220
|
+
<ScrollView width="100%" _web={{ height: 1 }} ref={scrollViewRef}>
|
|
215
221
|
<Column p={4}>
|
|
216
222
|
{onEditMode && <Row mb={4} justifyContent="flex-end">
|
|
217
223
|
<Button
|