@onehat/ui 0.4.82 → 0.4.84
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 +7 -6
- package/src/Components/Container/Container.js +336 -147
- package/src/Components/Form/Field/Combo/Combo.js +84 -15
- package/src/Components/Form/Field/Hidden.js +13 -0
- package/src/Components/Form/Field/Json.js +2 -1
- package/src/Components/Form/Field/Tag/Tag.js +93 -78
- package/src/Components/Form/Field/Tag/ValueBox.js +2 -2
- package/src/Components/Form/Form.js +3 -2
- package/src/Components/Grid/Grid.js +79 -42
- package/src/Components/Grid/GridRow.js +10 -0
- package/src/Components/Hoc/Secondary/withSecondaryEditor.js +137 -43
- package/src/Components/Hoc/Secondary/withSecondarySideEditor.js +6 -4
- package/src/Components/Hoc/withContextMenu.js +13 -4
- package/src/Components/Hoc/withDraggable.js +7 -3
- package/src/Components/Hoc/withSideEditor.js +7 -4
- package/src/Components/Layout/AsyncOperation.js +54 -25
- package/src/Components/Panel/Mask.js +1 -14
- package/src/Components/Screens/Manager.js +4 -5
- package/src/Components/Toolbar/Pagination.js +108 -106
- package/src/Components/Tree/Tree.js +24 -0
- package/src/Components/Tree/TreeNode.js +2 -1
- package/src/Components/Viewer/Viewer.js +2 -5
- package/src/Components/index.js +2 -0
- package/src/Constants/Progress.js +6 -1
- package/src/PlatformImports/Web/Attachments.js +3 -3
|
@@ -24,6 +24,7 @@ import testProps from '../../../../Functions/testProps.js';
|
|
|
24
24
|
import UiGlobals from '../../../../UiGlobals.js';
|
|
25
25
|
import Input from '../Input.js';
|
|
26
26
|
import { Grid, WindowedGridEditor } from '../../../Grid/Grid.js';
|
|
27
|
+
import useForceUpdate from '../../../../Hooks/useForceUpdate.js';
|
|
27
28
|
import withAlert from '../../../Hoc/withAlert.js';
|
|
28
29
|
import withComponent from '../../../Hoc/withComponent.js';
|
|
29
30
|
import withData from '../../../Hoc/withData.js';
|
|
@@ -121,13 +122,16 @@ export const ComboComponent = forwardRef((props, ref) => {
|
|
|
121
122
|
setValue,
|
|
122
123
|
} = props,
|
|
123
124
|
styles = UiGlobals.styles,
|
|
125
|
+
forceUpdate = useForceUpdate(),
|
|
124
126
|
inputRef = useRef(),
|
|
125
127
|
inputCloneRef = useRef(),
|
|
126
128
|
triggerRef = useRef(),
|
|
127
129
|
menuRef = useRef(),
|
|
128
130
|
displayValueRef = useRef(),
|
|
129
131
|
typingTimeout = useRef(),
|
|
130
|
-
|
|
132
|
+
isMenuShown = useRef(false),
|
|
133
|
+
isGridLayoutRunWithRender = useRef(false),
|
|
134
|
+
[isMenuAbove, setIsMenuAbove] = useState(false),
|
|
131
135
|
[isViewerShown, setIsViewerShown] = useState(false),
|
|
132
136
|
[viewerSelection, setViewerSelection] = useState([]),
|
|
133
137
|
[isRendered, setIsRendered] = useState(false),
|
|
@@ -140,15 +144,39 @@ export const ComboComponent = forwardRef((props, ref) => {
|
|
|
140
144
|
[newEntityDisplayValue, setNewEntityDisplayValue] = useState(null),
|
|
141
145
|
[filteredData, setFilteredData] = useState(data),
|
|
142
146
|
[inputHeight, setInputHeight] = useState(0),
|
|
147
|
+
[menuRenderedHeight, setMenuRenderedHeight] = useState(0),
|
|
143
148
|
[width, setWidth] = useState(0),
|
|
144
149
|
[top, setTop] = useState(0),
|
|
145
150
|
[left, setLeft] = useState(0),
|
|
151
|
+
getIsMenuShown = () => {
|
|
152
|
+
return isMenuShown.current;
|
|
153
|
+
},
|
|
154
|
+
setIsMenuShown = (bool) => {
|
|
155
|
+
isMenuShown.current = bool;
|
|
156
|
+
|
|
157
|
+
if (!bool) {
|
|
158
|
+
// The menu's onLayout runs every time there's a change in its size or position.
|
|
159
|
+
// We're only interested in the *first* time it runs with a rendered height.
|
|
160
|
+
// So if hiding the menu, reset isGridLayoutRunWithRender here and we'll set it to true
|
|
161
|
+
// the first time onLayout runs with a height.
|
|
162
|
+
setIsGridLayoutRunWithRender(false);
|
|
163
|
+
setIsMenuAbove(false); // reset this so the next time the menu opens, it starts below the input
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
forceUpdate();
|
|
167
|
+
},
|
|
168
|
+
getIsGridLayoutRunWithRender = () => {
|
|
169
|
+
return isGridLayoutRunWithRender.current;
|
|
170
|
+
},
|
|
171
|
+
setIsGridLayoutRunWithRender = (bool) => {
|
|
172
|
+
isGridLayoutRunWithRender.current = bool;
|
|
173
|
+
},
|
|
146
174
|
onLayout = (e) => {
|
|
147
175
|
setIsRendered(true);
|
|
148
176
|
setContainerWidth(e.nativeEvent.layout.width);
|
|
149
177
|
},
|
|
150
178
|
showMenu = async () => {
|
|
151
|
-
if (
|
|
179
|
+
if (getIsMenuShown()) {
|
|
152
180
|
return;
|
|
153
181
|
}
|
|
154
182
|
if (CURRENT_MODE === UI_MODE_WEB && inputRef.current?.getBoundingClientRect) {
|
|
@@ -181,13 +209,13 @@ export const ComboComponent = forwardRef((props, ref) => {
|
|
|
181
209
|
setIsMenuShown(true);
|
|
182
210
|
},
|
|
183
211
|
hideMenu = () => {
|
|
184
|
-
if (!
|
|
212
|
+
if (!getIsMenuShown()) {
|
|
185
213
|
return;
|
|
186
214
|
}
|
|
187
215
|
setIsMenuShown(false);
|
|
188
216
|
},
|
|
189
217
|
toggleMenu = () => {
|
|
190
|
-
setIsMenuShown(!
|
|
218
|
+
setIsMenuShown(!getIsMenuShown());
|
|
191
219
|
},
|
|
192
220
|
temporarilySetIsNavigatingViaKeyboard = () => {
|
|
193
221
|
setIsNavigatingViaKeyboard(true);
|
|
@@ -359,14 +387,14 @@ export const ComboComponent = forwardRef((props, ref) => {
|
|
|
359
387
|
if (reloadOnTrigger && Repository) {
|
|
360
388
|
await Repository.reload();
|
|
361
389
|
}
|
|
362
|
-
if (
|
|
390
|
+
if (getIsMenuShown()) {
|
|
363
391
|
hideMenu();
|
|
364
392
|
} else {
|
|
365
393
|
showMenu();
|
|
366
394
|
}
|
|
367
395
|
},
|
|
368
396
|
onTriggerBlur = (e) => {
|
|
369
|
-
if (!
|
|
397
|
+
if (!getIsMenuShown()) {
|
|
370
398
|
return;
|
|
371
399
|
}
|
|
372
400
|
|
|
@@ -409,6 +437,33 @@ export const ComboComponent = forwardRef((props, ref) => {
|
|
|
409
437
|
onCheckButtonPress = () => {
|
|
410
438
|
hideMenu();
|
|
411
439
|
},
|
|
440
|
+
onGridLayout = (e) => {
|
|
441
|
+
// This method is to determine if we need to flip the grid above the input
|
|
442
|
+
// because the menu is partially offscreen
|
|
443
|
+
|
|
444
|
+
if (CURRENT_MODE !== UI_MODE_WEB || !e.nativeEvent.layout.height) {
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// we reach this point only if the grid has rendered with a height.
|
|
449
|
+
|
|
450
|
+
if (!getIsGridLayoutRunWithRender()) {
|
|
451
|
+
// we reach this point only on the *first* time onGridLayout runs with a height.
|
|
452
|
+
// determine if the menu is partially offscreen
|
|
453
|
+
const
|
|
454
|
+
menuRect = menuRef.current.getBoundingClientRect(),
|
|
455
|
+
inputRect = inputRef.current.getBoundingClientRect(),
|
|
456
|
+
menuOverflows = menuRect.bottom > window.innerHeight;
|
|
457
|
+
if (menuOverflows) {
|
|
458
|
+
// flip it
|
|
459
|
+
setIsMenuAbove(true);
|
|
460
|
+
} else {
|
|
461
|
+
setIsMenuAbove(false);
|
|
462
|
+
}
|
|
463
|
+
setMenuRenderedHeight(e.nativeEvent.layout.height);
|
|
464
|
+
setIsGridLayoutRunWithRender(true);
|
|
465
|
+
}
|
|
466
|
+
},
|
|
412
467
|
isEventStillInComponent = (e) => {
|
|
413
468
|
const {
|
|
414
469
|
relatedTarget
|
|
@@ -518,7 +573,7 @@ export const ComboComponent = forwardRef((props, ref) => {
|
|
|
518
573
|
setFilteredData(found);
|
|
519
574
|
}
|
|
520
575
|
|
|
521
|
-
if (!
|
|
576
|
+
if (!getIsMenuShown()) {
|
|
522
577
|
showMenu();
|
|
523
578
|
}
|
|
524
579
|
setIsSearchMode(true);
|
|
@@ -734,7 +789,7 @@ export const ComboComponent = forwardRef((props, ref) => {
|
|
|
734
789
|
</Pressable>;
|
|
735
790
|
}
|
|
736
791
|
|
|
737
|
-
if (
|
|
792
|
+
if (getIsMenuShown()) {
|
|
738
793
|
const gridProps = _.pick(props, [
|
|
739
794
|
'Editor',
|
|
740
795
|
'model',
|
|
@@ -743,6 +798,9 @@ export const ComboComponent = forwardRef((props, ref) => {
|
|
|
743
798
|
'idIx',
|
|
744
799
|
'displayIx',
|
|
745
800
|
// 'value',
|
|
801
|
+
'disableAdd',
|
|
802
|
+
'disableEdit',
|
|
803
|
+
'disableDelete',
|
|
746
804
|
'disableView',
|
|
747
805
|
'disableCopy',
|
|
748
806
|
'disableDuplicate',
|
|
@@ -761,8 +819,9 @@ export const ComboComponent = forwardRef((props, ref) => {
|
|
|
761
819
|
if (!Repository) {
|
|
762
820
|
gridProps.data = filteredData;
|
|
763
821
|
}
|
|
764
|
-
const
|
|
765
|
-
|
|
822
|
+
const
|
|
823
|
+
WhichGrid = isEditor ? WindowedGridEditor : Grid,
|
|
824
|
+
gridStyle = {};
|
|
766
825
|
if (CURRENT_MODE === UI_MODE_WEB) {
|
|
767
826
|
gridStyle.height = menuHeight || styles.FORM_COMBO_MENU_HEIGHT;
|
|
768
827
|
}
|
|
@@ -786,6 +845,7 @@ export const ComboComponent = forwardRef((props, ref) => {
|
|
|
786
845
|
disablePresetButtons={!isEditor}
|
|
787
846
|
alternateRowBackgrounds={false}
|
|
788
847
|
showSelectHandle={false}
|
|
848
|
+
onLayout={onGridLayout}
|
|
789
849
|
onChangeSelection={(selection) => {
|
|
790
850
|
|
|
791
851
|
if (Repository && selection[0]?.isPhantom) {
|
|
@@ -938,7 +998,7 @@ export const ComboComponent = forwardRef((props, ref) => {
|
|
|
938
998
|
</Box>;
|
|
939
999
|
}
|
|
940
1000
|
dropdownMenu = <Popover
|
|
941
|
-
isOpen={
|
|
1001
|
+
isOpen={getIsMenuShown()}
|
|
942
1002
|
onClose={() => {
|
|
943
1003
|
hideMenu();
|
|
944
1004
|
}}
|
|
@@ -962,15 +1022,24 @@ export const ComboComponent = forwardRef((props, ref) => {
|
|
|
962
1022
|
'max-w-full',
|
|
963
1023
|
)}
|
|
964
1024
|
style={{
|
|
965
|
-
|
|
1025
|
+
// If flipped, position above input; otherwise, below
|
|
1026
|
+
top: isMenuAbove
|
|
1027
|
+
? (top - menuRenderedHeight) // above
|
|
1028
|
+
: top, // below
|
|
966
1029
|
left,
|
|
967
1030
|
width,
|
|
968
|
-
// height: (menuHeight || styles.FORM_COMBO_MENU_HEIGHT) + inputHeight,
|
|
969
1031
|
minWidth: 100,
|
|
970
1032
|
}}
|
|
971
1033
|
>
|
|
972
|
-
{
|
|
973
|
-
|
|
1034
|
+
{isMenuAbove ?
|
|
1035
|
+
<>
|
|
1036
|
+
{grid}
|
|
1037
|
+
{inputClone}
|
|
1038
|
+
</> :
|
|
1039
|
+
<>
|
|
1040
|
+
{inputClone}
|
|
1041
|
+
{grid}
|
|
1042
|
+
</>}
|
|
974
1043
|
</Box>
|
|
975
1044
|
</Popover>;
|
|
976
1045
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import withComponent from '../../Hoc/withComponent.js';
|
|
2
|
+
import withValue from '../../Hoc/withValue.js';
|
|
3
|
+
|
|
4
|
+
const HiddenElement = (props) => {
|
|
5
|
+
|
|
6
|
+
// This component does not render any visible UI elements,
|
|
7
|
+
// but it acts as a hidden input field for form submissions
|
|
8
|
+
// via the withValue HOC.
|
|
9
|
+
|
|
10
|
+
return null;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default withComponent(withValue(HiddenElement));
|
|
@@ -21,6 +21,7 @@ export function JsonElement(props) {
|
|
|
21
21
|
tooltip = null,
|
|
22
22
|
isDisabled = false,
|
|
23
23
|
isViewOnly = false,
|
|
24
|
+
isCollapsed = true,
|
|
24
25
|
tooltipPlacement = 'bottom',
|
|
25
26
|
testID,
|
|
26
27
|
|
|
@@ -59,7 +60,7 @@ export function JsonElement(props) {
|
|
|
59
60
|
editable={!isViewOnly}
|
|
60
61
|
src={src}
|
|
61
62
|
enableClipboard={false}
|
|
62
|
-
collapsed={
|
|
63
|
+
collapsed={isCollapsed}
|
|
63
64
|
onEdit={(obj) => {
|
|
64
65
|
setValue(JSON.stringify(obj.updated_src));
|
|
65
66
|
}}
|
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
import {
|
|
13
13
|
EDITOR_TYPE__PLAIN,
|
|
14
14
|
} from '../../../../Constants/Editor.js';
|
|
15
|
+
import Button from '../../../Buttons/Button.js';
|
|
16
|
+
import testProps from '../../../../Functions/testProps.js';
|
|
15
17
|
import Form from '../../Form.js';
|
|
16
18
|
import Viewer from '../../../Viewer/Viewer.js';
|
|
17
19
|
import withAlert from '../../../Hoc/withAlert.js';
|
|
@@ -36,14 +38,13 @@ function TagComponent(props) {
|
|
|
36
38
|
Editor,
|
|
37
39
|
_combo = {},
|
|
38
40
|
SourceRepository,
|
|
41
|
+
mustSaveBeforeEditingJoinData = false,
|
|
39
42
|
joinDataConfig,
|
|
40
|
-
|
|
43
|
+
getBaseParams, // See note in useEffect
|
|
44
|
+
outerValueId, // See note in useEffect
|
|
41
45
|
tooltip,
|
|
42
46
|
testID,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
// parent Form
|
|
46
|
-
onChangeValue,
|
|
47
|
+
isDirty = false,
|
|
47
48
|
|
|
48
49
|
// withAlert
|
|
49
50
|
alert,
|
|
@@ -77,11 +78,11 @@ function TagComponent(props) {
|
|
|
77
78
|
}
|
|
78
79
|
return null;
|
|
79
80
|
}),
|
|
80
|
-
[isInited, setIsInited] = useState(
|
|
81
|
+
[isInited, setIsInited] = useState(_.isUndefined(getBaseParams)), // default to true unless getBaseParams is defined
|
|
81
82
|
modelFieldStartsWith = hasJoinData ? Inflector.underscore(JoinRepository.getSchema().name) + '__' : '',
|
|
82
83
|
valueRef = useRef(value),
|
|
83
84
|
onView = async (item, e) => {
|
|
84
|
-
//
|
|
85
|
+
// show the joined record's viewer
|
|
85
86
|
const
|
|
86
87
|
id = item.id,
|
|
87
88
|
repository = TargetRepository;
|
|
@@ -142,7 +143,7 @@ function TagComponent(props) {
|
|
|
142
143
|
}
|
|
143
144
|
|
|
144
145
|
// The value we get from combo is a simple int
|
|
145
|
-
// Convert this to id
|
|
146
|
+
// Convert this to { id, text} from either Repository or data array.
|
|
146
147
|
const
|
|
147
148
|
data = props.data,
|
|
148
149
|
idIx = props.idIx,
|
|
@@ -171,32 +172,36 @@ function TagComponent(props) {
|
|
|
171
172
|
|
|
172
173
|
let joinData = {};
|
|
173
174
|
if (hasJoinData) {
|
|
174
|
-
// build up the default starting values,
|
|
175
|
+
// build up the default starting values for joinData,
|
|
175
176
|
// first with schema defaultValues...
|
|
176
177
|
const
|
|
177
178
|
allSchemaDefaults = JoinRepository.getSchema().getDefaultValues(),
|
|
178
179
|
modelSchemaDefaults = _.pickBy(allSchemaDefaults, (value, key) => {
|
|
179
180
|
return key.startsWith(modelFieldStartsWith);
|
|
180
181
|
}),
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
// then with default values in joinDataConfig, if they exist
|
|
188
|
-
_.each(joinDataConfig, (fieldConfig) => {
|
|
189
|
-
if (!_.isNil(fieldConfig.defaultValue)) {
|
|
190
|
-
joinData[fieldConfig.name] = fieldConfig.defaultValue;
|
|
191
|
-
}
|
|
182
|
+
fullFieldNames = propertyDef.joinData.map((fieldName) => { // add the 'model_name__' prefix so we can get schema default values
|
|
183
|
+
return modelFieldStartsWith + fieldName;
|
|
184
|
+
}),
|
|
185
|
+
schemaDefaultValues = _.pick(modelSchemaDefaults, fullFieldNames);
|
|
186
|
+
joinData = _.mapKeys(schemaDefaultValues, (value, key) => { // strip out the 'model_name__' prefix from field names
|
|
187
|
+
return key.startsWith(modelFieldStartsWith) ? key.slice(modelFieldStartsWith.length) : key;
|
|
192
188
|
});
|
|
193
|
-
|
|
189
|
+
|
|
190
|
+
// then override with default values in joinDataConfig, if they exist
|
|
191
|
+
if (joinDataConfig) {
|
|
192
|
+
_.each(Object.keys(joinDataConfig), (fieldName) => {
|
|
193
|
+
const fieldConfig = joinDataConfig[fieldName];
|
|
194
|
+
if (!_.isUndefined(fieldConfig.defaultValue)) { // null in jsonDataConfig will override a default value in schema!
|
|
195
|
+
joinData[fieldName] = fieldConfig.defaultValue;
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
}
|
|
194
199
|
}
|
|
195
200
|
|
|
196
201
|
|
|
197
202
|
// add new value
|
|
198
203
|
const
|
|
199
|
-
newValue = [...value], // clone, so we trigger a re-render
|
|
204
|
+
newValue = [...value], // clone Tag's full current value (array), so we trigger a re-render after adding the new value
|
|
200
205
|
newItem = {
|
|
201
206
|
id,
|
|
202
207
|
text: displayValue,
|
|
@@ -215,8 +220,8 @@ function TagComponent(props) {
|
|
|
215
220
|
});
|
|
216
221
|
setValue(newValue);
|
|
217
222
|
},
|
|
218
|
-
|
|
219
|
-
//
|
|
223
|
+
onViewEditJoinData = async (item, e) => {
|
|
224
|
+
// show the joinData viewer/editor
|
|
220
225
|
|
|
221
226
|
/* item format:
|
|
222
227
|
item = {
|
|
@@ -229,22 +234,27 @@ function TagComponent(props) {
|
|
|
229
234
|
}
|
|
230
235
|
*/
|
|
231
236
|
|
|
232
|
-
//
|
|
237
|
+
// Prepare Form to edit the joinData
|
|
233
238
|
const
|
|
234
|
-
|
|
239
|
+
// create the Form.record, format: { meters_pm_schedules__also_resets: null, meters_pm_schedules__hide_every_n: 5 }
|
|
240
|
+
record = _.mapKeys(item.joinData, (value, key) => { // add the 'model_name__' prefix so we can match JoinRepository property names
|
|
235
241
|
return modelFieldStartsWith + key;
|
|
236
242
|
}),
|
|
243
|
+
// create the Form.items
|
|
237
244
|
items = propertyDef.joinData.map((fieldName) => {
|
|
238
245
|
let obj = {
|
|
239
246
|
name: modelFieldStartsWith + fieldName,
|
|
240
247
|
};
|
|
241
|
-
|
|
248
|
+
|
|
249
|
+
// add in any specific config for joinData[fieldName]], if it exists
|
|
250
|
+
// (The outer *Editor can configure each Tag field's joinData Form item.
|
|
251
|
+
// This moves that configuration down and adds outerValueId)
|
|
242
252
|
if (joinDataConfig?.[fieldName]) {
|
|
243
|
-
const
|
|
244
|
-
|
|
253
|
+
const joinDataConfigFieldname = _.clone(joinDataConfig[fieldName]); // don't mutate original
|
|
254
|
+
joinDataConfigFieldname.outerValueId = item.id; // so that joinData can be aware of the value of the inspected ValueBox; see note in useEffect, below
|
|
245
255
|
obj = {
|
|
246
256
|
...obj,
|
|
247
|
-
...
|
|
257
|
+
...joinDataConfigFieldname,
|
|
248
258
|
};
|
|
249
259
|
}
|
|
250
260
|
|
|
@@ -253,6 +263,7 @@ function TagComponent(props) {
|
|
|
253
263
|
|
|
254
264
|
let height = 300;
|
|
255
265
|
let body;
|
|
266
|
+
const extraModalProps = {};
|
|
256
267
|
if (isViewOnly) {
|
|
257
268
|
// show Viewer
|
|
258
269
|
body = <Viewer
|
|
@@ -263,12 +274,17 @@ function TagComponent(props) {
|
|
|
263
274
|
labelWidth: 200,
|
|
264
275
|
}}
|
|
265
276
|
/>;
|
|
277
|
+
|
|
278
|
+
extraModalProps.customButtons = [
|
|
279
|
+
<Button
|
|
280
|
+
{...testProps('closeBtn')}
|
|
281
|
+
key="closeBtn"
|
|
282
|
+
onPress={hideModal}
|
|
283
|
+
text="Close"
|
|
284
|
+
className="text-white"
|
|
285
|
+
/>,
|
|
286
|
+
];
|
|
266
287
|
} else {
|
|
267
|
-
switch (items.length) {
|
|
268
|
-
case 1: height = 250; break;
|
|
269
|
-
case 2: height = 400; break;
|
|
270
|
-
default: height = 600; break;
|
|
271
|
-
}
|
|
272
288
|
body = <Form
|
|
273
289
|
editorType={EDITOR_TYPE__PLAIN}
|
|
274
290
|
isEditorViewOnly={false}
|
|
@@ -285,7 +301,7 @@ function TagComponent(props) {
|
|
|
285
301
|
]}
|
|
286
302
|
onSave={(values)=> {
|
|
287
303
|
|
|
288
|
-
// strip the '
|
|
304
|
+
// strip the 'model_name__' prefix from the field names
|
|
289
305
|
values = _.mapKeys(values, (value, key) => {
|
|
290
306
|
return key.startsWith(modelFieldStartsWith) ? key.slice(modelFieldStartsWith.length) : key;
|
|
291
307
|
});
|
|
@@ -303,6 +319,11 @@ function TagComponent(props) {
|
|
|
303
319
|
}}
|
|
304
320
|
/>;
|
|
305
321
|
}
|
|
322
|
+
switch (items.length) {
|
|
323
|
+
case 1: height = 250; break;
|
|
324
|
+
case 2: height = 400; break;
|
|
325
|
+
default: height = 600; break;
|
|
326
|
+
}
|
|
306
327
|
|
|
307
328
|
showModal({
|
|
308
329
|
title: 'Extra data for "' + item.text + '"',
|
|
@@ -312,6 +333,7 @@ function TagComponent(props) {
|
|
|
312
333
|
includeReset: false,
|
|
313
334
|
includeCancel: false,
|
|
314
335
|
body,
|
|
336
|
+
...extraModalProps,
|
|
315
337
|
});
|
|
316
338
|
},
|
|
317
339
|
onGridAdd = (selection) => {
|
|
@@ -381,54 +403,47 @@ function TagComponent(props) {
|
|
|
381
403
|
text={val.text}
|
|
382
404
|
onView={() => onView(val)}
|
|
383
405
|
showEye={showEye}
|
|
384
|
-
|
|
385
|
-
showJoin={hasJoinData}
|
|
406
|
+
onViewEditJoinData={() => onViewEditJoinData(val)}
|
|
407
|
+
showJoin={hasJoinData && (!mustSaveBeforeEditingJoinData || !isDirty)}
|
|
386
408
|
onDelete={!isViewOnly ? () => onDelete(val) : null}
|
|
387
409
|
minimizeForRow={minimizeForRow}
|
|
388
410
|
/>;
|
|
389
411
|
});
|
|
390
412
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
// return baseParams;
|
|
421
|
-
// },
|
|
422
|
-
// },
|
|
423
|
-
// },
|
|
424
|
-
// }
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
if (getBaseParams) {
|
|
413
|
+
if (!_.isUndefined(getBaseParams) && outerValueId) {
|
|
414
|
+
useEffect(() => {
|
|
415
|
+
|
|
416
|
+
// NOTE: This useEffect is so we can dynamically set the TargetRepository's baseParams,
|
|
417
|
+
// based on outerValueId, before it loads.
|
|
418
|
+
// We did this for cases where the Tag field has joinData that's managing a nested Tag field.
|
|
419
|
+
// ... This deals with recursion, so gets "alice in wonderland" quickly!
|
|
420
|
+
// If that inner Tag field has getBaseParams defined on a joinDataConfig field of the outer Tag,
|
|
421
|
+
// then that means it needs to set its baseParams dynamically, based on the value of the outer ValueBox.
|
|
422
|
+
|
|
423
|
+
// For example: in the MetersEditor:
|
|
424
|
+
// {
|
|
425
|
+
// name: 'meters__pm_schedules',
|
|
426
|
+
// mustSaveBeforeEditingJoinData: true,
|
|
427
|
+
// joinDataConfig: {
|
|
428
|
+
// also_resets: {
|
|
429
|
+
// getBaseParams: (values, outerValueId) => {
|
|
430
|
+
// const baseParams = {
|
|
431
|
+
// 'conditions[MetersPmSchedules.meter_id]': meter_id, // limit also_resets to those MetersPmSchedules related to this meter
|
|
432
|
+
// };
|
|
433
|
+
// if (outerValueId) {
|
|
434
|
+
// baseParams['conditions[MetersPmSchedules.id <>]'] = outerValueId; // exclude the ValueBox that was clicked on
|
|
435
|
+
// }
|
|
436
|
+
// return baseParams;
|
|
437
|
+
// },
|
|
438
|
+
// },
|
|
439
|
+
// },
|
|
440
|
+
// }
|
|
441
|
+
|
|
428
442
|
TargetRepository.setBaseParams(getBaseParams(value, outerValueId));
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
443
|
+
setIsInited(true);
|
|
444
|
+
|
|
445
|
+
}, [value]);
|
|
446
|
+
}
|
|
432
447
|
|
|
433
448
|
if (!isInited) {
|
|
434
449
|
return null;
|
|
@@ -16,7 +16,7 @@ export default function ValueBox(props) {
|
|
|
16
16
|
text,
|
|
17
17
|
onView,
|
|
18
18
|
showEye = false,
|
|
19
|
-
|
|
19
|
+
onViewEditJoinData,
|
|
20
20
|
showJoin = false,
|
|
21
21
|
onDelete,
|
|
22
22
|
minimizeForRow = false,
|
|
@@ -60,7 +60,7 @@ export default function ValueBox(props) {
|
|
|
60
60
|
size: styles.FORM_TAG_VALUEBOX_ICON_SIZE,
|
|
61
61
|
className: 'text-grey-600',
|
|
62
62
|
}}
|
|
63
|
-
onPress={
|
|
63
|
+
onPress={onViewEditJoinData}
|
|
64
64
|
className={clsx(
|
|
65
65
|
'ValueBox-joinBtn',
|
|
66
66
|
'h-full',
|
|
@@ -205,7 +205,7 @@ function Form(props) {
|
|
|
205
205
|
|
|
206
206
|
// Fallback to empty schema that allows any fields and defaults to valid
|
|
207
207
|
return yup.object().noUnknown(false).default({});
|
|
208
|
-
})(),
|
|
208
|
+
})() || yup.object().shape({}), // on rare occasions, validatorToUse was null. This fixes it
|
|
209
209
|
{
|
|
210
210
|
control,
|
|
211
211
|
formState,
|
|
@@ -816,6 +816,7 @@ function Form(props) {
|
|
|
816
816
|
{...testProps('field-' + name)}
|
|
817
817
|
name={name}
|
|
818
818
|
value={value}
|
|
819
|
+
isDirty={isDirty}
|
|
819
820
|
onChangeValue={(newValue) => {
|
|
820
821
|
if (newValue === undefined) {
|
|
821
822
|
newValue = null; // React Hook Form doesn't respond well when setting value to undefined
|
|
@@ -1548,7 +1549,7 @@ function Form(props) {
|
|
|
1548
1549
|
function disableRequiredYupFields(validator) {
|
|
1549
1550
|
// based on https://github.com/jquense/yup/issues/1466#issuecomment-944386480
|
|
1550
1551
|
if (!validator) {
|
|
1551
|
-
return null
|
|
1552
|
+
return yup.object().shape({}); // Return valid empty schema instead of null
|
|
1552
1553
|
}
|
|
1553
1554
|
|
|
1554
1555
|
const nextSchema = validator.clone();
|