@onehat/ui 0.3.11 → 0.3.17
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/Editor/Editor.js +7 -3
- package/src/Components/Editor/Viewer.js +38 -21
- package/src/Components/Form/Field/Combo/Combo.js +7 -14
- package/src/Components/Form/Field/Combo/Tag.js +18 -7
- package/src/Components/Form/Field/Text.js +1 -2
- package/src/Components/Form/Form.js +43 -7
- package/src/Components/Grid/Grid.js +46 -53
- package/src/Components/Grid/GridHeaderRow.js +16 -10
- package/src/Components/Grid/GridRow.js +11 -7
- package/src/Components/Hoc/withEditor.js +21 -4
- package/src/Components/Hoc/withFilters.js +4 -1
- package/src/Components/Hoc/withPresetButtons.js +2 -3
- package/src/Components/Hoc/withSelection.js +37 -17
- package/src/Components/Hoc/withValue.js +56 -8
- package/src/Components/Panel/Panel.js +3 -1
- package/src/Components/Screens/Manager.js +3 -1
- package/src/Constants/Styles.js +1 -0
- package/src/Functions/setCustomInflector.js +6 -0
- package/src/PlatformImports/Web/Attachments.js +1 -1
- package/src/UiGlobals.js +1 -0
- package/src/Components/Form/Field/Tag.js +0 -11
package/package.json
CHANGED
|
@@ -29,13 +29,17 @@ export default function Editor(props) {
|
|
|
29
29
|
|
|
30
30
|
if (_.isEmpty(selection)) {
|
|
31
31
|
return null; // hide the editor when no selection
|
|
32
|
-
return <Box {...props} bg="#ddd" />;
|
|
33
32
|
}
|
|
34
33
|
|
|
35
|
-
|
|
34
|
+
// Repository?.isRemotePhantomMode && selection.length === 1 &&
|
|
35
|
+
if (editorMode === EDITOR_MODE__VIEW) {
|
|
36
|
+
const record = selection[0];
|
|
37
|
+
if (record.isDestroyed) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
36
40
|
return <Viewer
|
|
37
41
|
{...props}
|
|
38
|
-
record={
|
|
42
|
+
record={record}
|
|
39
43
|
onEditMode={isEditorViewOnly ? null : onEditMode}
|
|
40
44
|
onClose={onClose}
|
|
41
45
|
onDelete={onDelete}
|
|
@@ -6,6 +6,9 @@ import {
|
|
|
6
6
|
Row,
|
|
7
7
|
Text,
|
|
8
8
|
} from 'native-base';
|
|
9
|
+
import {
|
|
10
|
+
EDITOR_TYPE__SIDE,
|
|
11
|
+
} from '../../Constants/Editor.js';
|
|
9
12
|
import UiGlobals from '../../UiGlobals.js';
|
|
10
13
|
import getComponentFromType from '../../Functions/getComponentFromType.js';
|
|
11
14
|
import Label from '../Form/Label.js';
|
|
@@ -29,10 +32,12 @@ export default function Viewer(props) {
|
|
|
29
32
|
selectorSelected,
|
|
30
33
|
|
|
31
34
|
// withEditor
|
|
35
|
+
editorType,
|
|
32
36
|
onEditMode,
|
|
33
37
|
onClose,
|
|
34
38
|
onDelete,
|
|
35
39
|
} = props,
|
|
40
|
+
isSideEditor = editorType === EDITOR_TYPE__SIDE,
|
|
36
41
|
styles = UiGlobals.styles,
|
|
37
42
|
flex = props.flex || 1,
|
|
38
43
|
buildAncillary = () => {
|
|
@@ -45,12 +50,18 @@ export default function Viewer(props) {
|
|
|
45
50
|
selectorId = null,
|
|
46
51
|
...propsToPass
|
|
47
52
|
} = item;
|
|
53
|
+
if (!propsToPass.h) {
|
|
54
|
+
propsToPass.h = 400;
|
|
55
|
+
}
|
|
48
56
|
const
|
|
49
57
|
Element = getComponentFromType(type),
|
|
50
58
|
element = <Element
|
|
51
59
|
selectorId={selectorId}
|
|
52
60
|
selectorSelected={selectorSelected || record}
|
|
53
61
|
flex={1}
|
|
62
|
+
h={350}
|
|
63
|
+
canEditorViewOnly={true}
|
|
64
|
+
uniqueRepository={true}
|
|
54
65
|
{...propsToPass}
|
|
55
66
|
/>;
|
|
56
67
|
if (title) {
|
|
@@ -65,6 +76,10 @@ export default function Viewer(props) {
|
|
|
65
76
|
return components;
|
|
66
77
|
};
|
|
67
78
|
|
|
79
|
+
const
|
|
80
|
+
showDeleteBtn = onDelete && viewerCanDelete,
|
|
81
|
+
showCloseBtn = !isSideEditor;
|
|
82
|
+
|
|
68
83
|
return <Column flex={flex} {...props}>
|
|
69
84
|
<ScrollView width="100%" _web={{ height: 1 }}>
|
|
70
85
|
<Column m={2}>
|
|
@@ -88,26 +103,28 @@ export default function Viewer(props) {
|
|
|
88
103
|
|
|
89
104
|
</Column>
|
|
90
105
|
</ScrollView>
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
<
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
106
|
+
{(showDeleteBtn || showCloseBtn) &&
|
|
107
|
+
<Footer justifyContent="flex-end">
|
|
108
|
+
{showDeleteBtn &&
|
|
109
|
+
<Row flex={1} justifyContent="flex-start">
|
|
110
|
+
<Button
|
|
111
|
+
key="deleteBtn"
|
|
112
|
+
onPress={onDelete}
|
|
113
|
+
bg="warning"
|
|
114
|
+
_hover={{
|
|
115
|
+
bg: 'warningHover',
|
|
116
|
+
}}
|
|
117
|
+
color="#fff"
|
|
118
|
+
>Delete</Button>
|
|
119
|
+
</Row>}
|
|
120
|
+
{showCloseBtn &&
|
|
121
|
+
<Button.Group space={2}>
|
|
122
|
+
<Button
|
|
123
|
+
key="closeBtn"
|
|
124
|
+
onPress={onClose}
|
|
125
|
+
color="#fff"
|
|
126
|
+
>Close</Button>
|
|
127
|
+
</Button.Group>}
|
|
128
|
+
</Footer>}
|
|
112
129
|
</Column>;
|
|
113
130
|
}
|
|
@@ -34,7 +34,6 @@ export function ComboComponent(props) {
|
|
|
34
34
|
tooltip = null,
|
|
35
35
|
menuMinWidth = 150,
|
|
36
36
|
disableDirectEntry = false,
|
|
37
|
-
disablePagination = true,
|
|
38
37
|
hideMenuOnSelection = true,
|
|
39
38
|
_input = {},
|
|
40
39
|
isEditor = false,
|
|
@@ -55,7 +54,7 @@ export function ComboComponent(props) {
|
|
|
55
54
|
selectionMode,
|
|
56
55
|
selectNext,
|
|
57
56
|
selectPrev,
|
|
58
|
-
|
|
57
|
+
getDisplayValuesFromSelection,
|
|
59
58
|
|
|
60
59
|
tooltipPlacement = 'bottom',
|
|
61
60
|
} = props,
|
|
@@ -90,16 +89,12 @@ export function ComboComponent(props) {
|
|
|
90
89
|
if (rectTop !== top) {
|
|
91
90
|
setTop(rectTop);
|
|
92
91
|
}
|
|
93
|
-
setHeight(null);
|
|
94
92
|
} else {
|
|
95
93
|
// Menu is above the combo
|
|
96
|
-
|
|
97
|
-
const rectTop = rect.top -200;
|
|
94
|
+
const rectTop = rect.top - styles.FORM_COMBO_MENU_HEIGHT;
|
|
98
95
|
if (rectTop !== top) {
|
|
99
96
|
setTop(rectTop);
|
|
100
97
|
}
|
|
101
|
-
|
|
102
|
-
setHeight(200);
|
|
103
98
|
}
|
|
104
99
|
if (rect.left !== left) {
|
|
105
100
|
setLeft(rect.left);
|
|
@@ -322,7 +317,7 @@ export function ComboComponent(props) {
|
|
|
322
317
|
if (found) {
|
|
323
318
|
const
|
|
324
319
|
newSelection = [found],
|
|
325
|
-
newTextValue =
|
|
320
|
+
newTextValue = getDisplayValuesFromSelection(newSelection);
|
|
326
321
|
|
|
327
322
|
setTextValue(newTextValue);
|
|
328
323
|
setSelection(newSelection);
|
|
@@ -355,7 +350,7 @@ export function ComboComponent(props) {
|
|
|
355
350
|
}
|
|
356
351
|
|
|
357
352
|
// Adjust text input to match selection
|
|
358
|
-
let localTextValue =
|
|
353
|
+
let localTextValue = getDisplayValuesFromSelection(selection);
|
|
359
354
|
if (!_.isEqual(localTextValue, textValue)) {
|
|
360
355
|
setTextValue(localTextValue);
|
|
361
356
|
}
|
|
@@ -489,14 +484,12 @@ export function ComboComponent(props) {
|
|
|
489
484
|
top={top + 'px'}
|
|
490
485
|
left={left + 'px'}
|
|
491
486
|
w={width + 'px'}
|
|
492
|
-
h={height ? height + 'px' : null}
|
|
493
487
|
minWidth={menuMinWidth}
|
|
494
488
|
overflow="auto"
|
|
495
489
|
bg="#fff"
|
|
496
490
|
>
|
|
497
491
|
<Popover.Body
|
|
498
492
|
ref={menuRef}
|
|
499
|
-
maxHeight={200}
|
|
500
493
|
borderWidth={1}
|
|
501
494
|
borderColor='trueGray.400'
|
|
502
495
|
borderTopWidth={0}
|
|
@@ -505,8 +498,6 @@ export function ComboComponent(props) {
|
|
|
505
498
|
<WhichGrid
|
|
506
499
|
showHeaders={false}
|
|
507
500
|
showHovers={true}
|
|
508
|
-
pageSize={100}
|
|
509
|
-
disableAdjustingPageSizeToHeight={true}
|
|
510
501
|
shadow={1}
|
|
511
502
|
getRowProps={() => {
|
|
512
503
|
return {
|
|
@@ -518,9 +509,11 @@ export function ComboComponent(props) {
|
|
|
518
509
|
w: '100%',
|
|
519
510
|
};
|
|
520
511
|
}}
|
|
512
|
+
allowToggleSelection={true}
|
|
513
|
+
disableAdjustingPageSizeToHeight={true}
|
|
521
514
|
{...props}
|
|
515
|
+
h={styles.FORM_COMBO_MENU_HEIGHT + 'px'}
|
|
522
516
|
disablePresetButtons={!isEditor}
|
|
523
|
-
disablePagination={disablePagination}
|
|
524
517
|
setSelection={(selection) => {
|
|
525
518
|
// Decorator fn to add local functionality
|
|
526
519
|
// Close the menu when row is selected on grid
|
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SELECTION_MODE_MULTI,
|
|
3
3
|
} from '../../../../Constants/Selection.js';
|
|
4
|
-
import Combo from './Combo.js';
|
|
4
|
+
import Combo, { ComboEditor } from './Combo.js';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
return
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
function withAdditionalProps(WrappedComponent) {
|
|
7
|
+
return (props) => {
|
|
8
|
+
return <WrappedComponent
|
|
9
|
+
selectionMode={SELECTION_MODE_MULTI}
|
|
10
|
+
valueIsAlwaysArray={true}
|
|
11
|
+
valueAsIdAndText={true}
|
|
12
|
+
valueAsStringifiedJson={true}
|
|
13
|
+
disableDirectEntry={true}
|
|
14
|
+
pageSize={500}
|
|
15
|
+
{...props}
|
|
16
|
+
/>;
|
|
17
|
+
};
|
|
12
18
|
}
|
|
19
|
+
|
|
20
|
+
const Tag = withAdditionalProps(Combo);
|
|
21
|
+
export const TagEditor = withAdditionalProps(ComboEditor);
|
|
22
|
+
|
|
23
|
+
export default Tag;
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
} from 'native-base';
|
|
5
5
|
import UiGlobals from '../../../UiGlobals.js';
|
|
6
6
|
import withTooltip from '../../Hoc/withTooltip.js';
|
|
7
|
-
import withValue from '../../Hoc/withValue.js';
|
|
8
7
|
|
|
9
8
|
const
|
|
10
9
|
TextElement = (props) => {
|
|
@@ -21,7 +20,7 @@ const
|
|
|
21
20
|
{...props}
|
|
22
21
|
>{props.value}</Text>;
|
|
23
22
|
},
|
|
24
|
-
TextField =
|
|
23
|
+
TextField = TextElement; // NOT using withValue on Text element, as this element is simply for display purposes!
|
|
25
24
|
|
|
26
25
|
// Tooltip needs us to forwardRef
|
|
27
26
|
export default withTooltip(React.forwardRef((props, ref) => {
|
|
@@ -82,6 +82,7 @@ function Form(props) {
|
|
|
82
82
|
|
|
83
83
|
// withEditor
|
|
84
84
|
isEditorViewOnly = false,
|
|
85
|
+
isSaving = false,
|
|
85
86
|
editorMode,
|
|
86
87
|
onCancel,
|
|
87
88
|
onSave,
|
|
@@ -102,6 +103,7 @@ function Form(props) {
|
|
|
102
103
|
isMultiple = _.isArray(record),
|
|
103
104
|
isSingle = !isMultiple, // for convenience
|
|
104
105
|
forceUpdate = useForceUpdate(),
|
|
106
|
+
[previousRecord, setPreviousRecord] = useState(record),
|
|
105
107
|
initialValues = _.merge(startingValues, (record && !record.isDestroyed ? record.submitValues : {})),
|
|
106
108
|
defaultValues = isMultiple ? getNullFieldValues(initialValues, Repository) : initialValues, // when multiple entities, set all default values to null
|
|
107
109
|
{
|
|
@@ -162,6 +164,7 @@ function Form(props) {
|
|
|
162
164
|
renderer,
|
|
163
165
|
w,
|
|
164
166
|
flex,
|
|
167
|
+
useSelectorId = false,
|
|
165
168
|
} = config;
|
|
166
169
|
|
|
167
170
|
if (!isEditable) {
|
|
@@ -213,6 +216,11 @@ function Form(props) {
|
|
|
213
216
|
}
|
|
214
217
|
const Element = getComponentFromType(editor);
|
|
215
218
|
|
|
219
|
+
if (useSelectorId) {
|
|
220
|
+
editorProps.selectorId = selectorId;
|
|
221
|
+
editorProps.selectorSelected = editorProps;
|
|
222
|
+
}
|
|
223
|
+
|
|
216
224
|
let element = <Element
|
|
217
225
|
name={name}
|
|
218
226
|
value={value}
|
|
@@ -223,8 +231,6 @@ function Form(props) {
|
|
|
223
231
|
}
|
|
224
232
|
}}
|
|
225
233
|
onBlur={onBlur}
|
|
226
|
-
selectorId={selectorId}
|
|
227
|
-
selectorSelected={selectorSelected}
|
|
228
234
|
flex={1}
|
|
229
235
|
{...editorProps}
|
|
230
236
|
// {...defaults}
|
|
@@ -261,6 +267,7 @@ function Form(props) {
|
|
|
261
267
|
label,
|
|
262
268
|
items,
|
|
263
269
|
onChange: onEditorChange,
|
|
270
|
+
useSelectorId = false,
|
|
264
271
|
...propsToPass
|
|
265
272
|
} = item;
|
|
266
273
|
let editorTypeProps = {};
|
|
@@ -365,6 +372,11 @@ function Form(props) {
|
|
|
365
372
|
if (isValidElement(Element)) {
|
|
366
373
|
throw new Error('Should not yet be valid React element. Did you use <Element> instead of () => <Element> when defining it?')
|
|
367
374
|
}
|
|
375
|
+
|
|
376
|
+
if (useSelectorId) {
|
|
377
|
+
editorTypeProps.selectorId = selectorId;
|
|
378
|
+
editorTypeProps.selectorSelected = editorProps;
|
|
379
|
+
}
|
|
368
380
|
let element = <Element
|
|
369
381
|
name={name}
|
|
370
382
|
value={value}
|
|
@@ -375,8 +387,6 @@ function Form(props) {
|
|
|
375
387
|
}
|
|
376
388
|
}}
|
|
377
389
|
onBlur={onBlur}
|
|
378
|
-
selectorId={selectorId}
|
|
379
|
-
selectorSelected={selectorSelected}
|
|
380
390
|
flex={1}
|
|
381
391
|
{...defaults}
|
|
382
392
|
{...propsToPass}
|
|
@@ -420,12 +430,16 @@ function Form(props) {
|
|
|
420
430
|
selectorId,
|
|
421
431
|
...propsToPass
|
|
422
432
|
} = item;
|
|
433
|
+
if (!propsToPass.h) {
|
|
434
|
+
propsToPass.h = 400;
|
|
435
|
+
}
|
|
423
436
|
const
|
|
424
437
|
Element = getComponentFromType(type),
|
|
425
438
|
element = <Element
|
|
426
439
|
selectorId={selectorId}
|
|
427
440
|
selectorSelected={selectorSelected || record}
|
|
428
441
|
flex={1}
|
|
442
|
+
uniqueRepository={true}
|
|
429
443
|
{...propsToPass}
|
|
430
444
|
/>;
|
|
431
445
|
if (title) {
|
|
@@ -444,8 +458,23 @@ function Form(props) {
|
|
|
444
458
|
if (editorType === EDITOR_TYPE__INLINE) {
|
|
445
459
|
alert(errors.message);
|
|
446
460
|
}
|
|
461
|
+
},
|
|
462
|
+
onSaveDecorated = async (data, e) => {
|
|
463
|
+
// reset the form after a save
|
|
464
|
+
const result = await onSave(data, e);
|
|
465
|
+
if (result) {
|
|
466
|
+
const values = record.submitValues;
|
|
467
|
+
reset(values);
|
|
468
|
+
}
|
|
447
469
|
};
|
|
448
470
|
|
|
471
|
+
useEffect(() => {
|
|
472
|
+
if (record !== previousRecord) {
|
|
473
|
+
setPreviousRecord(record);
|
|
474
|
+
reset(defaultValues);
|
|
475
|
+
}
|
|
476
|
+
}, [record]);
|
|
477
|
+
|
|
449
478
|
useEffect(() => {
|
|
450
479
|
if (!Repository) {
|
|
451
480
|
return () => {};
|
|
@@ -487,6 +516,13 @@ function Form(props) {
|
|
|
487
516
|
sizeProps.maxHeight = maxHeight;
|
|
488
517
|
}
|
|
489
518
|
|
|
519
|
+
const savingProps = {};
|
|
520
|
+
if (isSaving) {
|
|
521
|
+
savingProps.borderTopWidth = 2;
|
|
522
|
+
savingProps.borderTopColor = '#f00';
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
|
|
490
526
|
let formComponents,
|
|
491
527
|
editor;
|
|
492
528
|
if (editorType === EDITOR_TYPE__INLINE) {
|
|
@@ -570,7 +606,7 @@ function Form(props) {
|
|
|
570
606
|
|
|
571
607
|
{editor}
|
|
572
608
|
|
|
573
|
-
<Footer justifyContent="flex-end" {...footerProps}>
|
|
609
|
+
<Footer justifyContent="flex-end" {...footerProps} {...savingProps}>
|
|
574
610
|
{onDelete && editorMode === EDITOR_MODE__EDIT &&
|
|
575
611
|
<Row flex={1} justifyContent="flex-start">
|
|
576
612
|
<Button
|
|
@@ -603,11 +639,11 @@ function Form(props) {
|
|
|
603
639
|
>Cancel</Button>}
|
|
604
640
|
{!isEditorViewOnly && onSave && <Button
|
|
605
641
|
key="saveBtn"
|
|
606
|
-
onPress={(e) => handleSubmit(
|
|
642
|
+
onPress={(e) => handleSubmit(onSaveDecorated, onSubmitError)(e)}
|
|
607
643
|
isDisabled={isSaveDisabled}
|
|
608
644
|
color="#fff"
|
|
609
645
|
>{editorMode === EDITOR_MODE__ADD ? 'Add' : 'Save'}</Button>}
|
|
610
|
-
{isEditorViewOnly && onClose && <Button
|
|
646
|
+
{isEditorViewOnly && onClose && editorType !== EDITOR_TYPE__SIDE && <Button
|
|
611
647
|
key="closeBtn"
|
|
612
648
|
onPress={onClose}
|
|
613
649
|
color="#fff"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState, useEffect, useRef, useMemo, } from 'react';
|
|
1
|
+
import React, { useState, useEffect, useRef, useMemo, useCallback, } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Column,
|
|
4
4
|
FlatList,
|
|
@@ -86,7 +86,7 @@ function GridComponent(props) {
|
|
|
86
86
|
canColumnsReorder = true,
|
|
87
87
|
canColumnsResize = true,
|
|
88
88
|
canRowsReorder = false,
|
|
89
|
-
allowToggleSelection =
|
|
89
|
+
allowToggleSelection = false, // i.e. single click with no shift key toggles the selection of the item clicked on
|
|
90
90
|
disableBottomToolbar = false,
|
|
91
91
|
disablePagination = false,
|
|
92
92
|
bottomToolbar = 'pagination',
|
|
@@ -104,6 +104,7 @@ function GridComponent(props) {
|
|
|
104
104
|
onDuplicate,
|
|
105
105
|
onReset,
|
|
106
106
|
onContextMenu,
|
|
107
|
+
isAdding,
|
|
107
108
|
|
|
108
109
|
// withData
|
|
109
110
|
Repository,
|
|
@@ -141,6 +142,7 @@ function GridComponent(props) {
|
|
|
141
142
|
styles = UiGlobals.styles,
|
|
142
143
|
forceUpdate = useForceUpdate(),
|
|
143
144
|
gridRef = useRef(),
|
|
145
|
+
isAddingRef = useRef(),
|
|
144
146
|
[isReady, setIsReady] = useState(false),
|
|
145
147
|
[isLoading, setIsLoading] = useState(false),
|
|
146
148
|
[localColumnsConfig, setLocalColumnsConfigRaw] = useState([]),
|
|
@@ -162,6 +164,10 @@ function GridComponent(props) {
|
|
|
162
164
|
shiftKey,
|
|
163
165
|
metaKey,
|
|
164
166
|
} = e;
|
|
167
|
+
let allowToggle = allowToggleSelection;
|
|
168
|
+
if (metaKey) {
|
|
169
|
+
allowToggle = true;
|
|
170
|
+
}
|
|
165
171
|
|
|
166
172
|
if (selectionMode === SELECTION_MODE_MULTI) {
|
|
167
173
|
if (shiftKey) {
|
|
@@ -170,28 +176,18 @@ function GridComponent(props) {
|
|
|
170
176
|
} else {
|
|
171
177
|
selectRangeTo(item);
|
|
172
178
|
}
|
|
173
|
-
} else if (metaKey) {
|
|
174
|
-
if (isInSelection(item)) {
|
|
175
|
-
// Already selected
|
|
176
|
-
if (allowToggleSelection) {
|
|
177
|
-
removeFromSelection(item);
|
|
178
|
-
} else {
|
|
179
|
-
// Do nothing.
|
|
180
|
-
}
|
|
181
|
-
} else {
|
|
182
|
-
addToSelection(item);
|
|
183
|
-
}
|
|
184
179
|
} else {
|
|
185
|
-
if (
|
|
186
|
-
|
|
187
|
-
if (allowToggleSelection) {
|
|
180
|
+
if (allowToggle) {
|
|
181
|
+
if (isInSelection(item)) {
|
|
188
182
|
removeFromSelection(item);
|
|
189
183
|
} else {
|
|
190
|
-
|
|
184
|
+
addToSelection(item);
|
|
191
185
|
}
|
|
192
186
|
} else {
|
|
193
|
-
|
|
194
|
-
|
|
187
|
+
if (!isInSelection(item)) {
|
|
188
|
+
// select just this one
|
|
189
|
+
setSelection([item]);
|
|
190
|
+
}
|
|
195
191
|
}
|
|
196
192
|
}
|
|
197
193
|
} else {
|
|
@@ -199,7 +195,7 @@ function GridComponent(props) {
|
|
|
199
195
|
let newSelection = selection;
|
|
200
196
|
if (isInSelection(item)) {
|
|
201
197
|
// Already selected
|
|
202
|
-
if (
|
|
198
|
+
if (allowToggle) {
|
|
203
199
|
// Create empty selection
|
|
204
200
|
newSelection = [];
|
|
205
201
|
} else {
|
|
@@ -616,41 +612,34 @@ function GridComponent(props) {
|
|
|
616
612
|
setDragRowSlot(null);
|
|
617
613
|
},
|
|
618
614
|
onLayout = (e) => {
|
|
619
|
-
if (disableAdjustingPageSizeToHeight || !Repository) {
|
|
615
|
+
if (disableAdjustingPageSizeToHeight || !Repository || CURRENT_MODE !== UI_MODE_WEB || !gridRef.current || isAddingRef.current) {
|
|
620
616
|
return;
|
|
621
617
|
}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
targetHeight = targetBoundingBox.height,
|
|
633
|
-
firstRow = target.children[0]?.children[0]?.children[0]?.children[0]?.children[0];
|
|
634
|
-
if (firstRow) {
|
|
635
|
-
const
|
|
636
|
-
rowBoundingBox = firstRow.getBoundingClientRect(),
|
|
637
|
-
rowHeight = rowBoundingBox.height,
|
|
638
|
-
rowsPerTarget = Math.floor(targetHeight / rowHeight);
|
|
639
|
-
pageSize = rowsPerTarget;
|
|
640
|
-
if (showHeaders) {
|
|
641
|
-
pageSize--;
|
|
642
|
-
}
|
|
643
|
-
if (bottomToolbar) {
|
|
644
|
-
pageSize--;
|
|
645
|
-
}
|
|
646
|
-
}
|
|
618
|
+
|
|
619
|
+
const
|
|
620
|
+
gr = gridRef.current,
|
|
621
|
+
scrollableNode = gr.getScrollableNode(),
|
|
622
|
+
scrollableNodeBoundingBox = scrollableNode.getBoundingClientRect(),
|
|
623
|
+
scrollableNodeHeight = scrollableNodeBoundingBox.height,
|
|
624
|
+
firstRow = scrollableNode.children[0].children[showHeaders ? 1: 0];
|
|
625
|
+
|
|
626
|
+
if (!firstRow) {
|
|
627
|
+
return;
|
|
647
628
|
}
|
|
648
|
-
|
|
649
|
-
|
|
629
|
+
|
|
630
|
+
const
|
|
631
|
+
rowHeight = firstRow.getBoundingClientRect().height,
|
|
632
|
+
rowsPerContainer = Math.floor(scrollableNodeHeight / rowHeight);
|
|
633
|
+
let pageSize = rowsPerContainer;
|
|
634
|
+
if (showHeaders) {
|
|
635
|
+
pageSize--;
|
|
636
|
+
}
|
|
637
|
+
if (pageSize !== Repository.pageSize) {
|
|
650
638
|
Repository.setPageSize(pageSize);
|
|
651
639
|
}
|
|
652
|
-
}
|
|
653
|
-
|
|
640
|
+
},
|
|
641
|
+
debouncedOnLayout = useCallback(_.debounce(onLayout, 500), []);
|
|
642
|
+
|
|
654
643
|
useEffect(() => {
|
|
655
644
|
|
|
656
645
|
const calculateLocalColumnsConfig = () => {
|
|
@@ -778,6 +767,9 @@ function GridComponent(props) {
|
|
|
778
767
|
|
|
779
768
|
}, [selectorId, selectorSelected]);
|
|
780
769
|
|
|
770
|
+
|
|
771
|
+
isAddingRef.current = isAdding;
|
|
772
|
+
|
|
781
773
|
const footerToolbarItemComponents = useMemo(() => getFooterToolbarItems(), [additionalToolbarButtons, isDragMode]);
|
|
782
774
|
|
|
783
775
|
if (!isReady) {
|
|
@@ -811,23 +803,24 @@ function GridComponent(props) {
|
|
|
811
803
|
} else {
|
|
812
804
|
sizeProps.flex = flex ?? 1;
|
|
813
805
|
}
|
|
806
|
+
|
|
814
807
|
return <Column
|
|
815
808
|
{...testProps('Grid')}
|
|
816
809
|
w="100%"
|
|
817
810
|
bg={bg}
|
|
818
811
|
borderWidth={styles.GRID_BORDER_WIDTH}
|
|
819
812
|
borderColor={styles.GRID_BORDER_COLOR}
|
|
820
|
-
onLayout={
|
|
813
|
+
onLayout={debouncedOnLayout}
|
|
821
814
|
{...sizeProps}
|
|
822
815
|
>
|
|
823
816
|
{topToolbar}
|
|
824
817
|
|
|
825
|
-
<Column w="100%" flex={1} borderTopWidth={isLoading ? 2 : 1} borderTopColor={isLoading ? '#f00' : 'trueGray.300'} onClick={() => {
|
|
818
|
+
<Column w="100%" flex={1} minHeight={40} borderTopWidth={isLoading ? 2 : 1} borderTopColor={isLoading ? '#f00' : 'trueGray.300'} onClick={() => {
|
|
826
819
|
if (!isDragMode && !isInlineEditorShown) {
|
|
827
820
|
deselectAll();
|
|
828
821
|
}
|
|
829
822
|
}}>
|
|
830
|
-
{!entities
|
|
823
|
+
{!entities?.length ? <NoRecordsFound text={noneFoundText} onRefresh={onRefresh} /> :
|
|
831
824
|
<FlatList
|
|
832
825
|
ref={gridRef}
|
|
833
826
|
// ListHeaderComponent={listHeaderComponent}
|
|
@@ -285,7 +285,7 @@ export default function GridHeaderRow(props) {
|
|
|
285
285
|
|
|
286
286
|
// These header Components should match the columns exactly
|
|
287
287
|
// so we can drag/drop them to control the columns.
|
|
288
|
-
const headerColumns = _.map(localColumnsConfig, (config, ix) => {
|
|
288
|
+
const headerColumns = _.map(localColumnsConfig, (config, ix, all) => {
|
|
289
289
|
let {
|
|
290
290
|
columnId,
|
|
291
291
|
fieldName,
|
|
@@ -305,15 +305,21 @@ export default function GridHeaderRow(props) {
|
|
|
305
305
|
borderRightColor: '#fff',
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
-
if (
|
|
309
|
-
propsToPass.w =
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
308
|
+
if (all.length === 1) {
|
|
309
|
+
propsToPass.w = '100%';
|
|
310
|
+
isReorderable = false;
|
|
311
|
+
isResizable = false;
|
|
312
|
+
} else {
|
|
313
|
+
if (w) {
|
|
314
|
+
propsToPass.w = w;
|
|
315
|
+
} else if (flex) {
|
|
316
|
+
propsToPass.flex = flex;
|
|
317
|
+
propsToPass.minWidth = 100;
|
|
318
|
+
} else if (localColumnsConfig.length === 1) {
|
|
319
|
+
// Only one column and flex is not set
|
|
320
|
+
propsToPass.flex = 1;
|
|
321
|
+
if (!header) {
|
|
322
|
+
}
|
|
317
323
|
}
|
|
318
324
|
}
|
|
319
325
|
|
|
@@ -32,15 +32,19 @@ export default function GridRow(props) {
|
|
|
32
32
|
return useMemo(() => {
|
|
33
33
|
const renderColumns = (item) => {
|
|
34
34
|
if (_.isArray(columnsConfig)) {
|
|
35
|
-
return _.map(columnsConfig, (config, key) => {
|
|
35
|
+
return _.map(columnsConfig, (config, key, all) => {
|
|
36
36
|
const propsToPass = columnProps[key] || {};
|
|
37
|
-
if (
|
|
38
|
-
propsToPass.w =
|
|
39
|
-
} else if (config.flex) {
|
|
40
|
-
propsToPass.flex = config.flex;
|
|
41
|
-
propsToPass.minWidth = 100;
|
|
37
|
+
if (all.length === 1) {
|
|
38
|
+
propsToPass.w = '100%';
|
|
42
39
|
} else {
|
|
43
|
-
|
|
40
|
+
if (config.w) {
|
|
41
|
+
propsToPass.w = config.w;
|
|
42
|
+
} else if (config.flex) {
|
|
43
|
+
propsToPass.flex = config.flex;
|
|
44
|
+
propsToPass.minWidth = 100;
|
|
45
|
+
} else {
|
|
46
|
+
propsToPass.flex = 1;
|
|
47
|
+
}
|
|
44
48
|
}
|
|
45
49
|
propsToPass.p = 1;
|
|
46
50
|
propsToPass.justifyContent = 'center';
|
|
@@ -16,7 +16,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
16
16
|
const {
|
|
17
17
|
userCanEdit = true,
|
|
18
18
|
userCanView = true,
|
|
19
|
-
canEditorViewOnly = false,
|
|
19
|
+
canEditorViewOnly = false, // whether the editor can *ever* change state out of 'View' mode
|
|
20
20
|
disableAdd = false,
|
|
21
21
|
disableEdit = false,
|
|
22
22
|
disableDelete = false,
|
|
@@ -49,8 +49,10 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
49
49
|
listeners = useRef({}),
|
|
50
50
|
editorStateRef = useRef(),
|
|
51
51
|
[currentRecord, setCurrentRecord] = useState(null),
|
|
52
|
+
[isAdding, setIsAdding] = useState(false),
|
|
53
|
+
[isSaving, setIsSaving] = useState(false),
|
|
52
54
|
[isEditorShown, setIsEditorShown] = useState(false),
|
|
53
|
-
[isEditorViewOnly, setIsEditorViewOnly] = useState(canEditorViewOnly),
|
|
55
|
+
[isEditorViewOnly, setIsEditorViewOnly] = useState(canEditorViewOnly), // current state of whether editor is in view-only mode
|
|
54
56
|
[lastSelection, setLastSelection] = useState(),
|
|
55
57
|
setSelectionDecorated = (newSelection) => {
|
|
56
58
|
function doIt() {
|
|
@@ -71,7 +73,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
71
73
|
// forceUpdate(); // we don't want to get into an infinite loop of renders. Simply directly assign the listeners in every child render
|
|
72
74
|
},
|
|
73
75
|
onAdd = async () => {
|
|
74
|
-
const defaultValues = Repository.getSchema().
|
|
76
|
+
const defaultValues = Repository.getSchema().getDefaultValues();
|
|
75
77
|
let addValues = _.clone(defaultValues);
|
|
76
78
|
|
|
77
79
|
if (selectorId && !_.isEmpty(selectorSelected)) {
|
|
@@ -105,7 +107,10 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
105
107
|
// Unmap the values, so we can input true originalData
|
|
106
108
|
addValues = Repository.unmapData(addValues);
|
|
107
109
|
|
|
110
|
+
setIsAdding(true);
|
|
111
|
+
setIsSaving(true);
|
|
108
112
|
const entity = await Repository.add(addValues, false, true);
|
|
113
|
+
setIsSaving(false);
|
|
109
114
|
setSelection([entity]);
|
|
110
115
|
setIsEditorViewOnly(false);
|
|
111
116
|
setEditorMode(EDITOR_MODE__ADD);
|
|
@@ -183,6 +188,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
183
188
|
if (getListeners().onAfterDelete) {
|
|
184
189
|
await getListeners().onAfterDelete(selection);
|
|
185
190
|
}
|
|
191
|
+
setSelection([]);
|
|
186
192
|
if (cb) {
|
|
187
193
|
cb();
|
|
188
194
|
}
|
|
@@ -244,12 +250,19 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
244
250
|
await getListeners().onBeforeEditSave(what);
|
|
245
251
|
}
|
|
246
252
|
|
|
253
|
+
setIsSaving(true);
|
|
247
254
|
await Repository.save();
|
|
248
|
-
|
|
255
|
+
setIsSaving(false);
|
|
256
|
+
|
|
257
|
+
setIsAdding(false);
|
|
258
|
+
setEditorMode(EDITOR_MODE__EDIT);
|
|
259
|
+
// setIsEditorShown(false);
|
|
249
260
|
|
|
250
261
|
if (getListeners().onAfterEdit) {
|
|
251
262
|
await getListeners().onAfterEdit(what);
|
|
252
263
|
}
|
|
264
|
+
|
|
265
|
+
return true;
|
|
253
266
|
},
|
|
254
267
|
onEditorCancel = () => {
|
|
255
268
|
async function doIt() {
|
|
@@ -259,6 +272,8 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
259
272
|
if (isSingle && isPhantom) {
|
|
260
273
|
await deleteRecord();
|
|
261
274
|
}
|
|
275
|
+
|
|
276
|
+
setIsAdding(false);
|
|
262
277
|
setEditorMode(EDITOR_MODE__VIEW);
|
|
263
278
|
setIsEditorShown(false);
|
|
264
279
|
}
|
|
@@ -330,6 +345,8 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
330
345
|
setCurrentRecord={setCurrentRecord}
|
|
331
346
|
isEditorShown={isEditorShown}
|
|
332
347
|
isEditorViewOnly={isEditorViewOnly}
|
|
348
|
+
isAdding={isAdding}
|
|
349
|
+
isSaving={isSaving}
|
|
333
350
|
editorMode={editorMode}
|
|
334
351
|
onEditMode={onEditMode}
|
|
335
352
|
onViewMode={onViewMode}
|
|
@@ -59,7 +59,7 @@ export default function withFilters(WrappedComponent) {
|
|
|
59
59
|
defaultFilters: modelDefaultFilters,
|
|
60
60
|
ancillaryFilters: modelAncillaryFilters,
|
|
61
61
|
} = Repository.getSchema().model,
|
|
62
|
-
id = useId(),
|
|
62
|
+
id = props.id || useId(),
|
|
63
63
|
|
|
64
64
|
// determine the starting filters
|
|
65
65
|
startingFilters = !_.isEmpty(customFilters) ? customFilters : // custom filters override component filters
|
|
@@ -235,6 +235,9 @@ export default function withFilters(WrappedComponent) {
|
|
|
235
235
|
const
|
|
236
236
|
filterProps = {
|
|
237
237
|
mx: 1,
|
|
238
|
+
disableAdjustingPageSizeToHeight: true,
|
|
239
|
+
pageSize: 20,
|
|
240
|
+
uniqueRepository: true,
|
|
238
241
|
},
|
|
239
242
|
filterElements = [];
|
|
240
243
|
_.each(filters, (filter, ix) => {
|
|
@@ -62,7 +62,6 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
|
|
|
62
62
|
|
|
63
63
|
// withSelection
|
|
64
64
|
selection,
|
|
65
|
-
setSelection,
|
|
66
65
|
|
|
67
66
|
// parent container
|
|
68
67
|
selectorId,
|
|
@@ -87,7 +86,7 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
|
|
|
87
86
|
}
|
|
88
87
|
break;
|
|
89
88
|
case 'edit':
|
|
90
|
-
if (disableEdit || canEditorViewOnly) {
|
|
89
|
+
if (disableEdit || canEditorViewOnly || isSideEditor) {
|
|
91
90
|
isDisabled = true;
|
|
92
91
|
}
|
|
93
92
|
break;
|
|
@@ -251,7 +250,7 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
|
|
|
251
250
|
}
|
|
252
251
|
|
|
253
252
|
// Send it to clipboard
|
|
254
|
-
navigator
|
|
253
|
+
navigator?.clipboard.writeText(text);
|
|
255
254
|
};
|
|
256
255
|
// onPrint = () => {
|
|
257
256
|
// debugger;
|
|
@@ -38,9 +38,14 @@ export default function withSelection(WrappedComponent) {
|
|
|
38
38
|
displayIx,
|
|
39
39
|
} = props,
|
|
40
40
|
usesWithValue = !!setValue,
|
|
41
|
-
|
|
41
|
+
initialSelection = selection || defaultSelection || [],
|
|
42
|
+
[localSelection, setLocalSelection] = useState(initialSelection),
|
|
42
43
|
[isReady, setIsReady] = useState(selection || false), // if selection is already defined, or value is not null and we don't need to load repository, it's ready
|
|
43
44
|
setSelection = (selection) => {
|
|
45
|
+
if (_.isEqual(selection, localSelection)) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
44
49
|
setLocalSelection(selection);
|
|
45
50
|
if (onChangeSelection) {
|
|
46
51
|
onChangeSelection(selection);
|
|
@@ -175,7 +180,7 @@ export default function withSelection(WrappedComponent) {
|
|
|
175
180
|
});
|
|
176
181
|
return found;
|
|
177
182
|
},
|
|
178
|
-
|
|
183
|
+
getIdsFromLocalSelection = () => {
|
|
179
184
|
if (!localSelection[0]) {
|
|
180
185
|
return null;
|
|
181
186
|
}
|
|
@@ -190,7 +195,7 @@ export default function withSelection(WrappedComponent) {
|
|
|
190
195
|
}
|
|
191
196
|
return values;
|
|
192
197
|
},
|
|
193
|
-
|
|
198
|
+
getDisplayValuesFromLocalSelection = (selection) => {
|
|
194
199
|
if (!selection[0]) {
|
|
195
200
|
return '';
|
|
196
201
|
}
|
|
@@ -203,18 +208,17 @@ export default function withSelection(WrappedComponent) {
|
|
|
203
208
|
})
|
|
204
209
|
.join(', ');
|
|
205
210
|
},
|
|
206
|
-
|
|
211
|
+
conformValueToLocalSelection = () => {
|
|
207
212
|
if (!setValue) {
|
|
208
213
|
return;
|
|
209
214
|
}
|
|
210
|
-
|
|
211
|
-
const localValue =
|
|
215
|
+
|
|
216
|
+
const localValue = getIdsFromLocalSelection();
|
|
212
217
|
if (!_.isEqual(localValue, value)) {
|
|
213
218
|
setValue(localValue);
|
|
214
219
|
}
|
|
215
220
|
},
|
|
216
|
-
conformSelectionToValue = () => {
|
|
217
|
-
// adjust the selection to match the value
|
|
221
|
+
conformSelectionToValue = async () => {
|
|
218
222
|
let newSelection = [];
|
|
219
223
|
if (Repository) {
|
|
220
224
|
// Get entity or entities that match value
|
|
@@ -222,9 +226,20 @@ export default function withSelection(WrappedComponent) {
|
|
|
222
226
|
if (_.isArray(value)) {
|
|
223
227
|
newSelection = Repository.getBy((entity) => inArray(entity.id, value));
|
|
224
228
|
} else {
|
|
225
|
-
|
|
229
|
+
let found = Repository.getById(value);
|
|
226
230
|
if (found) {
|
|
227
231
|
newSelection.push(found);
|
|
232
|
+
} else if (Repository?.isRemote && Repository?.entities.length) {
|
|
233
|
+
|
|
234
|
+
// Value cannot be found in Repository, but actually exists on server
|
|
235
|
+
// Try to get this value from the server directly
|
|
236
|
+
Repository.filter(Repository.schema.model.idProperty, value);
|
|
237
|
+
await Repository.load();
|
|
238
|
+
found = Repository.getById(value);
|
|
239
|
+
if (found) {
|
|
240
|
+
newSelection.push(found);
|
|
241
|
+
}
|
|
242
|
+
|
|
228
243
|
}
|
|
229
244
|
}
|
|
230
245
|
}
|
|
@@ -262,7 +277,8 @@ export default function withSelection(WrappedComponent) {
|
|
|
262
277
|
|
|
263
278
|
(async () => {
|
|
264
279
|
|
|
265
|
-
if (
|
|
280
|
+
if (usesWithValue && Repository?.isRemote
|
|
281
|
+
&& !Repository.isAutoLoad && !Repository.isLoaded && !Repository.isLoading) {
|
|
266
282
|
// on initialization, we can't conformSelectionToValue if the repository is not yet loaded,
|
|
267
283
|
// so first load repo, then conform to value
|
|
268
284
|
await Repository.load();
|
|
@@ -270,7 +286,11 @@ export default function withSelection(WrappedComponent) {
|
|
|
270
286
|
|
|
271
287
|
if (usesWithValue && !_.isNil(value)) {
|
|
272
288
|
|
|
273
|
-
conformSelectionToValue();
|
|
289
|
+
await conformSelectionToValue();
|
|
290
|
+
|
|
291
|
+
} else if (!_.isEmpty(selection)) {
|
|
292
|
+
|
|
293
|
+
conformValueToLocalSelection();
|
|
274
294
|
|
|
275
295
|
} else if (autoSelectFirstItem) {
|
|
276
296
|
let newSelection = [];
|
|
@@ -289,7 +309,6 @@ export default function withSelection(WrappedComponent) {
|
|
|
289
309
|
|
|
290
310
|
}, []);
|
|
291
311
|
|
|
292
|
-
|
|
293
312
|
if (usesWithValue) {
|
|
294
313
|
useEffect(() => {
|
|
295
314
|
if (!isReady) {
|
|
@@ -298,15 +317,16 @@ export default function withSelection(WrappedComponent) {
|
|
|
298
317
|
|
|
299
318
|
conformSelectionToValue();
|
|
300
319
|
|
|
301
|
-
}, [value
|
|
320
|
+
}, [value]);
|
|
302
321
|
|
|
303
322
|
useEffect(() => {
|
|
304
323
|
if (!isReady) {
|
|
305
324
|
return () => {};
|
|
306
325
|
}
|
|
307
326
|
|
|
308
|
-
|
|
309
|
-
|
|
327
|
+
conformValueToLocalSelection();
|
|
328
|
+
|
|
329
|
+
}, [selection]);
|
|
310
330
|
}
|
|
311
331
|
|
|
312
332
|
if (!isReady) {
|
|
@@ -325,8 +345,8 @@ export default function withSelection(WrappedComponent) {
|
|
|
325
345
|
deselectAll={deselectAll}
|
|
326
346
|
selectRangeTo={selectRangeTo}
|
|
327
347
|
isInSelection={isInSelection}
|
|
328
|
-
|
|
329
|
-
|
|
348
|
+
getIdsFromSelection={getIdsFromLocalSelection}
|
|
349
|
+
getDisplayValuesFromSelection={getDisplayValuesFromLocalSelection}
|
|
330
350
|
/>;
|
|
331
351
|
};
|
|
332
352
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useState, useEffect, } from 'react';
|
|
2
|
-
import
|
|
2
|
+
import natsort from 'natsort';
|
|
3
3
|
import _ from 'lodash';
|
|
4
4
|
|
|
5
5
|
// This HOC gives the component value props, primarily for a Form Field.
|
|
@@ -19,6 +19,9 @@ export default function withValue(WrappedComponent) {
|
|
|
19
19
|
onChangeValue,
|
|
20
20
|
value,
|
|
21
21
|
startingValue = null,
|
|
22
|
+
valueIsAlwaysArray = false,
|
|
23
|
+
valueAsIdAndText = false,
|
|
24
|
+
valueAsStringifiedJson = false,
|
|
22
25
|
|
|
23
26
|
// withData
|
|
24
27
|
Repository,
|
|
@@ -26,6 +29,39 @@ export default function withValue(WrappedComponent) {
|
|
|
26
29
|
} = props,
|
|
27
30
|
[localValue, setLocalValue] = useState(startingValue || value),
|
|
28
31
|
setValue = (newValue) => {
|
|
32
|
+
if (valueIsAlwaysArray && !_.isArray(newValue)) {
|
|
33
|
+
newValue = _.isNil(newValue) ? [] : [newValue];
|
|
34
|
+
}
|
|
35
|
+
if (_.isArray(newValue)) {
|
|
36
|
+
// TODO: sort by the sortProperty, whatever that is, instead of just value
|
|
37
|
+
newValue.sort(natsort()); // Only sort if we're using id/text arrangement. Otherwise, keep sort order as specified in Repository.
|
|
38
|
+
}
|
|
39
|
+
if (valueAsIdAndText) {
|
|
40
|
+
if (_.isArray(newValue)) {
|
|
41
|
+
newValue = _.map(newValue, (id) => {
|
|
42
|
+
if (_.isNil(id)) {
|
|
43
|
+
return id;
|
|
44
|
+
}
|
|
45
|
+
const record = Repository.getById(id);
|
|
46
|
+
return {
|
|
47
|
+
id: record.getId(),
|
|
48
|
+
text: record.getDisplayValue(),
|
|
49
|
+
};
|
|
50
|
+
})
|
|
51
|
+
} else {
|
|
52
|
+
if (!_.isNil(id)) {
|
|
53
|
+
const record = Repository.getById(newValue);
|
|
54
|
+
newValue = {
|
|
55
|
+
id: record.getId(),
|
|
56
|
+
text: record.getDisplayValue(),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (valueAsStringifiedJson) {
|
|
62
|
+
newValue = JSON.stringify(newValue);
|
|
63
|
+
}
|
|
64
|
+
|
|
29
65
|
if (newValue === localValue) {
|
|
30
66
|
return;
|
|
31
67
|
}
|
|
@@ -33,10 +69,6 @@ export default function withValue(WrappedComponent) {
|
|
|
33
69
|
setLocalValue(newValue);
|
|
34
70
|
|
|
35
71
|
if (onChangeValue) {
|
|
36
|
-
if (_.isArray(newValue)) {
|
|
37
|
-
// convert from inner value to outer value
|
|
38
|
-
newValue = JSON.stringify(newValue);
|
|
39
|
-
}
|
|
40
72
|
onChangeValue(newValue);
|
|
41
73
|
}
|
|
42
74
|
},
|
|
@@ -71,10 +103,26 @@ export default function withValue(WrappedComponent) {
|
|
|
71
103
|
}, [value]);
|
|
72
104
|
|
|
73
105
|
|
|
106
|
+
// Convert localValue to normal JS primitives for field components
|
|
74
107
|
let convertedValue = localValue;
|
|
75
|
-
if (_.isString(
|
|
76
|
-
|
|
77
|
-
|
|
108
|
+
if (_.isString(convertedValue) && valueAsStringifiedJson && !_.isNil(convertedValue)) {
|
|
109
|
+
convertedValue = JSON.parse(convertedValue);
|
|
110
|
+
}
|
|
111
|
+
if (valueIsAlwaysArray) {
|
|
112
|
+
if (_.isEmpty(convertedValue) || _.isNil(convertedValue)) {
|
|
113
|
+
convertedValue = null;
|
|
114
|
+
} else if (convertedValue.length === 1) {
|
|
115
|
+
convertedValue = convertedValue[0];
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (valueAsIdAndText && !_.isNil(convertedValue)) {
|
|
119
|
+
if (_.isArray(convertedValue)) {
|
|
120
|
+
convertedValue = _.map(convertedValue, (value) => {
|
|
121
|
+
return value?.id;
|
|
122
|
+
});
|
|
123
|
+
} else {
|
|
124
|
+
convertedValue = convertedValue?.id;
|
|
125
|
+
}
|
|
78
126
|
}
|
|
79
127
|
|
|
80
128
|
return <WrappedComponent
|
|
@@ -9,10 +9,12 @@ import {
|
|
|
9
9
|
HORIZONTAL,
|
|
10
10
|
VERTICAL,
|
|
11
11
|
} from '../../Constants/Directions.js';
|
|
12
|
+
import Inflector from 'inflector-js';
|
|
12
13
|
import Header from './Header.js';
|
|
13
14
|
import Mask from './Mask.js';
|
|
14
15
|
import withCollapsible from '../Hoc/withCollapsible.js';
|
|
15
16
|
import emptyFn from '../../Functions/emptyFn.js';
|
|
17
|
+
import UiGlobals from '../../UiGlobals.js';
|
|
16
18
|
import _ from 'lodash';
|
|
17
19
|
|
|
18
20
|
// Note on collapseDirections:
|
|
@@ -33,7 +35,7 @@ function Panel(props) {
|
|
|
33
35
|
onLayout = null,
|
|
34
36
|
|
|
35
37
|
// Header
|
|
36
|
-
title = props.model,
|
|
38
|
+
title = UiGlobals.customInflect(Inflector.camel2words(Inflector.underscore(props.model))),
|
|
37
39
|
showHeader = true,
|
|
38
40
|
header = null,
|
|
39
41
|
isClosable = false,
|
|
@@ -60,7 +60,7 @@ export default function ManagerScreen(props) {
|
|
|
60
60
|
whichComponent = sideModeComponent;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
return <Column flex={1} w="100%">
|
|
63
|
+
return <Column maxHeight="100vh" overflow="hidden" flex={1} w="100%">
|
|
64
64
|
<Row
|
|
65
65
|
h="80px"
|
|
66
66
|
py={2}
|
|
@@ -89,6 +89,8 @@ export default function ManagerScreen(props) {
|
|
|
89
89
|
tooltip="Side Editor"
|
|
90
90
|
/>
|
|
91
91
|
</Row>
|
|
92
|
+
|
|
92
93
|
{whichComponent}
|
|
94
|
+
|
|
93
95
|
</Column>;
|
|
94
96
|
}
|
package/src/Constants/Styles.js
CHANGED
|
@@ -16,6 +16,7 @@ const defaults = {
|
|
|
16
16
|
FORM_COMBO_INPUT_FONTSIZE: DEFAULT_FONTSIZE,
|
|
17
17
|
FORM_COMBO_INPUT_BG: WHITE,
|
|
18
18
|
FORM_COMBO_INPUT_FOCUS_BG: FOCUS,
|
|
19
|
+
FORM_COMBO_MENU_HEIGHT: 250,
|
|
19
20
|
FORM_COMBO_TRIGGER_BG: WHITE,
|
|
20
21
|
FORM_COMBO_TRIGGER_HOVER_BG: 'trueGray.300',
|
|
21
22
|
FORM_DATE_ICON_BG: 'primary.200',
|
|
@@ -49,7 +49,7 @@ function AttachmentsElement(props) {
|
|
|
49
49
|
|
|
50
50
|
} = props,
|
|
51
51
|
styles = UiGlobals.styles,
|
|
52
|
-
model = selectorSelected?.repository
|
|
52
|
+
model = selectorSelected?.repository?.name,
|
|
53
53
|
modelid = selectorSelected?.id,
|
|
54
54
|
[isReady, setIsReady] = useState(false),
|
|
55
55
|
[isUploading, setIsUploading] = useState(false),
|
package/src/UiGlobals.js
CHANGED