@onehat/ui 0.3.29 → 0.3.30
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
CHANGED
|
@@ -1,28 +1,79 @@
|
|
|
1
|
-
import { useState, } from 'react';
|
|
1
|
+
import { useState, useRef, } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Box,
|
|
4
4
|
Column,
|
|
5
5
|
Row,
|
|
6
6
|
Text,
|
|
7
7
|
} from 'native-base';
|
|
8
|
+
import FieldSetContext from '../../Contexts/FieldSetContext.js';
|
|
9
|
+
import useForceUpdate from '../../Hooks/useForceUpdate.js';
|
|
8
10
|
import UiGlobals from '../../UiGlobals.js';
|
|
9
11
|
import IconButton from '../Buttons/IconButton.js';
|
|
12
|
+
import CheckboxButton from '../Buttons/CheckboxButton.js';
|
|
10
13
|
import CaretUp from '../Icons/CaretUp.js';
|
|
11
14
|
import CaretDown from '../Icons/CaretDown.js';
|
|
15
|
+
import _ from 'lodash';
|
|
12
16
|
|
|
13
17
|
export default function FieldSet(props) {
|
|
14
18
|
const {
|
|
15
19
|
title,
|
|
16
20
|
helpText,
|
|
17
21
|
children,
|
|
22
|
+
isCollapsible = true,
|
|
18
23
|
isCollapsed,
|
|
19
24
|
hasErrors,
|
|
25
|
+
showToggleAllCheckbox = false,
|
|
20
26
|
...propsToPass
|
|
21
27
|
} = props,
|
|
22
28
|
styles = UiGlobals.styles,
|
|
29
|
+
forceUpdate = useForceUpdate(),
|
|
30
|
+
childRefs = useRef([]),
|
|
31
|
+
isAllCheckedRef = useRef(false),
|
|
23
32
|
[localIsCollapsed, setLocalIsCollapsed] = useState(isCollapsed),
|
|
33
|
+
getIsAllChecked = () => {
|
|
34
|
+
return isAllCheckedRef.current;
|
|
35
|
+
},
|
|
36
|
+
setIsAllChecked = (bool) => {
|
|
37
|
+
isAllCheckedRef.current = bool;
|
|
38
|
+
forceUpdate();
|
|
39
|
+
},
|
|
24
40
|
onToggleCollapse = () => {
|
|
25
41
|
setLocalIsCollapsed(!localIsCollapsed);
|
|
42
|
+
},
|
|
43
|
+
onToggleAllChecked = () => {
|
|
44
|
+
const bool = !getIsAllChecked();
|
|
45
|
+
setIsAllChecked(bool);
|
|
46
|
+
|
|
47
|
+
_.each(childRefs.current, (child) => {
|
|
48
|
+
if (child.value !== bool) {
|
|
49
|
+
child.value = bool;
|
|
50
|
+
child.setValue(bool);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
},
|
|
54
|
+
registerChild = (child) => {
|
|
55
|
+
childRefs.current.push(child);
|
|
56
|
+
checkChildren();
|
|
57
|
+
},
|
|
58
|
+
onChangeValue = (value, childRef) => {
|
|
59
|
+
const child = _.find(childRefs.current, child => child.childRef === childRef);
|
|
60
|
+
if (child.value !== value) {
|
|
61
|
+
child.value = value;
|
|
62
|
+
checkChildren();
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
checkChildren = () => {
|
|
66
|
+
let isAllChecked = true;
|
|
67
|
+
_.each(childRefs.current, (child) => {
|
|
68
|
+
if (!child.value) {
|
|
69
|
+
isAllChecked = false;
|
|
70
|
+
return false; // break
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (isAllChecked !== getIsAllChecked()) {
|
|
75
|
+
setIsAllChecked(isAllChecked);
|
|
76
|
+
}
|
|
26
77
|
};
|
|
27
78
|
|
|
28
79
|
return <Box
|
|
@@ -50,16 +101,34 @@ export default function FieldSet(props) {
|
|
|
50
101
|
numberOfLines={1}
|
|
51
102
|
ellipsizeMode="head"
|
|
52
103
|
>{title}</Text>
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
104
|
+
{showToggleAllCheckbox && <Row alignSelf="right">
|
|
105
|
+
<Text
|
|
106
|
+
fontSize={styles.FORM_FIELDSET_FONTSIZE}
|
|
107
|
+
py={1}
|
|
108
|
+
px={3}
|
|
109
|
+
flex={1}
|
|
110
|
+
numberOfLines={1}
|
|
111
|
+
>Toggle All?</Text>
|
|
112
|
+
<CheckboxButton
|
|
113
|
+
isChecked={getIsAllChecked()}
|
|
114
|
+
onPress={onToggleAllChecked}
|
|
115
|
+
_icon={{
|
|
116
|
+
size: 'lg',
|
|
117
|
+
}}
|
|
118
|
+
/>
|
|
119
|
+
</Row>}
|
|
120
|
+
{isCollapsible && <IconButton
|
|
121
|
+
_icon={{
|
|
122
|
+
as: localIsCollapsed ? <CaretDown /> : <CaretUp />,
|
|
123
|
+
size: 'sm',
|
|
124
|
+
color: 'trueGray.300',
|
|
125
|
+
}}
|
|
126
|
+
onPress={onToggleCollapse}
|
|
127
|
+
/>}
|
|
61
128
|
</Row>}
|
|
62
129
|
{helpText && <Text>{helpText}</Text>}
|
|
63
|
-
{!localIsCollapsed &&
|
|
130
|
+
{!localIsCollapsed && <FieldSetContext.Provider value={{ registerChild, onChangeValue, }}>
|
|
131
|
+
{children}
|
|
132
|
+
</FieldSetContext.Provider>}
|
|
64
133
|
</Box>;
|
|
65
134
|
}
|
|
@@ -65,10 +65,12 @@ function Form(props) {
|
|
|
65
65
|
validator, // custom validator, mainly for EDITOR_TYPE__PLAIN
|
|
66
66
|
footerProps = {},
|
|
67
67
|
buttonGroupProps = {}, // buttons in footer
|
|
68
|
+
checkIsEditingDisabled = true,
|
|
68
69
|
onBack,
|
|
69
70
|
onReset,
|
|
70
71
|
onViewMode,
|
|
71
|
-
|
|
72
|
+
submitBtnLabel,
|
|
73
|
+
onSubmit,
|
|
72
74
|
additionalEditButtons = [],
|
|
73
75
|
|
|
74
76
|
// sizing of outer container
|
|
@@ -274,7 +276,7 @@ function Form(props) {
|
|
|
274
276
|
editorTypeProps = {};
|
|
275
277
|
|
|
276
278
|
const propertyDef = name && Repository?.getSchema().getPropertyDefinition(name);
|
|
277
|
-
if (propertyDef?.isEditingDisabled) {
|
|
279
|
+
if (propertyDef?.isEditingDisabled && checkIsEditingDisabled) {
|
|
278
280
|
isEditable = false;
|
|
279
281
|
}
|
|
280
282
|
if (!type) {
|
|
@@ -479,6 +481,13 @@ function Form(props) {
|
|
|
479
481
|
const values = record.submitValues;
|
|
480
482
|
reset(values);
|
|
481
483
|
}
|
|
484
|
+
},
|
|
485
|
+
onSubmitDecorated = async (data, e) => {
|
|
486
|
+
const result = await onSubmit(data, e);
|
|
487
|
+
if (result) {
|
|
488
|
+
const values = record.submitValues;
|
|
489
|
+
reset(values);
|
|
490
|
+
}
|
|
482
491
|
};
|
|
483
492
|
|
|
484
493
|
useEffect(() => {
|
|
@@ -579,9 +588,11 @@ function Form(props) {
|
|
|
579
588
|
break;
|
|
580
589
|
}
|
|
581
590
|
|
|
582
|
-
let isSaveDisabled = false
|
|
591
|
+
let isSaveDisabled = false,
|
|
592
|
+
isSubmitDisabled = false;
|
|
583
593
|
if (!_.isEmpty(formState.errors)) {
|
|
584
594
|
isSaveDisabled = true;
|
|
595
|
+
isSubmitDisabled = true;
|
|
585
596
|
}
|
|
586
597
|
if (_.isEmpty(formState.dirtyFields) && !record?.isRemotePhantom) {
|
|
587
598
|
isSaveDisabled = true;
|
|
@@ -655,7 +666,14 @@ function Form(props) {
|
|
|
655
666
|
onPress={(e) => handleSubmit(onSaveDecorated, onSubmitError)(e)}
|
|
656
667
|
isDisabled={isSaveDisabled}
|
|
657
668
|
color="#fff"
|
|
658
|
-
>{
|
|
669
|
+
>{editorMode === EDITOR_MODE__ADD ? 'Add' : 'Save'}</Button>}
|
|
670
|
+
{onSubmit && <Button
|
|
671
|
+
key="submitBtn"
|
|
672
|
+
onPress={(e) => handleSubmit(onSubmitDecorated, onSubmitError)(e)}
|
|
673
|
+
isDisabled={isSubmitDisabled}
|
|
674
|
+
color="#fff"
|
|
675
|
+
>{submitBtnLabel || 'Submit'}</Button>}
|
|
676
|
+
|
|
659
677
|
{isEditorViewOnly && onClose && editorType !== EDITOR_TYPE__SIDE && <Button
|
|
660
678
|
key="closeBtn"
|
|
661
679
|
onPress={onClose}
|
|
@@ -3,8 +3,8 @@ import {
|
|
|
3
3
|
Column,
|
|
4
4
|
Button,
|
|
5
5
|
Modal,
|
|
6
|
-
Row,
|
|
7
6
|
} from 'native-base';
|
|
7
|
+
import * as yup from 'yup'; // https://github.com/jquense/yup#string
|
|
8
8
|
import Inflector from 'inflector-js';
|
|
9
9
|
import qs from 'qs';
|
|
10
10
|
import FormPanel from '../Panel/FormPanel.js';
|
|
@@ -35,6 +35,10 @@ export default function withPdfButton(WrappedComponent) {
|
|
|
35
35
|
// withData
|
|
36
36
|
Repository,
|
|
37
37
|
model,
|
|
38
|
+
|
|
39
|
+
// withSelection
|
|
40
|
+
selection,
|
|
41
|
+
|
|
38
42
|
} = props,
|
|
39
43
|
[isModalShown, setIsModalShown] = useState(false),
|
|
40
44
|
[width, height] = useAdjustedWindowSize(500, 800);
|
|
@@ -63,6 +67,8 @@ export default function withPdfButton(WrappedComponent) {
|
|
|
63
67
|
type: 'Checkbox',
|
|
64
68
|
};
|
|
65
69
|
}),
|
|
70
|
+
showToggleAllCheckbox: true,
|
|
71
|
+
isCollapsible: false,
|
|
66
72
|
});
|
|
67
73
|
}
|
|
68
74
|
|
|
@@ -78,6 +84,10 @@ export default function withPdfButton(WrappedComponent) {
|
|
|
78
84
|
if (!item.defaults) {
|
|
79
85
|
item.defaults = {};
|
|
80
86
|
}
|
|
87
|
+
if (type === 'FieldSet') {
|
|
88
|
+
item.showToggleAllCheckbox = true;
|
|
89
|
+
item.isCollapsible = false;
|
|
90
|
+
}
|
|
81
91
|
item.defaults.labelWidth = '90%';
|
|
82
92
|
if (!_.isEmpty(items)) {
|
|
83
93
|
const defaults = item.defaults;
|
|
@@ -97,6 +107,12 @@ export default function withPdfButton(WrappedComponent) {
|
|
|
97
107
|
item.type = 'Checkbox';
|
|
98
108
|
return item;
|
|
99
109
|
},
|
|
110
|
+
buildValidator = (modalItems) => {
|
|
111
|
+
|
|
112
|
+
// TODO: Build a real validator that checks all modalItems as booleans
|
|
113
|
+
|
|
114
|
+
return yup.object();
|
|
115
|
+
},
|
|
100
116
|
getStartingValues = (modalItems) => {
|
|
101
117
|
const startingValues = {};
|
|
102
118
|
function walkTree(item) {
|
|
@@ -117,9 +133,12 @@ export default function withPdfButton(WrappedComponent) {
|
|
|
117
133
|
return startingValues;
|
|
118
134
|
},
|
|
119
135
|
getPdf = (data) => {
|
|
136
|
+
data.id = selection[0].id;
|
|
137
|
+
|
|
120
138
|
const
|
|
121
139
|
url = UiGlobals.baseURL + model + '/viewPdf?',
|
|
122
140
|
queryString = qs.stringify(data);
|
|
141
|
+
|
|
123
142
|
window.open(url + queryString, '_blank');
|
|
124
143
|
};
|
|
125
144
|
|
|
@@ -142,7 +161,8 @@ export default function withPdfButton(WrappedComponent) {
|
|
|
142
161
|
if (isModalShown) {
|
|
143
162
|
const
|
|
144
163
|
modalItems = buildModalItems(),
|
|
145
|
-
startingValues = getStartingValues(modalItems)
|
|
164
|
+
startingValues = getStartingValues(modalItems),
|
|
165
|
+
validator = buildValidator(modalItems);
|
|
146
166
|
modal = <Modal
|
|
147
167
|
isOpen={true}
|
|
148
168
|
onClose={() => setIsModalShown(false)}
|
|
@@ -156,14 +176,16 @@ export default function withPdfButton(WrappedComponent) {
|
|
|
156
176
|
Repository={Repository}
|
|
157
177
|
items={modalItems}
|
|
158
178
|
startingValues={startingValues}
|
|
179
|
+
validator={validator}
|
|
180
|
+
checkIsEditingDisabled={false}
|
|
159
181
|
onCancel={(e) => {
|
|
160
182
|
setIsModalShown(false);
|
|
161
183
|
}}
|
|
162
|
-
|
|
184
|
+
onSubmit={(data, e) => {
|
|
163
185
|
getPdf(data);
|
|
164
186
|
setIsModalShown(false);
|
|
165
187
|
}}
|
|
166
|
-
|
|
188
|
+
submitBtnLabel="View PDF"
|
|
167
189
|
/>
|
|
168
190
|
</Column>
|
|
169
191
|
</Modal>;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { useState, useEffect, } from 'react';
|
|
1
|
+
import { useState, useEffect, useRef, useContext, useCallback, } from 'react';
|
|
2
2
|
import natsort from 'natsort';
|
|
3
|
+
import useForceUpdate from '../../Hooks/useForceUpdate.js';
|
|
4
|
+
import FieldSetContext from '../../Contexts/FieldSetContext.js';
|
|
3
5
|
import _ from 'lodash';
|
|
4
6
|
|
|
5
7
|
// This HOC gives the component value props, primarily for a Form Field.
|
|
@@ -27,8 +29,23 @@ export default function withValue(WrappedComponent) {
|
|
|
27
29
|
Repository,
|
|
28
30
|
idIx,
|
|
29
31
|
} = props,
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
forceUpdate = useForceUpdate(),
|
|
33
|
+
childRef = useRef({}),
|
|
34
|
+
onChangeValueRef = useRef(),
|
|
35
|
+
localValueRef = useRef(startingValue || value),
|
|
36
|
+
fieldSetOnChangeValueRef = useRef(),
|
|
37
|
+
fieldSetContext = useContext(FieldSetContext),
|
|
38
|
+
fieldSetRegisterChild = fieldSetContext?.registerChild,
|
|
39
|
+
fieldSetOnChangeValue = fieldSetContext?.onChangeValue,
|
|
40
|
+
getLocalValue = () => {
|
|
41
|
+
return localValueRef.current;
|
|
42
|
+
},
|
|
43
|
+
setLocalValue = (value) => {
|
|
44
|
+
localValueRef.current = value;
|
|
45
|
+
forceUpdate();
|
|
46
|
+
},
|
|
47
|
+
setValueRef = useRef((newValue) => {
|
|
48
|
+
// NOTE: We useRef so that this function stays current after renders
|
|
32
49
|
if (valueIsAlwaysArray && !_.isArray(newValue)) {
|
|
33
50
|
newValue = _.isNil(newValue) ? [] : [newValue];
|
|
34
51
|
}
|
|
@@ -62,15 +79,21 @@ export default function withValue(WrappedComponent) {
|
|
|
62
79
|
newValue = JSON.stringify(newValue);
|
|
63
80
|
}
|
|
64
81
|
|
|
65
|
-
if (newValue ===
|
|
82
|
+
if (newValue === getLocalValue()) {
|
|
66
83
|
return;
|
|
67
84
|
}
|
|
68
85
|
|
|
69
86
|
setLocalValue(newValue);
|
|
70
87
|
|
|
71
|
-
if (
|
|
72
|
-
|
|
88
|
+
if (onChangeValueRef.current) {
|
|
89
|
+
onChangeValueRef.current(newValue, childRef.current);
|
|
73
90
|
}
|
|
91
|
+
if (fieldSetOnChangeValueRef.current) {
|
|
92
|
+
fieldSetOnChangeValueRef.current(newValue, childRef.current);
|
|
93
|
+
}
|
|
94
|
+
}),
|
|
95
|
+
setValue = (args) => {
|
|
96
|
+
setValueRef.current(args);
|
|
74
97
|
},
|
|
75
98
|
onChangeSelection = (selection) => {
|
|
76
99
|
let value = null,
|
|
@@ -96,15 +119,29 @@ export default function withValue(WrappedComponent) {
|
|
|
96
119
|
setValue(value);
|
|
97
120
|
};
|
|
98
121
|
|
|
122
|
+
// Ensure these passed functions stay current after render
|
|
123
|
+
onChangeValueRef.current = onChangeValue;
|
|
124
|
+
fieldSetOnChangeValueRef.current = fieldSetOnChangeValue;
|
|
125
|
+
|
|
99
126
|
useEffect(() => {
|
|
100
|
-
if (!_.isEqual(value,
|
|
127
|
+
if (!_.isEqual(value, getLocalValue())) {
|
|
101
128
|
setLocalValue(value);
|
|
102
129
|
}
|
|
103
130
|
}, [value]);
|
|
104
131
|
|
|
132
|
+
if (fieldSetRegisterChild) {
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
fieldSetRegisterChild({
|
|
135
|
+
childRef: childRef.current,
|
|
136
|
+
value,
|
|
137
|
+
setValue: setValueRef.current,
|
|
138
|
+
});
|
|
139
|
+
}, []);
|
|
140
|
+
}
|
|
141
|
+
|
|
105
142
|
|
|
106
143
|
// Convert localValue to normal JS primitives for field components
|
|
107
|
-
let convertedValue =
|
|
144
|
+
let convertedValue = getLocalValue();
|
|
108
145
|
if (_.isString(convertedValue) && valueAsStringifiedJson && !_.isNil(convertedValue)) {
|
|
109
146
|
convertedValue = JSON.parse(convertedValue);
|
|
110
147
|
}
|