@onehat/ui 0.2.41 → 0.2.43
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/Field/TextArea.js +5 -1
- package/src/Components/Form/Form.js +30 -12
- package/src/Components/Grid/Grid.js +2 -2
- package/src/Components/Hoc/withEditor.js +62 -30
- package/src/Components/Hoc/withPresetButtons.js +15 -7
- package/src/Components/Screens/DataMgt.js +5 -5
package/package.json
CHANGED
|
@@ -5,10 +5,13 @@ import {
|
|
|
5
5
|
import UiGlobals from '../../../UiGlobals.js';
|
|
6
6
|
import withTooltip from '../../Hoc/withTooltip.js';
|
|
7
7
|
import withValue from '../../Hoc/withValue.js';
|
|
8
|
+
import _ from 'lodash';
|
|
8
9
|
|
|
9
10
|
const
|
|
10
11
|
TextAreaElement = (props) => {
|
|
11
|
-
const
|
|
12
|
+
const
|
|
13
|
+
styles = UiGlobals.styles,
|
|
14
|
+
value = _.isNil(props.value) ? '' : props.value; // null value may not actually reset this TextArea, so set it explicitly to empty string
|
|
12
15
|
return <TextArea
|
|
13
16
|
ref={props.outerRef}
|
|
14
17
|
onChangeText={props.setValue}
|
|
@@ -17,6 +20,7 @@ const
|
|
|
17
20
|
fontSize={styles.FORM_TEXTAREA_FONTSIZE}
|
|
18
21
|
h={styles.FORM_TEXTAREA_HEIGHT}
|
|
19
22
|
{...props}
|
|
23
|
+
value={value}
|
|
20
24
|
/>;
|
|
21
25
|
},
|
|
22
26
|
TextAreaField = withValue(TextAreaElement);
|
|
@@ -14,6 +14,9 @@ import {
|
|
|
14
14
|
EDITOR_TYPE__SIDE,
|
|
15
15
|
EDITOR_TYPE__SMART,
|
|
16
16
|
EDITOR_TYPE__PLAIN,
|
|
17
|
+
EDITOR_MODE__VIEW,
|
|
18
|
+
EDITOR_MODE__ADD,
|
|
19
|
+
EDITOR_MODE__EDIT,
|
|
17
20
|
} from '../../Constants/Editor.js';
|
|
18
21
|
import { useForm, Controller } from 'react-hook-form'; // https://react-hook-form.com/api/
|
|
19
22
|
import * as yup from 'yup'; // https://github.com/jquense/yup#string
|
|
@@ -74,12 +77,13 @@ function Form(props) {
|
|
|
74
77
|
|
|
75
78
|
// withEditor
|
|
76
79
|
isViewOnly = false,
|
|
80
|
+
editorMode,
|
|
77
81
|
onCancel,
|
|
78
82
|
onEditorSave,
|
|
79
83
|
onSave = onEditorSave,
|
|
80
84
|
onClose,
|
|
81
85
|
|
|
82
|
-
//
|
|
86
|
+
// DataMgt
|
|
83
87
|
selectorId,
|
|
84
88
|
selectorSelected,
|
|
85
89
|
|
|
@@ -456,18 +460,32 @@ function Form(props) {
|
|
|
456
460
|
<Row flex={1}>{formComponents}</Row>
|
|
457
461
|
</ScrollView>;
|
|
458
462
|
}
|
|
463
|
+
|
|
464
|
+
let editorModeF;
|
|
465
|
+
switch(editorMode) {
|
|
466
|
+
case EDITOR_MODE__VIEW:
|
|
467
|
+
editorModeF = 'View';
|
|
468
|
+
break;
|
|
469
|
+
case EDITOR_MODE__ADD:
|
|
470
|
+
editorModeF = 'Add';
|
|
471
|
+
break;
|
|
472
|
+
case EDITOR_MODE__EDIT:
|
|
473
|
+
editorModeF = isMultiple ? 'Edit Multiple' : 'Edit';
|
|
474
|
+
break;
|
|
475
|
+
}
|
|
459
476
|
|
|
460
477
|
return <Column {...sizeProps} onLayout={onLayout}>
|
|
461
478
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
479
|
+
<Row p={2} alignItems="center">
|
|
480
|
+
{isSingle && editorMode === EDITOR_MODE__EDIT && onBack &&
|
|
481
|
+
<Button
|
|
482
|
+
key="backBtn"
|
|
483
|
+
onPress={onBack}
|
|
484
|
+
leftIcon={<Icon as={AngleLeft} color="#fff" size="sm" />}
|
|
485
|
+
color="#fff"
|
|
486
|
+
>Back</Button>}
|
|
487
|
+
<Text ml={2} fontSize={18}>{editorModeF} Mode</Text>
|
|
488
|
+
</Row>
|
|
471
489
|
|
|
472
490
|
{editor}
|
|
473
491
|
|
|
@@ -475,7 +493,7 @@ function Form(props) {
|
|
|
475
493
|
<Button.Group space={2} {...buttonGroupProps}>
|
|
476
494
|
{!isViewOnly && <IconButton
|
|
477
495
|
key="resetBtn"
|
|
478
|
-
onPress={reset}
|
|
496
|
+
onPress={() => reset()}
|
|
479
497
|
icon={<Rotate color="#fff" />}
|
|
480
498
|
/>}
|
|
481
499
|
{!isViewOnly && onCancel && <Button
|
|
@@ -495,7 +513,7 @@ function Form(props) {
|
|
|
495
513
|
onPress={(e) => handleSubmit(onSave, onSubmitError)(e)}
|
|
496
514
|
isDisabled={!_.isEmpty(formState.errors) || (!isSingle && !record?.isPhantom && !formState.isDirty)}
|
|
497
515
|
color="#fff"
|
|
498
|
-
>Save</Button>}
|
|
516
|
+
>{editorMode === EDITOR_MODE__ADD ? 'Add' : 'Save'}</Button>}
|
|
499
517
|
{isViewOnly && onClose && <Button
|
|
500
518
|
key="closeBtn"
|
|
501
519
|
onPress={onClose}
|
|
@@ -82,6 +82,7 @@ export function Grid(props) {
|
|
|
82
82
|
hideNavColumn = true,
|
|
83
83
|
noneFoundText,
|
|
84
84
|
disableLoadingIndicator = false,
|
|
85
|
+
disableSelectorSelected = false,
|
|
85
86
|
showRowExpander = false,
|
|
86
87
|
rowExpanderTpl = '',
|
|
87
88
|
showHeaders = true,
|
|
@@ -129,10 +130,9 @@ export function Grid(props) {
|
|
|
129
130
|
isInSelection,
|
|
130
131
|
noSelectorMeansNoResults = false,
|
|
131
132
|
|
|
132
|
-
//
|
|
133
|
+
// DataMgt
|
|
133
134
|
selectorId,
|
|
134
135
|
selectorSelected,
|
|
135
|
-
disableSelectorSelected = false,
|
|
136
136
|
|
|
137
137
|
// withInlineEditor
|
|
138
138
|
inlineEditorRef,
|
|
@@ -8,6 +8,8 @@ import _ from 'lodash';
|
|
|
8
8
|
|
|
9
9
|
export default function withEditor(WrappedComponent) {
|
|
10
10
|
return (props) => {
|
|
11
|
+
|
|
12
|
+
let [editorMode, setEditorMode] = useState(EDITOR_MODE__VIEW); // Can change below, so use 'let'
|
|
11
13
|
const {
|
|
12
14
|
useEditor = false,
|
|
13
15
|
userCanEdit = true,
|
|
@@ -25,6 +27,10 @@ export default function withEditor(WrappedComponent) {
|
|
|
25
27
|
},
|
|
26
28
|
record,
|
|
27
29
|
|
|
30
|
+
// DataMgt
|
|
31
|
+
selectorId,
|
|
32
|
+
selectorSelected,
|
|
33
|
+
|
|
28
34
|
// withData
|
|
29
35
|
Repository,
|
|
30
36
|
|
|
@@ -38,21 +44,27 @@ export default function withEditor(WrappedComponent) {
|
|
|
38
44
|
[currentRecord, setCurrentRecord] = useState(null),
|
|
39
45
|
[isEditorShown, setIsEditorShown] = useState(false),
|
|
40
46
|
[isEditorViewOnly, setIsEditorViewOnly] = useState(false),
|
|
41
|
-
[
|
|
47
|
+
[lastSelection, setLastSelection] = useState(),
|
|
42
48
|
addRecord = async () => {
|
|
43
|
-
if (!userCanEdit) {
|
|
49
|
+
if (!userCanEdit || disableAdd) {
|
|
44
50
|
return;
|
|
45
51
|
}
|
|
46
52
|
const
|
|
47
53
|
defaultValues = Repository.getSchema().model.defaultValues,
|
|
48
|
-
|
|
54
|
+
addValues = _.clone(defaultValues);
|
|
55
|
+
|
|
56
|
+
if (selectorId && !_.isEmpty(selectorSelected)) {
|
|
57
|
+
addValues[selectorId] = selectorSelected.id;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const entity = await Repository.add(addValues, false, true, true);
|
|
49
61
|
setSelection([entity]);
|
|
50
62
|
setIsEditorViewOnly(false);
|
|
51
63
|
setEditorMode(EDITOR_MODE__ADD);
|
|
52
64
|
setIsEditorShown(true);
|
|
53
65
|
},
|
|
54
66
|
editRecord = () => {
|
|
55
|
-
if (!userCanEdit) {
|
|
67
|
+
if (!userCanEdit || disableEdit) {
|
|
56
68
|
return;
|
|
57
69
|
}
|
|
58
70
|
setIsEditorViewOnly(false);
|
|
@@ -60,7 +72,7 @@ export default function withEditor(WrappedComponent) {
|
|
|
60
72
|
setIsEditorShown(true);
|
|
61
73
|
},
|
|
62
74
|
deleteRecord = (e) => {
|
|
63
|
-
if (!userCanEdit) {
|
|
75
|
+
if (!userCanEdit || disableDelete) {
|
|
64
76
|
return;
|
|
65
77
|
}
|
|
66
78
|
const
|
|
@@ -74,11 +86,9 @@ export default function withEditor(WrappedComponent) {
|
|
|
74
86
|
confirm('Are you sure you want to delete the ' + identifier, onDelete);
|
|
75
87
|
}
|
|
76
88
|
},
|
|
77
|
-
onDelete = () => {
|
|
89
|
+
onDelete = async () => {
|
|
78
90
|
Repository.delete(selection);
|
|
79
|
-
|
|
80
|
-
Repository.save();
|
|
81
|
-
}
|
|
91
|
+
await Repository.save();
|
|
82
92
|
},
|
|
83
93
|
viewRecord = () => {
|
|
84
94
|
if (!userCanView) {
|
|
@@ -92,7 +102,7 @@ export default function withEditor(WrappedComponent) {
|
|
|
92
102
|
setIsEditorShown(true);
|
|
93
103
|
},
|
|
94
104
|
duplicateRecord = async () => {
|
|
95
|
-
if (!userCanEdit) {
|
|
105
|
+
if (!userCanEdit || disableDuplicate) {
|
|
96
106
|
return;
|
|
97
107
|
}
|
|
98
108
|
if (selection.length !== 1) {
|
|
@@ -104,9 +114,10 @@ export default function withEditor(WrappedComponent) {
|
|
|
104
114
|
rawValues = _.omit(entity.rawValues, idProperty),
|
|
105
115
|
duplicate = await Repository.add(rawValues, false, true);
|
|
106
116
|
setSelection([duplicate]);
|
|
117
|
+
setEditorMode(EDITOR_MODE__EDIT);
|
|
107
118
|
setIsEditorShown(true);
|
|
108
119
|
},
|
|
109
|
-
onEditorSave = (data, e) => {
|
|
120
|
+
onEditorSave = async (data, e) => {
|
|
110
121
|
const
|
|
111
122
|
what = record || selection,
|
|
112
123
|
isSingle = what.length === 1;
|
|
@@ -117,42 +128,63 @@ export default function withEditor(WrappedComponent) {
|
|
|
117
128
|
} else if (selection.length > 1) {
|
|
118
129
|
// Edit multiple entities
|
|
119
130
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
131
|
+
// Loop through all entities and change fields that are not null
|
|
132
|
+
const propertyNames = Object.getOwnPropertyNames(data);
|
|
133
|
+
_.each(propertyNames, (propertyName) => {
|
|
134
|
+
if (!_.isNil(data[propertyName])) {
|
|
135
|
+
_.each(what, (rec) => {
|
|
136
|
+
rec[propertyName] = data[propertyName]
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
});
|
|
128
140
|
}
|
|
141
|
+
await Repository.save();
|
|
129
142
|
setIsEditorShown(false);
|
|
130
143
|
},
|
|
131
|
-
onEditorCancel = () => {
|
|
144
|
+
onEditorCancel = async () => {
|
|
132
145
|
const
|
|
133
146
|
isSingle = selection.length === 1,
|
|
134
147
|
isPhantom = selection[0] && selection[0].isPhantom;
|
|
135
148
|
if (isSingle && isPhantom) {
|
|
136
|
-
onDelete();
|
|
149
|
+
await onDelete();
|
|
137
150
|
}
|
|
151
|
+
setEditorMode(EDITOR_MODE__VIEW);
|
|
138
152
|
setIsEditorShown(false);
|
|
139
153
|
},
|
|
140
154
|
onEditorClose = () => {
|
|
141
155
|
setIsEditorShown(false);
|
|
156
|
+
},
|
|
157
|
+
calculateEditorMode = () => {
|
|
158
|
+
let mode = EDITOR_MODE__VIEW;
|
|
159
|
+
if (userCanEdit) {
|
|
160
|
+
if (selection.length > 1) {
|
|
161
|
+
if (!disableEdit) {
|
|
162
|
+
// For multiple entities selected, change it to edit multiple mode
|
|
163
|
+
mode = EDITOR_MODE__EDIT;
|
|
164
|
+
}
|
|
165
|
+
} else if (selection.length === 1 && selection.isPhantom) {
|
|
166
|
+
if (!disableAdd) {
|
|
167
|
+
// When a phantom entity is selected, change it to add mode.
|
|
168
|
+
mode = EDITOR_MODE__ADD;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return mode;
|
|
142
173
|
};
|
|
143
174
|
|
|
144
175
|
useEffect(() => {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
} else {
|
|
150
|
-
if (editorMode !== EDITOR_MODE__VIEW) {
|
|
151
|
-
setEditorMode(EDITOR_MODE__VIEW);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
176
|
+
// When selection changes, set the mode appropriately
|
|
177
|
+
const mode = calculateEditorMode();
|
|
178
|
+
setEditorMode(mode);
|
|
179
|
+
setLastSelection(selection);
|
|
154
180
|
}, [selection]);
|
|
155
181
|
|
|
182
|
+
if (lastSelection !== selection) {
|
|
183
|
+
// NOTE: If I don't calculate this on the fly for selection changes,
|
|
184
|
+
// we see a flash of the previous state, since useEffect hasn't yet run.
|
|
185
|
+
editorMode = calculateEditorMode();
|
|
186
|
+
}
|
|
187
|
+
|
|
156
188
|
return <WrappedComponent
|
|
157
189
|
{...props}
|
|
158
190
|
currentRecord={currentRecord}
|
|
@@ -33,12 +33,6 @@ export default function withPresetButtons(WrappedComponent) {
|
|
|
33
33
|
} = props,
|
|
34
34
|
{
|
|
35
35
|
// for local use
|
|
36
|
-
selection,
|
|
37
|
-
onAdd,
|
|
38
|
-
onEdit,
|
|
39
|
-
onDelete,
|
|
40
|
-
onView,
|
|
41
|
-
onDuplicate,
|
|
42
36
|
useEditor = true,
|
|
43
37
|
disableAdd = false,
|
|
44
38
|
disableEdit = false,
|
|
@@ -51,6 +45,19 @@ export default function withPresetButtons(WrappedComponent) {
|
|
|
51
45
|
// withEditor
|
|
52
46
|
userCanEdit = true,
|
|
53
47
|
userCanView = true,
|
|
48
|
+
onAdd,
|
|
49
|
+
onEdit,
|
|
50
|
+
onDelete,
|
|
51
|
+
onView,
|
|
52
|
+
onDuplicate,
|
|
53
|
+
|
|
54
|
+
// withSelection
|
|
55
|
+
selection,
|
|
56
|
+
setSelection,
|
|
57
|
+
|
|
58
|
+
// DataMgt
|
|
59
|
+
selectorId,
|
|
60
|
+
selectorSelected,
|
|
54
61
|
} = props,
|
|
55
62
|
[isReady, setIsReady] = useState(false),
|
|
56
63
|
[localContextMenuItems, setLocalContextMenuItems] = useState([]),
|
|
@@ -210,7 +217,7 @@ export default function withPresetButtons(WrappedComponent) {
|
|
|
210
217
|
if (!isReady) {
|
|
211
218
|
setIsReady(true);
|
|
212
219
|
}
|
|
213
|
-
}, [selection, localColumnsConfig]);
|
|
220
|
+
}, [selection, selectorSelected, localColumnsConfig]);
|
|
214
221
|
|
|
215
222
|
if (!isReady) {
|
|
216
223
|
return null;
|
|
@@ -229,6 +236,7 @@ export default function withPresetButtons(WrappedComponent) {
|
|
|
229
236
|
if (additionalToolbarButtons) {
|
|
230
237
|
additionalToolbarButtonsToPass.concat(additionalToolbarButtons);
|
|
231
238
|
}
|
|
239
|
+
|
|
232
240
|
return <WrappedComponent
|
|
233
241
|
{...propsToPass}
|
|
234
242
|
contextMenuItems={contextMenuItemsToPass}
|
|
@@ -44,8 +44,8 @@ export default function DataMgt(props) {
|
|
|
44
44
|
[isWestCollapsed, setIsWestCollapsed] = useState(westStartsCollapsed),
|
|
45
45
|
[isEastCollapsed, setIsEastCollapsed] = useState(eastStartsCollapsed),
|
|
46
46
|
[isFullscreen, setIsFullscreen] = useState(false),
|
|
47
|
-
[westSelected, setWestSelectedRaw] = useState(
|
|
48
|
-
[centerSelected, setCenterSelected] = useState(
|
|
47
|
+
[westSelected, setWestSelectedRaw] = useState(),
|
|
48
|
+
[centerSelected, setCenterSelected] = useState(),
|
|
49
49
|
setWestSelected = (selected) => {
|
|
50
50
|
setWestSelectedRaw(selected);
|
|
51
51
|
setCenterSelected(); // clear selection in center
|
|
@@ -156,7 +156,7 @@ export default function DataMgt(props) {
|
|
|
156
156
|
autoLoad={!showSelector}
|
|
157
157
|
uniqueRepository={true}
|
|
158
158
|
selectorId={showSelector ? westSelector_id : null}
|
|
159
|
-
selectorSelected={westSelected}
|
|
159
|
+
selectorSelected={westSelected?.[0]}
|
|
160
160
|
noSelectorMeansNoResults={centerNoSelectorMeansNoResults}
|
|
161
161
|
onChangeSelection={setCenterSelected}
|
|
162
162
|
onEvent={onEvent}
|
|
@@ -167,7 +167,7 @@ export default function DataMgt(props) {
|
|
|
167
167
|
isFullscreen,
|
|
168
168
|
showSelector,
|
|
169
169
|
westSelected,
|
|
170
|
-
westSelected?.hash,
|
|
170
|
+
westSelected?.[0].hash,
|
|
171
171
|
centerNoSelectorMeansNoResults,
|
|
172
172
|
// {...centerProps}
|
|
173
173
|
])
|
|
@@ -191,7 +191,7 @@ export default function DataMgt(props) {
|
|
|
191
191
|
controlledByCenter = typeof associatedPanel.props.controlledByCenter === 'undefined' ? true : associatedPanel.props.controlledByCenter,
|
|
192
192
|
thisAssociatedPanelProps = {
|
|
193
193
|
selectorId: controlledByCenter ? centerSelector_id : westSelector_id,
|
|
194
|
-
selectorSelected: controlledByCenter ? centerSelected : westSelected,
|
|
194
|
+
selectorSelected: controlledByCenter ? centerSelected?.[0] : westSelected?.[0],
|
|
195
195
|
...associatedPanel.props,
|
|
196
196
|
};
|
|
197
197
|
return React.cloneElement(associatedPanel, { key: ix, reference: 'associatedPanel' + ix, ...allAssociatedPanelProps, ...thisAssociatedPanelProps, });
|