@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 +1 -1
- package/src/Components/Form/Form.js +3 -1
- package/src/Components/Hoc/Secondary/withSecondaryData.js +117 -0
- package/src/Components/Hoc/Secondary/withSecondaryEditor.js +455 -0
- package/src/Components/Hoc/Secondary/withSecondarySelection.js +377 -0
- package/src/Components/Hoc/Secondary/withSecondarySideEditor.js +50 -0
- package/src/Components/Hoc/Secondary/withSecondaryValue.js +154 -0
- package/src/Components/Hoc/Secondary/withSecondaryWindowedEditor.js +71 -0
- package/src/Components/Hoc/withEditor.js +1 -1
- package/src/Components/Icons/EllipsisHorizontal.js +20 -0
- package/src/Components/Icons/Js.js +20 -0
- package/src/Components/Icons/Table.js +20 -0
- package/src/Components/index.js +6 -0
package/package.json
CHANGED
|
@@ -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
|
-
|
|
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(
|
|
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
|
package/src/Components/index.js
CHANGED
|
@@ -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,
|