@onehat/ui 0.4.80 → 0.4.82
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/Container/Container.js +4 -4
- package/src/Components/Form/Field/Combo/Combo.js +5 -2
- package/src/Components/Form/Field/Combo/MeterTypesCombo.js +1 -0
- package/src/Components/Form/Field/Date.js +1 -1
- package/src/Components/Form/Field/Select/PageSizeSelect.js +6 -1
- package/src/Components/Form/Field/Select/Select.js +14 -38
- package/src/Components/Form/Field/Tag/Tag.js +215 -13
- package/src/Components/Form/Field/Tag/ValueBox.js +20 -1
- package/src/Components/Form/Form.js +25 -13
- package/src/Components/Grid/Grid.js +312 -106
- package/src/Components/Grid/GridHeaderRow.js +42 -22
- package/src/Components/Grid/GridRow.js +13 -6
- package/src/Components/Grid/RowHandle.js +16 -4
- package/src/Components/Hoc/withData.js +7 -0
- package/src/Components/Hoc/withEditor.js +19 -4
- package/src/Components/Hoc/withPresetButtons.js +1 -1
- package/src/Components/Hoc/withSideEditor.js +1 -1
- package/src/Components/Icons/Join.js +10 -0
- package/src/Components/Layout/AsyncOperation.js +62 -14
- package/src/Components/Layout/CenterBox.js +1 -1
- package/src/Components/Screens/Manager.js +1 -1
- package/src/Components/Toolbar/PaginationToolbar.js +3 -1
- package/src/Components/Toolbar/Toolbar.js +10 -6
- package/src/Components/Tree/TreeNode.js +38 -9
- package/src/Components/Viewer/Viewer.js +10 -2
- package/src/Constants/Progress.js +2 -1
package/package.json
CHANGED
|
@@ -78,10 +78,10 @@ function Container(props) {
|
|
|
78
78
|
id = props.id || props.self?.path,
|
|
79
79
|
canResize = CURRENT_MODE === UI_MODE_WEB,
|
|
80
80
|
[isReady, setIsReady] = useState(false),
|
|
81
|
-
[localIsNorthCollapsed, setLocalIsNorthCollapsedRaw] = useState(north ? north.props.startsCollapsed : false),
|
|
82
|
-
[localIsSouthCollapsed, setLocalIsSouthCollapsedRaw] = useState(south ? south.props.startsCollapsed : false),
|
|
83
|
-
[localIsEastCollapsed, setLocalIsEastCollapsedRaw] = useState(east ? east.props.startsCollapsed : false),
|
|
84
|
-
[localIsWestCollapsed, setLocalIsWestCollapsedRaw] = useState(west ? west.props.startsCollapsed : false),
|
|
81
|
+
[localIsNorthCollapsed, setLocalIsNorthCollapsedRaw] = useState(north ? !!north.props.startsCollapsed : false),
|
|
82
|
+
[localIsSouthCollapsed, setLocalIsSouthCollapsedRaw] = useState(south ? !!south.props.startsCollapsed : false),
|
|
83
|
+
[localIsEastCollapsed, setLocalIsEastCollapsedRaw] = useState(east ? !!east.props.startsCollapsed : false),
|
|
84
|
+
[localIsWestCollapsed, setLocalIsWestCollapsedRaw] = useState(west ? !!west.props.startsCollapsed : false),
|
|
85
85
|
[northHeight, setNorthHeightRaw] = useState(north ? north.props.h : 0),
|
|
86
86
|
[southHeight, setSouthHeightRaw] = useState(south ? south.props.h : 0),
|
|
87
87
|
[eastWidth, setEastWidthRaw] = useState(east ? east.props.w : 0),
|
|
@@ -773,6 +773,9 @@ export const ComboComponent = forwardRef((props, ref) => {
|
|
|
773
773
|
if (CURRENT_MODE === UI_MODE_NATIVE) {
|
|
774
774
|
gridClassName += ' h-[400px] max-h-[100%]';
|
|
775
775
|
}
|
|
776
|
+
if (gridProps.className) {
|
|
777
|
+
gridClassName += ' ' + gridProps.className;
|
|
778
|
+
}
|
|
776
779
|
grid = <WhichGrid
|
|
777
780
|
showHeaders={false}
|
|
778
781
|
showHovers={true}
|
|
@@ -886,9 +889,9 @@ export const ComboComponent = forwardRef((props, ref) => {
|
|
|
886
889
|
}}
|
|
887
890
|
reference="grid"
|
|
888
891
|
parent={self}
|
|
889
|
-
className={gridClassName}
|
|
890
892
|
style={gridStyle}
|
|
891
893
|
{...gridProps}
|
|
894
|
+
className={gridClassName}
|
|
892
895
|
{..._editor}
|
|
893
896
|
/>;
|
|
894
897
|
if (CURRENT_MODE === UI_MODE_WEB) {
|
|
@@ -962,7 +965,7 @@ export const ComboComponent = forwardRef((props, ref) => {
|
|
|
962
965
|
top,
|
|
963
966
|
left,
|
|
964
967
|
width,
|
|
965
|
-
height: (menuHeight || styles.FORM_COMBO_MENU_HEIGHT) + inputHeight,
|
|
968
|
+
// height: (menuHeight || styles.FORM_COMBO_MENU_HEIGHT) + inputHeight,
|
|
966
969
|
minWidth: 100,
|
|
967
970
|
}}
|
|
968
971
|
>
|
|
@@ -601,7 +601,7 @@ export const DateElement = forwardRef((props, ref) => {
|
|
|
601
601
|
styles.FORM_DATE_CLASSNAME,
|
|
602
602
|
);
|
|
603
603
|
if (props.className) {
|
|
604
|
-
className += props.className;
|
|
604
|
+
className += ' ' + props.className;
|
|
605
605
|
}
|
|
606
606
|
if (minimizeForRow) {
|
|
607
607
|
className += ' h-auto min-h-0 max-h-[50px]';
|
|
@@ -12,7 +12,11 @@ export default function PageSizeSelect(props) {
|
|
|
12
12
|
} = props;
|
|
13
13
|
|
|
14
14
|
return useMemo(() => {
|
|
15
|
-
return <HStack
|
|
15
|
+
return <HStack
|
|
16
|
+
className={clsx(
|
|
17
|
+
'PageSizeSelect-HStack',
|
|
18
|
+
)}
|
|
19
|
+
>
|
|
16
20
|
<Select
|
|
17
21
|
data={[
|
|
18
22
|
// [ 1, '1', ],
|
|
@@ -26,6 +30,7 @@ export default function PageSizeSelect(props) {
|
|
|
26
30
|
onChangeValue={(value) => Repository.setPageSize(value)}
|
|
27
31
|
tooltip="Page Size"
|
|
28
32
|
tooltipClassName="w-[70px]"
|
|
33
|
+
fixedWidth={false}
|
|
29
34
|
/>
|
|
30
35
|
</HStack>;
|
|
31
36
|
}, [
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { forwardRef, useState, useEffect, useRef, } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Select, SelectBackdrop, SelectContent, SelectDragIndicator, SelectDragIndicatorWrapper, SelectInput, SelectIcon, SelectItem, SelectPortal, SelectTrigger,
|
|
4
|
+
Text,
|
|
4
5
|
} from '@project-components/Gluestack';
|
|
5
6
|
import clsx from 'clsx';
|
|
6
7
|
import {
|
|
@@ -15,54 +16,22 @@ import CaretDown from '../../../Icons/CaretDown.js';
|
|
|
15
16
|
import _ from 'lodash';
|
|
16
17
|
|
|
17
18
|
const SelectElement = forwardRef((props, ref) => {
|
|
18
|
-
|
|
19
|
+
const {
|
|
19
20
|
data = [], // in format [ [ value, label, ], ... ]
|
|
20
21
|
value,
|
|
21
22
|
setValue,
|
|
22
23
|
onKeyPress,
|
|
23
24
|
placeholder,
|
|
24
25
|
disableAutoFlex = false,
|
|
26
|
+
fixedWidth = true,
|
|
25
27
|
...propsToPass
|
|
26
28
|
} = props,
|
|
27
29
|
styles = UiGlobals.styles,
|
|
28
|
-
|
|
29
|
-
// [localValue, setLocalValue] = useState(value),
|
|
30
|
-
// onKeyPressLocal = (e) => {
|
|
31
|
-
// if (e.key === 'Enter') {
|
|
32
|
-
// debouncedSetValueRef.current?.cancel();
|
|
33
|
-
// setValue(localValue);
|
|
34
|
-
// }
|
|
35
|
-
// if (onKeyPress) {
|
|
36
|
-
// onKeyPress(e, localValue);
|
|
37
|
-
// }
|
|
38
|
-
// },
|
|
30
|
+
style = props.style || {},
|
|
39
31
|
items = data.map(([ value, label, ], key) => {
|
|
40
32
|
return <SelectItem key={key} label={label} value={value} />;
|
|
41
33
|
});
|
|
42
34
|
|
|
43
|
-
// useEffect(() => {
|
|
44
|
-
|
|
45
|
-
// // Set up debounce fn
|
|
46
|
-
// // Have to do this because otherwise, lodash tries to create a debounced version of the fn from only this render
|
|
47
|
-
// debouncedSetValueRef.current?.cancel(); // Cancel any previous debounced fn
|
|
48
|
-
// debouncedSetValueRef.current = _.debounce(setValue, autoSubmitDelay);
|
|
49
|
-
|
|
50
|
-
// }, [setValue]);
|
|
51
|
-
|
|
52
|
-
// useEffect(() => {
|
|
53
|
-
|
|
54
|
-
// if (value !== localValue) {
|
|
55
|
-
// // Make local value conform to externally changed value
|
|
56
|
-
// setLocalValue(value);
|
|
57
|
-
// }
|
|
58
|
-
|
|
59
|
-
// }, [value]);
|
|
60
|
-
|
|
61
|
-
// if (localValue === null || typeof localValue === 'undefined') {
|
|
62
|
-
// localValue = ''; // If the value is null or undefined, don't let this be an uncontrolled select
|
|
63
|
-
// }
|
|
64
|
-
|
|
65
|
-
const style = props.style || {};
|
|
66
35
|
// auto-set width to flex if it's not already set another way
|
|
67
36
|
if (!disableAutoFlex && !hasWidth(props) && !hasFlex(props)) {
|
|
68
37
|
style.flex = 1;
|
|
@@ -70,13 +39,12 @@ const SelectElement = forwardRef((props, ref) => {
|
|
|
70
39
|
let className = clsx(
|
|
71
40
|
'Select',
|
|
72
41
|
'min-h-[40px]',
|
|
73
|
-
'w-full',
|
|
74
42
|
'text-left',
|
|
75
43
|
'rounded-lg',
|
|
76
44
|
styles.FORM_SELECT_CLASSNAME,
|
|
77
45
|
);
|
|
78
46
|
if (props.className) {
|
|
79
|
-
className += props.className;
|
|
47
|
+
className += ' ' + props.className;
|
|
80
48
|
}
|
|
81
49
|
|
|
82
50
|
return <Select
|
|
@@ -89,7 +57,15 @@ const SelectElement = forwardRef((props, ref) => {
|
|
|
89
57
|
style={style}
|
|
90
58
|
>
|
|
91
59
|
<SelectTrigger variant="outline" size="md" className="SelectTrigger" >
|
|
92
|
-
|
|
60
|
+
{fixedWidth ?
|
|
61
|
+
<SelectInput
|
|
62
|
+
placeholder={placeholder}
|
|
63
|
+
className={clsx(
|
|
64
|
+
'SelectInput',
|
|
65
|
+
)}
|
|
66
|
+
/> :
|
|
67
|
+
<Text className="SelectText p-2">{value}</Text>}
|
|
68
|
+
|
|
93
69
|
<SelectIcon className="mr-3" as={CaretDown} />
|
|
94
70
|
</SelectTrigger>
|
|
95
71
|
<SelectPortal className="SelectPortal">
|
|
@@ -1,18 +1,26 @@
|
|
|
1
|
-
import { useRef, } from 'react';
|
|
1
|
+
import { useRef, useState, useEffect, } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
HStack,
|
|
4
4
|
VStackNative,
|
|
5
5
|
} from '@project-components/Gluestack';
|
|
6
6
|
import clsx from 'clsx';
|
|
7
|
+
import * as yup from 'yup'; // https://github.com/jquense/yup#string
|
|
8
|
+
import oneHatData from '@onehat/data';
|
|
7
9
|
import {
|
|
8
10
|
EDITOR_TYPE__WINDOWED,
|
|
9
11
|
} from '../../../../Constants/Editor.js';
|
|
12
|
+
import {
|
|
13
|
+
EDITOR_TYPE__PLAIN,
|
|
14
|
+
} from '../../../../Constants/Editor.js';
|
|
15
|
+
import Form from '../../Form.js';
|
|
16
|
+
import Viewer from '../../../Viewer/Viewer.js';
|
|
10
17
|
import withAlert from '../../../Hoc/withAlert.js';
|
|
11
18
|
import withComponent from '../../../Hoc/withComponent.js';
|
|
12
19
|
import withData from '../../../Hoc/withData.js';
|
|
13
20
|
import withModal from '../../../Hoc/withModal.js';
|
|
14
21
|
import withValue from '../../../Hoc/withValue.js';
|
|
15
22
|
import ValueBox from './ValueBox.js';
|
|
23
|
+
import Inflector from 'inflector-js';
|
|
16
24
|
import Combo, { ComboEditor } from '../Combo/Combo.js';
|
|
17
25
|
import UiGlobals from '../../../../UiGlobals.js';
|
|
18
26
|
import _ from 'lodash';
|
|
@@ -27,8 +35,12 @@ function TagComponent(props) {
|
|
|
27
35
|
minimizeForRow = false,
|
|
28
36
|
Editor,
|
|
29
37
|
_combo = {},
|
|
38
|
+
SourceRepository,
|
|
39
|
+
joinDataConfig,
|
|
40
|
+
outerValueId, // for recursion only. See note in useEffect
|
|
30
41
|
tooltip,
|
|
31
42
|
testID,
|
|
43
|
+
getBaseParams,
|
|
32
44
|
|
|
33
45
|
// parent Form
|
|
34
46
|
onChangeValue,
|
|
@@ -39,6 +51,10 @@ function TagComponent(props) {
|
|
|
39
51
|
// withComponent
|
|
40
52
|
self,
|
|
41
53
|
|
|
54
|
+
// withData
|
|
55
|
+
Repository: TargetRepository,
|
|
56
|
+
setBaseParams,
|
|
57
|
+
|
|
42
58
|
// withFilters
|
|
43
59
|
isInFilter,
|
|
44
60
|
|
|
@@ -53,11 +69,22 @@ function TagComponent(props) {
|
|
|
53
69
|
...propsToPass // break connection between Tag and Combo props
|
|
54
70
|
} = props,
|
|
55
71
|
styles = UiGlobals.styles,
|
|
72
|
+
propertyDef = SourceRepository?.getSchema().getPropertyDefinition(self.reference),
|
|
73
|
+
hasJoinData = propertyDef?.joinData?.length,
|
|
74
|
+
[JoinRepository] = useState(() => {
|
|
75
|
+
if (hasJoinData) {
|
|
76
|
+
return oneHatData.getRepository(propertyDef.joinModel, true);
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}),
|
|
80
|
+
[isInited, setIsInited] = useState(false),
|
|
81
|
+
modelFieldStartsWith = hasJoinData ? Inflector.underscore(JoinRepository.getSchema().name) + '__' : '',
|
|
56
82
|
valueRef = useRef(value),
|
|
57
83
|
onView = async (item, e) => {
|
|
84
|
+
// This method shows the record viewer
|
|
58
85
|
const
|
|
59
86
|
id = item.id,
|
|
60
|
-
repository =
|
|
87
|
+
repository = TargetRepository;
|
|
61
88
|
if (repository.isLoading) {
|
|
62
89
|
await repository.waitUntilDoneLoading();
|
|
63
90
|
}
|
|
@@ -117,7 +144,6 @@ function TagComponent(props) {
|
|
|
117
144
|
// The value we get from combo is a simple int
|
|
118
145
|
// Convert this to id and displayValue from either Repository or data array.
|
|
119
146
|
const
|
|
120
|
-
Repository = props.Repository,
|
|
121
147
|
data = props.data,
|
|
122
148
|
idIx = props.idIx,
|
|
123
149
|
displayIx = props.displayIx,
|
|
@@ -127,9 +153,9 @@ function TagComponent(props) {
|
|
|
127
153
|
|
|
128
154
|
if (!id) {
|
|
129
155
|
displayValue = '';
|
|
130
|
-
} else if (
|
|
131
|
-
if (!
|
|
132
|
-
item =
|
|
156
|
+
} else if (TargetRepository) {
|
|
157
|
+
if (!TargetRepository.isDestroyed) {
|
|
158
|
+
item = TargetRepository.getById(id);
|
|
133
159
|
if (!item) {
|
|
134
160
|
throw Error('item not found');
|
|
135
161
|
}
|
|
@@ -143,13 +169,42 @@ function TagComponent(props) {
|
|
|
143
169
|
displayValue = item[displayIx];
|
|
144
170
|
}
|
|
145
171
|
|
|
172
|
+
let joinData = {};
|
|
173
|
+
if (hasJoinData) {
|
|
174
|
+
// build up the default starting values,
|
|
175
|
+
// first with schema defaultValues...
|
|
176
|
+
const
|
|
177
|
+
allSchemaDefaults = JoinRepository.getSchema().getDefaultValues(),
|
|
178
|
+
modelSchemaDefaults = _.pickBy(allSchemaDefaults, (value, key) => {
|
|
179
|
+
return key.startsWith(modelFieldStartsWith);
|
|
180
|
+
}),
|
|
181
|
+
joinFieldNames = joinDataConfig.map(fieldConfig => fieldConfig.name || fieldConfig),
|
|
182
|
+
schemaDefaultValues = _.pick(modelSchemaDefaults, joinFieldNames),
|
|
183
|
+
strippedSchemaDefaultValues = _.mapKeys(schemaDefaultValues, (value, key) => {
|
|
184
|
+
return key.startsWith(modelFieldStartsWith) ? key.slice(modelFieldStartsWith.length) : key;
|
|
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
|
+
}
|
|
192
|
+
});
|
|
193
|
+
joinData = { ...strippedSchemaDefaultValues, ...joinData };
|
|
194
|
+
}
|
|
195
|
+
|
|
146
196
|
|
|
147
197
|
// add new value
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
198
|
+
const
|
|
199
|
+
newValue = [...value], // clone, so we trigger a re-render
|
|
200
|
+
newItem = {
|
|
201
|
+
id,
|
|
202
|
+
text: displayValue,
|
|
203
|
+
};
|
|
204
|
+
if (hasJoinData) {
|
|
205
|
+
newItem.joinData = joinData;
|
|
206
|
+
}
|
|
207
|
+
newValue.push(newItem);
|
|
153
208
|
setValue(newValue);
|
|
154
209
|
clearComboValue();
|
|
155
210
|
},
|
|
@@ -160,6 +215,105 @@ function TagComponent(props) {
|
|
|
160
215
|
});
|
|
161
216
|
setValue(newValue);
|
|
162
217
|
},
|
|
218
|
+
onJoin = async (item, e) => {
|
|
219
|
+
// This method shows the joinData viewer/editor
|
|
220
|
+
|
|
221
|
+
/* item format:
|
|
222
|
+
item = {
|
|
223
|
+
id: 3,
|
|
224
|
+
text: "1000HR PM",
|
|
225
|
+
joinData: {
|
|
226
|
+
hide_every_n: 0,
|
|
227
|
+
also_resets: '[]',
|
|
228
|
+
},
|
|
229
|
+
}
|
|
230
|
+
*/
|
|
231
|
+
|
|
232
|
+
// prepend 'model_name__' to the field names, so they match the JoinRepository property names
|
|
233
|
+
const
|
|
234
|
+
record = _.mapKeys(item.joinData, (value, key) => {
|
|
235
|
+
return modelFieldStartsWith + key;
|
|
236
|
+
}),
|
|
237
|
+
items = propertyDef.joinData.map((fieldName) => {
|
|
238
|
+
let obj = {
|
|
239
|
+
name: modelFieldStartsWith + fieldName,
|
|
240
|
+
};
|
|
241
|
+
// add in any config from joinDataConfig for this field
|
|
242
|
+
if (joinDataConfig?.[fieldName]) {
|
|
243
|
+
const jdcf = _.clone(joinDataConfig[fieldName]); // don't mutate original
|
|
244
|
+
jdcf.outerValueId = item.id;
|
|
245
|
+
obj = {
|
|
246
|
+
...obj,
|
|
247
|
+
...jdcf,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return obj;
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
let height = 300;
|
|
255
|
+
let body;
|
|
256
|
+
if (isViewOnly) {
|
|
257
|
+
// show Viewer
|
|
258
|
+
body = <Viewer
|
|
259
|
+
record={record}
|
|
260
|
+
Repository={JoinRepository}
|
|
261
|
+
items={items}
|
|
262
|
+
columnDefaults={{
|
|
263
|
+
labelWidth: 200,
|
|
264
|
+
}}
|
|
265
|
+
/>;
|
|
266
|
+
} else {
|
|
267
|
+
switch (items.length) {
|
|
268
|
+
case 1: height = 250; break;
|
|
269
|
+
case 2: height = 400; break;
|
|
270
|
+
default: height = 600; break;
|
|
271
|
+
}
|
|
272
|
+
body = <Form
|
|
273
|
+
editorType={EDITOR_TYPE__PLAIN}
|
|
274
|
+
isEditorViewOnly={false}
|
|
275
|
+
record={record}
|
|
276
|
+
Repository={JoinRepository}
|
|
277
|
+
items={items}
|
|
278
|
+
additionalFooterButtons={[
|
|
279
|
+
{
|
|
280
|
+
text: 'Cancel',
|
|
281
|
+
onPress: hideModal,
|
|
282
|
+
skipSubmit: true,
|
|
283
|
+
variant: 'outline',
|
|
284
|
+
}
|
|
285
|
+
]}
|
|
286
|
+
onSave={(values)=> {
|
|
287
|
+
|
|
288
|
+
// strip the 'model__' prefix from the field names
|
|
289
|
+
values = _.mapKeys(values, (value, key) => {
|
|
290
|
+
return key.startsWith(modelFieldStartsWith) ? key.slice(modelFieldStartsWith.length) : key;
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
// Put these values back on joinData
|
|
294
|
+
item.joinData = values;
|
|
295
|
+
const newValue = [...valueRef.current]; // clone
|
|
296
|
+
const ix = _.findIndex(newValue, (val) => {
|
|
297
|
+
return val.id === item.id;
|
|
298
|
+
});
|
|
299
|
+
newValue[ix] = item;
|
|
300
|
+
setValue(newValue);
|
|
301
|
+
|
|
302
|
+
hideModal();
|
|
303
|
+
}}
|
|
304
|
+
/>;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
showModal({
|
|
308
|
+
title: 'Extra data for "' + item.text + '"',
|
|
309
|
+
w: 400,
|
|
310
|
+
h: height,
|
|
311
|
+
canClose: true,
|
|
312
|
+
includeReset: false,
|
|
313
|
+
includeCancel: false,
|
|
314
|
+
body,
|
|
315
|
+
});
|
|
316
|
+
},
|
|
163
317
|
onGridAdd = (selection) => {
|
|
164
318
|
// underlying GridEditor added a record.
|
|
165
319
|
// add it to this Tag's value
|
|
@@ -226,12 +380,60 @@ function TagComponent(props) {
|
|
|
226
380
|
key={ix}
|
|
227
381
|
text={val.text}
|
|
228
382
|
onView={() => onView(val)}
|
|
229
|
-
onDelete={!isViewOnly ? () => onDelete(val) : null}
|
|
230
383
|
showEye={showEye}
|
|
384
|
+
onJoin={() => onJoin(val)}
|
|
385
|
+
showJoin={hasJoinData}
|
|
386
|
+
onDelete={!isViewOnly ? () => onDelete(val) : null}
|
|
231
387
|
minimizeForRow={minimizeForRow}
|
|
232
388
|
/>;
|
|
233
389
|
});
|
|
234
390
|
|
|
391
|
+
useEffect(() => {
|
|
392
|
+
|
|
393
|
+
// NOTE: This useEffect is so we can set the Target baseParams before it loads
|
|
394
|
+
// We did this for cases where the Tag field has joinData that's managing a nested Tag field.
|
|
395
|
+
// ... This deals with recursion, so gets "alice in wonderland" quickly!
|
|
396
|
+
// If that inner Tag field has getBaseParams defined on the joinDataConfig of the outer Tag,
|
|
397
|
+
// then that means it needs to set its baseParams dynamically, based on the values that are
|
|
398
|
+
// currently set, as well as the value of the outer ValueBox that was clicked on.
|
|
399
|
+
|
|
400
|
+
// For example: in the MetersEditor:
|
|
401
|
+
// {
|
|
402
|
+
// name: 'meters__pm_schedules',
|
|
403
|
+
// parent: self,
|
|
404
|
+
// reference: 'meters__pm_schedules',
|
|
405
|
+
// joinDataConfig: {
|
|
406
|
+
// also_resets: {
|
|
407
|
+
// getBaseParams: (values, outerValueId) => {
|
|
408
|
+
// const
|
|
409
|
+
// baseParams = {
|
|
410
|
+
// 'conditions[MetersPmSchedules.meter_id]': meter_id, // limit also_resets to those MetersPmSchedules related to this meter
|
|
411
|
+
// },
|
|
412
|
+
// ids = values.map((value) => value.id),
|
|
413
|
+
// mpsValues = JSON.parse(self.children.meters__pm_schedules?.value || '[]');
|
|
414
|
+
// if (outerValueId) {
|
|
415
|
+
// ids.push(outerValueId);
|
|
416
|
+
// }
|
|
417
|
+
// if (!_.isEmpty(ids)) {
|
|
418
|
+
// baseParams['conditions[MetersPmSchedules.pm_schedule_id NOT IN]'] = ids;
|
|
419
|
+
// }
|
|
420
|
+
// return baseParams;
|
|
421
|
+
// },
|
|
422
|
+
// },
|
|
423
|
+
// },
|
|
424
|
+
// }
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
if (getBaseParams) {
|
|
428
|
+
TargetRepository.setBaseParams(getBaseParams(value, outerValueId));
|
|
429
|
+
}
|
|
430
|
+
setIsInited(true);
|
|
431
|
+
}, [value]);
|
|
432
|
+
|
|
433
|
+
if (!isInited) {
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
|
|
235
437
|
valueRef.current = value; // the onGrid* methods were dealing with stale data, so use a ref, and update it here
|
|
236
438
|
|
|
237
439
|
let WhichCombo = Combo;
|
|
@@ -309,7 +511,7 @@ function TagComponent(props) {
|
|
|
309
511
|
|
|
310
512
|
{!isViewOnly &&
|
|
311
513
|
<WhichCombo
|
|
312
|
-
Repository={
|
|
514
|
+
Repository={TargetRepository}
|
|
313
515
|
Editor={props.Editor}
|
|
314
516
|
onSubmit={onChangeComboValue}
|
|
315
517
|
parent={self}
|
|
@@ -6,6 +6,7 @@ import clsx from 'clsx';
|
|
|
6
6
|
import testProps from '../../../../Functions/testProps.js';
|
|
7
7
|
import IconButton from '../../../Buttons/IconButton.js';
|
|
8
8
|
import Eye from '../../../Icons/Eye.js';
|
|
9
|
+
import Edit from '../../../Icons/Edit.js';
|
|
9
10
|
import Xmark from '../../../Icons/Xmark.js';
|
|
10
11
|
import UiGlobals from '../../../../UiGlobals.js';
|
|
11
12
|
import _ from 'lodash';
|
|
@@ -14,8 +15,10 @@ export default function ValueBox(props) {
|
|
|
14
15
|
const {
|
|
15
16
|
text,
|
|
16
17
|
onView,
|
|
18
|
+
showEye = false,
|
|
19
|
+
onJoin,
|
|
20
|
+
showJoin = false,
|
|
17
21
|
onDelete,
|
|
18
|
-
showEye,
|
|
19
22
|
minimizeForRow = false,
|
|
20
23
|
} = props,
|
|
21
24
|
styles = UiGlobals.styles;
|
|
@@ -49,6 +52,22 @@ export default function ValueBox(props) {
|
|
|
49
52
|
styles.FORM_TAG_BTN_CLASSNAME,
|
|
50
53
|
)}
|
|
51
54
|
/>}
|
|
55
|
+
{showJoin &&
|
|
56
|
+
<IconButton
|
|
57
|
+
{...testProps('joinBtn')}
|
|
58
|
+
icon={Edit}
|
|
59
|
+
_icon={{
|
|
60
|
+
size: styles.FORM_TAG_VALUEBOX_ICON_SIZE,
|
|
61
|
+
className: 'text-grey-600',
|
|
62
|
+
}}
|
|
63
|
+
onPress={onJoin}
|
|
64
|
+
className={clsx(
|
|
65
|
+
'ValueBox-joinBtn',
|
|
66
|
+
'h-full',
|
|
67
|
+
minimizeForRow ? 'py-0' : '',
|
|
68
|
+
styles.FORM_TAG_BTN_CLASSNAME,
|
|
69
|
+
)}
|
|
70
|
+
/>}
|
|
52
71
|
<Text
|
|
53
72
|
className={clsx(
|
|
54
73
|
'ValueBox-Text',
|
|
@@ -183,7 +183,7 @@ function Form(props) {
|
|
|
183
183
|
pointerEvents: fabOpacity.value > 0 ? 'auto' : 'none', // Disable interaction when invisible
|
|
184
184
|
};
|
|
185
185
|
}),
|
|
186
|
-
initialValues = _.merge(startingValues, (record && !record.isDestroyed ? record.submitValues : {})),
|
|
186
|
+
initialValues = _.merge(startingValues, ((record && !record.isDestroyed) ? (record.submitValues || record) : {})),
|
|
187
187
|
defaultValues = isMultiple ? getNullFieldValues(initialValues, Repository) : initialValues, // when multiple entities, set all default values to null
|
|
188
188
|
validatorToUse = (() => {
|
|
189
189
|
// If a custom validator is provided, use it
|
|
@@ -440,6 +440,7 @@ function Form(props) {
|
|
|
440
440
|
}
|
|
441
441
|
if (type.match(/Tag/)) {
|
|
442
442
|
elementClassName += ' overflow-auto';
|
|
443
|
+
configPropsToPass.SourceRepository = Repository;
|
|
443
444
|
}
|
|
444
445
|
if (!type.match(/Toggle/)) {
|
|
445
446
|
elementClassName += ' h-full';
|
|
@@ -540,8 +541,8 @@ function Form(props) {
|
|
|
540
541
|
if (isHidden) {
|
|
541
542
|
return null;
|
|
542
543
|
}
|
|
543
|
-
if (type === 'DisplayField') {
|
|
544
|
-
isEditable = false;
|
|
544
|
+
if (type === 'DisplayField' || type?.match(/Grid/)) {
|
|
545
|
+
isEditable = false; // this merely disables the FormController for this element
|
|
545
546
|
}
|
|
546
547
|
if (!itemPropsToPass.className) {
|
|
547
548
|
itemPropsToPass.className = '';
|
|
@@ -667,16 +668,19 @@ function Form(props) {
|
|
|
667
668
|
if (isEditorViewOnly || !isEditable) {
|
|
668
669
|
let value = null;
|
|
669
670
|
if (isSingle) {
|
|
670
|
-
value = record?.properties[name]?.displayValue || null;
|
|
671
|
-
if (_.isNil(value) && record
|
|
671
|
+
value = record?.properties?.[name]?.displayValue || null;
|
|
672
|
+
if (_.isNil(value) && !_.isNil(record?.[name])) {
|
|
672
673
|
value = record[name];
|
|
673
674
|
}
|
|
674
|
-
if (_.isNil(value) && startingValues
|
|
675
|
+
if (_.isNil(value) && !_.isNil(startingValues?.[name])) {
|
|
675
676
|
value = startingValues[name];
|
|
676
677
|
}
|
|
677
678
|
}
|
|
679
|
+
if (type.match(/Tag/)) {
|
|
680
|
+
itemPropsToPass.SourceRepository = Repository;
|
|
681
|
+
}
|
|
678
682
|
|
|
679
|
-
let elementClassName = 'field-' + name;
|
|
683
|
+
let elementClassName = name ? 'field-' + name : '';
|
|
680
684
|
const defaultsClassName = defaults.className;
|
|
681
685
|
if (defaultsClassName) {
|
|
682
686
|
elementClassName += ' ' + defaultsClassName;
|
|
@@ -689,7 +693,6 @@ function Form(props) {
|
|
|
689
693
|
if (viewerTypeClassName) {
|
|
690
694
|
elementClassName += ' ' + viewerTypeClassName;
|
|
691
695
|
}
|
|
692
|
-
|
|
693
696
|
let element = <Element
|
|
694
697
|
{...testProps('field-' + name)}
|
|
695
698
|
value={value}
|
|
@@ -787,6 +790,9 @@ function Form(props) {
|
|
|
787
790
|
if (getDynamicProps) {
|
|
788
791
|
dynamicProps = getDynamicProps({ fieldState, formSetValue, formGetValues, formState });
|
|
789
792
|
}
|
|
793
|
+
if (type.match(/Tag/)) {
|
|
794
|
+
itemPropsToPass.SourceRepository = Repository;
|
|
795
|
+
}
|
|
790
796
|
|
|
791
797
|
let elementClassName = 'Form-Element field-' + name + ' w-full';
|
|
792
798
|
const defaultsClassName = defaults.className;
|
|
@@ -886,7 +892,8 @@ function Form(props) {
|
|
|
886
892
|
>*</Text>;
|
|
887
893
|
}
|
|
888
894
|
}
|
|
889
|
-
|
|
895
|
+
const labelToUse = dynamicProps.label || label;
|
|
896
|
+
if (!disableLabels && labelToUse && editorType !== EDITOR_TYPE__INLINE) {
|
|
890
897
|
const style = {};
|
|
891
898
|
if (defaults?.labelWidth) {
|
|
892
899
|
style.width = defaults.labelWidth;
|
|
@@ -901,7 +908,7 @@ function Form(props) {
|
|
|
901
908
|
element = <HStack className="Form-HStack8 w-full">
|
|
902
909
|
<Label style={style}>
|
|
903
910
|
{requiredIndicator}
|
|
904
|
-
{
|
|
911
|
+
{labelToUse}
|
|
905
912
|
</Label>
|
|
906
913
|
{element}
|
|
907
914
|
</HStack>;
|
|
@@ -909,7 +916,7 @@ function Form(props) {
|
|
|
909
916
|
element = <VStack className="Form-VStack9 w-full mt-3">
|
|
910
917
|
<Label style={style}>
|
|
911
918
|
{requiredIndicator}
|
|
912
|
-
{
|
|
919
|
+
{labelToUse}
|
|
913
920
|
</Label>
|
|
914
921
|
{element}
|
|
915
922
|
</VStack>;
|
|
@@ -1485,8 +1492,13 @@ function Form(props) {
|
|
|
1485
1492
|
|
|
1486
1493
|
} // END if (containerWidth)
|
|
1487
1494
|
|
|
1488
|
-
let className =
|
|
1489
|
-
|
|
1495
|
+
let className = clsx(
|
|
1496
|
+
'Form-VStackNative',
|
|
1497
|
+
'[transform:translateZ(0)]', // so embedded FAB will be relative to this container, not to viewport
|
|
1498
|
+
);
|
|
1499
|
+
if (props.className) {
|
|
1500
|
+
className += ' ' + props.className;
|
|
1501
|
+
}
|
|
1490
1502
|
const scrollToTopAnchor = <Box ref={(el) => (ancillaryItemsRef.current[0] = el)} className="h-0" />;
|
|
1491
1503
|
return <VStackNative
|
|
1492
1504
|
ref={formRef}
|