@onehat/ui 0.3.238 → 0.3.241
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/Components/Form/Field/TextArea.js +42 -5
- package/src/Components/Form/Form.js +50 -31
- package/src/Components/Grid/Grid.js +26 -0
- package/src/Components/Hoc/withInlineEditor.js +51 -39
- package/src/Components/Hoc/withInlineSideEditor.js +9 -0
- package/src/Components/Hoc/withSideEditor.js +3 -2
- package/src/Components/Hoc/withWindowedEditor.js +3 -2
- package/src/Constants/Editor.js +1 -0
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useState, useEffect, useRef, } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
TextArea,
|
|
4
4
|
} from 'native-base';
|
|
@@ -10,12 +10,49 @@ import _ from 'lodash';
|
|
|
10
10
|
|
|
11
11
|
const
|
|
12
12
|
TextAreaElement = (props) => {
|
|
13
|
-
|
|
13
|
+
let { // so localValue can be changed, if needed
|
|
14
|
+
setValue,
|
|
15
|
+
autoSubmit = true, // automatically setValue after user stops typing for autoSubmitDelay
|
|
16
|
+
autoSubmitDelay = UiGlobals.autoSubmitDelay,
|
|
17
|
+
onChangeText,
|
|
18
|
+
} = props,
|
|
19
|
+
value = _.isNil(props.value) ? '' : props.value, // null value may not actually reset this TextArea, so set it explicitly to empty string
|
|
14
20
|
styles = UiGlobals.styles,
|
|
15
|
-
|
|
21
|
+
debouncedSetValueRef = useRef(),
|
|
22
|
+
[localValue, setLocalValue] = useState(value),
|
|
23
|
+
onChangeTextLocal = (value) => {
|
|
24
|
+
if (value === '') {
|
|
25
|
+
value = null; // empty string makes value null
|
|
26
|
+
}
|
|
27
|
+
setLocalValue(value);
|
|
28
|
+
if (autoSubmit) {
|
|
29
|
+
debouncedSetValueRef.current(value);
|
|
30
|
+
}
|
|
31
|
+
if (onChangeText) {
|
|
32
|
+
onChangeText(value);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
// Set up debounce fn
|
|
38
|
+
// Have to do this because otherwise, lodash tries to create a debounced version of the fn from only this render
|
|
39
|
+
debouncedSetValueRef.current = _.debounce(setValue, autoSubmitDelay);
|
|
40
|
+
}, [setValue]);
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
|
|
44
|
+
// Make local value conform to externally changed value
|
|
45
|
+
setLocalValue(value);
|
|
46
|
+
|
|
47
|
+
}, [value]);
|
|
48
|
+
|
|
49
|
+
if (localValue === null || typeof localValue === 'undefined') {
|
|
50
|
+
localValue = ''; // If the value is null or undefined, don't let this be an uncontrolled input
|
|
51
|
+
}
|
|
52
|
+
|
|
16
53
|
return <TextArea
|
|
17
54
|
ref={props.outerRef}
|
|
18
|
-
onChangeText={
|
|
55
|
+
onChangeText={onChangeTextLocal}
|
|
19
56
|
flex={1}
|
|
20
57
|
bg={styles.FORM_TEXTAREA_BG}
|
|
21
58
|
_focus={{
|
|
@@ -24,7 +61,7 @@ const
|
|
|
24
61
|
fontSize={styles.FORM_TEXTAREA_FONTSIZE}
|
|
25
62
|
h={styles.FORM_TEXTAREA_HEIGHT}
|
|
26
63
|
{...props}
|
|
27
|
-
value={
|
|
64
|
+
value={localValue}
|
|
28
65
|
/>;
|
|
29
66
|
},
|
|
30
67
|
TextAreaField = withComponent(withValue(TextAreaElement));
|
|
@@ -193,6 +193,7 @@ function Form(props) {
|
|
|
193
193
|
_.each(columnsConfig, (config, ix) => {
|
|
194
194
|
let {
|
|
195
195
|
fieldName,
|
|
196
|
+
editField,
|
|
196
197
|
isEditable,
|
|
197
198
|
editor,
|
|
198
199
|
renderer,
|
|
@@ -211,9 +212,10 @@ function Form(props) {
|
|
|
211
212
|
<Text numberOfLines={1} ellipsizeMode="head">{renderedValue}</Text>
|
|
212
213
|
</Box>);
|
|
213
214
|
} else {
|
|
215
|
+
const fieldToEdit = editField || fieldName;
|
|
214
216
|
elements.push(<Controller
|
|
215
217
|
key={'controller-' + ix}
|
|
216
|
-
name={
|
|
218
|
+
name={fieldToEdit}
|
|
217
219
|
// rules={rules}
|
|
218
220
|
control={control}
|
|
219
221
|
render={(args) => {
|
|
@@ -236,8 +238,8 @@ function Form(props) {
|
|
|
236
238
|
} = fieldState;
|
|
237
239
|
let editorProps = {};
|
|
238
240
|
if (!editor) {
|
|
239
|
-
const propertyDef =
|
|
240
|
-
editor = propertyDef
|
|
241
|
+
const propertyDef = fieldToEdit && Repository?.getSchema().getPropertyDefinition(fieldToEdit);
|
|
242
|
+
editor = propertyDef?.editorType;
|
|
241
243
|
if (_.isPlainObject(editor)) {
|
|
242
244
|
const {
|
|
243
245
|
type,
|
|
@@ -248,6 +250,13 @@ function Form(props) {
|
|
|
248
250
|
editor = type;
|
|
249
251
|
}
|
|
250
252
|
}
|
|
253
|
+
if (!editor) {
|
|
254
|
+
editor = 'Text';
|
|
255
|
+
}
|
|
256
|
+
if (!editor.match(/Toggle/)) {
|
|
257
|
+
editorProps.h = '40px'; // Toggle height gets applied incorrectly; just skip it
|
|
258
|
+
}
|
|
259
|
+
|
|
251
260
|
const Element = getComponentFromType(editor);
|
|
252
261
|
|
|
253
262
|
if (useSelectorId) {
|
|
@@ -258,21 +267,25 @@ function Form(props) {
|
|
|
258
267
|
let element = <Element
|
|
259
268
|
name={name}
|
|
260
269
|
value={value}
|
|
261
|
-
|
|
270
|
+
onChangeValue={(newValue) => {
|
|
271
|
+
if (newValue === undefined) {
|
|
272
|
+
newValue = null; // React Hook Form doesn't respond well when setting value to undefined
|
|
273
|
+
}
|
|
262
274
|
onChange(newValue);
|
|
263
|
-
if (onEditorChange) {
|
|
275
|
+
if (typeof onEditorChange !== 'undefined' && onEditorChange) {
|
|
264
276
|
onEditorChange(newValue, formSetValue, formGetValues, formState);
|
|
265
277
|
}
|
|
266
278
|
}}
|
|
267
279
|
onBlur={onBlur}
|
|
268
280
|
flex={1}
|
|
269
|
-
{...editorProps}
|
|
270
281
|
parent={self}
|
|
271
|
-
reference={
|
|
272
|
-
|
|
273
|
-
// {...propsToPass}
|
|
282
|
+
reference={fieldToEdit}
|
|
283
|
+
{...editorProps}
|
|
274
284
|
/>;
|
|
275
285
|
|
|
286
|
+
if (editor.match(/Tag/)) {
|
|
287
|
+
columnProps.overflow = 'auto';
|
|
288
|
+
}
|
|
276
289
|
// element = <Tooltip key={ix} label={header} placement="bottom">
|
|
277
290
|
// {element}
|
|
278
291
|
// </Tooltip>;
|
|
@@ -516,22 +529,20 @@ function Form(props) {
|
|
|
516
529
|
{...editorTypeProps}
|
|
517
530
|
{...dynamicProps}
|
|
518
531
|
/>;
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
message = message.replace(error.ref.name, label);
|
|
525
|
-
}
|
|
532
|
+
let message = null;
|
|
533
|
+
if (error) {
|
|
534
|
+
message = error.message;
|
|
535
|
+
if (label && error.ref?.name) {
|
|
536
|
+
message = message.replace(error.ref.name, label);
|
|
526
537
|
}
|
|
527
|
-
if (message) {
|
|
528
|
-
message = <Text color="#f00">{message}</Text>;
|
|
529
|
-
}
|
|
530
|
-
element = <Column pt={1} flex={1}>
|
|
531
|
-
{element}
|
|
532
|
-
{message}
|
|
533
|
-
</Column>;
|
|
534
538
|
}
|
|
539
|
+
if (message) {
|
|
540
|
+
message = <Text color="#f00">{message}</Text>;
|
|
541
|
+
}
|
|
542
|
+
element = <Column pt={1} flex={1}>
|
|
543
|
+
{element}
|
|
544
|
+
{message}
|
|
545
|
+
</Column>;
|
|
535
546
|
|
|
536
547
|
if (item.additionalEditButtons) {
|
|
537
548
|
const buttons = buildAdditionalButtons(item.additionalEditButtons, self, { fieldState, formSetValue, formGetValues, formState });
|
|
@@ -830,9 +841,17 @@ function Form(props) {
|
|
|
830
841
|
}
|
|
831
842
|
|
|
832
843
|
if (editorType === EDITOR_TYPE__INLINE) {
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
footerProps.
|
|
844
|
+
footerProps.position = 'sticky';
|
|
845
|
+
footerProps.alignSelf = 'flex-start';
|
|
846
|
+
footerProps.justifyContent = 'center';
|
|
847
|
+
footerProps.top = '100px';
|
|
848
|
+
footerProps.left = '20px';
|
|
849
|
+
footerProps.width = '200px';
|
|
850
|
+
footerProps.bg = 'primary.100';
|
|
851
|
+
footerProps.p = 0;
|
|
852
|
+
footerProps.px = 4;
|
|
853
|
+
footerProps.py = 2;
|
|
854
|
+
footerProps.borderBottomRadius = 5;
|
|
836
855
|
}
|
|
837
856
|
|
|
838
857
|
if (onDelete && editorMode === EDITOR_MODE__EDIT && isSingle) {
|
|
@@ -868,11 +887,11 @@ function Form(props) {
|
|
|
868
887
|
}
|
|
869
888
|
}
|
|
870
889
|
|
|
871
|
-
return <Column {...sizeProps} onLayout={onLayoutDecorated} ref={formRef}>
|
|
890
|
+
return <Column {...sizeProps} onLayout={onLayoutDecorated} ref={formRef} testID="form">
|
|
872
891
|
{!!containerWidth && <>
|
|
873
892
|
{editorType === EDITOR_TYPE__INLINE &&
|
|
874
|
-
<
|
|
875
|
-
|
|
893
|
+
<Row
|
|
894
|
+
display="inline-block"
|
|
876
895
|
flex={1}
|
|
877
896
|
bg="#fff"
|
|
878
897
|
py={1}
|
|
@@ -880,14 +899,14 @@ function Form(props) {
|
|
|
880
899
|
borderBottomWidth={5}
|
|
881
900
|
borderTopColor="primary.100"
|
|
882
901
|
borderBottomColor="primary.100"
|
|
883
|
-
>{editor}</
|
|
902
|
+
>{editor}</Row>}
|
|
884
903
|
{editorType !== EDITOR_TYPE__INLINE &&
|
|
885
904
|
<ScrollView _web={{ minHeight, }} width="100%" pb={1}>
|
|
886
905
|
{formButtons}
|
|
887
906
|
{editor}
|
|
888
907
|
</ScrollView>}
|
|
889
908
|
|
|
890
|
-
<Footer justifyContent="flex-end" {...footerProps}
|
|
909
|
+
<Footer justifyContent="flex-end" {...footerProps} {...savingProps}>
|
|
891
910
|
{onDelete && editorMode === EDITOR_MODE__EDIT && isSingle &&
|
|
892
911
|
|
|
893
912
|
<Row flex={1} justifyContent="flex-start">
|
|
@@ -44,6 +44,7 @@ import withMultiSelection from '../Hoc/withMultiSelection.js';
|
|
|
44
44
|
import withSelection from '../Hoc/withSelection.js';
|
|
45
45
|
import withWindowedEditor from '../Hoc/withWindowedEditor.js';
|
|
46
46
|
import withInlineEditor from '../Hoc/withInlineEditor.js';
|
|
47
|
+
import withInlineSideEditor from '../Hoc/withInlineSideEditor.js';
|
|
47
48
|
import getSaved from '../../Functions/getSaved.js';
|
|
48
49
|
import setSaved from '../../Functions/setSaved.js';
|
|
49
50
|
import getIconButtonFromConfig from '../../Functions/getIconButtonFromConfig.js';
|
|
@@ -1155,4 +1156,29 @@ export const InlineGridEditor = withComponent(
|
|
|
1155
1156
|
)
|
|
1156
1157
|
);
|
|
1157
1158
|
|
|
1159
|
+
// export const InlineSideGridEditor = withComponent(
|
|
1160
|
+
// withAlert(
|
|
1161
|
+
// withEvents(
|
|
1162
|
+
// withData(
|
|
1163
|
+
// withDropTarget(
|
|
1164
|
+
// withMultiSelection(
|
|
1165
|
+
// withSelection(
|
|
1166
|
+
// withInlineSideEditor(
|
|
1167
|
+
// withFilters(
|
|
1168
|
+
// withPresetButtons(
|
|
1169
|
+
// withContextMenu(
|
|
1170
|
+
// GridComponent
|
|
1171
|
+
// )
|
|
1172
|
+
// ),
|
|
1173
|
+
// true // isGrid
|
|
1174
|
+
// )
|
|
1175
|
+
// )
|
|
1176
|
+
// )
|
|
1177
|
+
// )
|
|
1178
|
+
// )
|
|
1179
|
+
// )
|
|
1180
|
+
// )
|
|
1181
|
+
// )
|
|
1182
|
+
// );
|
|
1183
|
+
|
|
1158
1184
|
export default Grid;
|
|
@@ -31,10 +31,10 @@ function withAdditionalProps(WrappedComponent) {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
// NOTE: Effectivtly, the HOC composition is:
|
|
34
|
-
// withAdditionalProps(withEditor(
|
|
34
|
+
// withAdditionalProps(withEditor(withInlineEditor))
|
|
35
35
|
|
|
36
|
-
export default function withInlineEditor(WrappedComponent) {
|
|
37
|
-
|
|
36
|
+
export default function withInlineEditor(WrappedComponent, skipWrappers = false) {
|
|
37
|
+
const InlineEditor = (props) => {
|
|
38
38
|
const {
|
|
39
39
|
editorType,
|
|
40
40
|
isEditorShown = false,
|
|
@@ -82,11 +82,17 @@ export default function withInlineEditor(WrappedComponent) {
|
|
|
82
82
|
editorBounds = editor.parentElement.getBoundingClientRect(), // reference parentElement, because this doesn't change based on last moveEditor call
|
|
83
83
|
delta = editorBounds.top - rowBounds.top;
|
|
84
84
|
|
|
85
|
-
editorStyle.top = (-1 * delta)
|
|
85
|
+
editorStyle.top = (-1 * delta) + 'px';
|
|
86
86
|
};
|
|
87
|
+
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
if (maskRef.current) {
|
|
90
|
+
maskRef.current.focus();
|
|
91
|
+
}
|
|
92
|
+
}, [isEditorShown]);
|
|
87
93
|
|
|
88
94
|
if (isEditorShown && selection.length < 1) {
|
|
89
|
-
|
|
95
|
+
return null; // phantom record may have just been deleted
|
|
90
96
|
}
|
|
91
97
|
if (isEditorShown && selection.length !== 1) {
|
|
92
98
|
throw new Error('Can only edit one at a time with inline editor!');
|
|
@@ -100,57 +106,59 @@ export default function withInlineEditor(WrappedComponent) {
|
|
|
100
106
|
inlineEditor = <>
|
|
101
107
|
{isEditorShown && <Box
|
|
102
108
|
ref={maskRef}
|
|
109
|
+
testID="mask"
|
|
103
110
|
position="fixed"
|
|
104
111
|
w="100vw"
|
|
105
112
|
h="100vh"
|
|
106
113
|
top="0"
|
|
107
114
|
left="0"
|
|
108
115
|
bg="#000"
|
|
109
|
-
opacity={0.
|
|
116
|
+
opacity={0.3}
|
|
110
117
|
zIndex={0}
|
|
111
118
|
onClick={(e) => {
|
|
112
119
|
e.preventDefault();
|
|
113
120
|
e.stopPropagation();
|
|
114
121
|
onEditorCancel();
|
|
115
122
|
}}
|
|
123
|
+
tabIndex={-1}
|
|
124
|
+
onKeyDown={(e) => {
|
|
125
|
+
if (e.key === 'Escape') {
|
|
126
|
+
onEditorCancel();
|
|
127
|
+
}
|
|
128
|
+
}}
|
|
116
129
|
></Box>}
|
|
117
130
|
<Column
|
|
118
131
|
ref={inlineEditorRef}
|
|
119
132
|
position="absolute"
|
|
120
133
|
zIndex={10}
|
|
134
|
+
testID="inline-editor"
|
|
135
|
+
h={isEditorShown ? '100px' : 0}
|
|
136
|
+
minWidth="100%"
|
|
137
|
+
display="inline-block"
|
|
138
|
+
whiteSpace="nowrap"
|
|
121
139
|
>
|
|
122
|
-
{isEditorShown &&
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
py: 2,
|
|
145
|
-
}}
|
|
146
|
-
bg="#fff"
|
|
147
|
-
borderTopWidth={4}
|
|
148
|
-
borderTopColor={styles.GRID_INLINE_EDITOR_BORDER_COLOR}
|
|
149
|
-
borderBottomWidth={4}
|
|
150
|
-
borderBottomColor={styles.GRID_INLINE_EDITOR_BORDER_COLOR}
|
|
151
|
-
py={1}
|
|
152
|
-
px={0}
|
|
153
|
-
/>}
|
|
140
|
+
{isEditorShown &&
|
|
141
|
+
<Form
|
|
142
|
+
parent={self}
|
|
143
|
+
reference="form"
|
|
144
|
+
editorType={EDITOR_TYPE__INLINE}
|
|
145
|
+
editorStateRef={editorStateRef}
|
|
146
|
+
record={selection[0]}
|
|
147
|
+
Repository={Repository}
|
|
148
|
+
isMultiple={selection.length > 1}
|
|
149
|
+
isEditorViewOnly={isEditorViewOnly}
|
|
150
|
+
columnsConfig={localColumnsConfig}
|
|
151
|
+
onCancel={onEditorCancel}
|
|
152
|
+
onSave={onEditorSave}
|
|
153
|
+
onClose={onEditorClose}
|
|
154
|
+
bg="#fff"
|
|
155
|
+
borderTopWidth={4}
|
|
156
|
+
borderTopColor={styles.GRID_INLINE_EDITOR_BORDER_COLOR}
|
|
157
|
+
borderBottomWidth={4}
|
|
158
|
+
borderBottomColor={styles.GRID_INLINE_EDITOR_BORDER_COLOR}
|
|
159
|
+
py={1}
|
|
160
|
+
px={0}
|
|
161
|
+
/>}
|
|
154
162
|
</Column>
|
|
155
163
|
</>;
|
|
156
164
|
}
|
|
@@ -162,5 +170,9 @@ export default function withInlineEditor(WrappedComponent) {
|
|
|
162
170
|
inlineEditor={inlineEditor}
|
|
163
171
|
isInlineEditorShown={isEditorShown}
|
|
164
172
|
/>;
|
|
165
|
-
}
|
|
173
|
+
};
|
|
174
|
+
if (skipWrappers) {
|
|
175
|
+
return InlineEditor; // this is for InlineSideEditor, not yet implemented
|
|
176
|
+
}
|
|
177
|
+
return withAdditionalProps(withEditor(InlineEditor));
|
|
166
178
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
|
|
2
|
+
import withInlineEditor from './withInlineEditor.js';
|
|
3
|
+
import withSideEditor from './withSideEditor.js';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export default function withInlineSideEditor(WrappedComponent) {
|
|
7
|
+
throw Error('Not yet implemented');
|
|
8
|
+
return withSideEditor(withInlineEditor(WrappedComponent, true));
|
|
9
|
+
}
|
|
@@ -20,7 +20,7 @@ function withAdditionalProps(WrappedComponent) {
|
|
|
20
20
|
// withAdditionalProps(withEditor(withSideEditor))
|
|
21
21
|
|
|
22
22
|
export default function withSideEditor(WrappedComponent, isTree = false) {
|
|
23
|
-
|
|
23
|
+
const SideEditor = (props) => {
|
|
24
24
|
const {
|
|
25
25
|
Editor,
|
|
26
26
|
editorProps = {},
|
|
@@ -57,5 +57,6 @@ export default function withSideEditor(WrappedComponent, isTree = false) {
|
|
|
57
57
|
reference="editor"
|
|
58
58
|
/>}
|
|
59
59
|
/>;
|
|
60
|
-
}
|
|
60
|
+
};
|
|
61
|
+
return withAdditionalProps(withEditor(SideEditor));
|
|
61
62
|
}
|
|
@@ -42,7 +42,7 @@ function withAdditionalProps(WrappedComponent) {
|
|
|
42
42
|
// withAdditionalProps(withEditor(withWindowedEditor))
|
|
43
43
|
|
|
44
44
|
export default function withWindowedEditor(WrappedComponent, isTree = false) {
|
|
45
|
-
|
|
45
|
+
const WindowedEditor = (props) => {
|
|
46
46
|
const {
|
|
47
47
|
isEditorShown = false,
|
|
48
48
|
setIsEditorShown,
|
|
@@ -81,5 +81,6 @@ export default function withWindowedEditor(WrappedComponent, isTree = false) {
|
|
|
81
81
|
/>
|
|
82
82
|
</Modal>}
|
|
83
83
|
</>;
|
|
84
|
-
}
|
|
84
|
+
};
|
|
85
|
+
return withAdditionalProps(withEditor(WindowedEditor, isTree));
|
|
85
86
|
}
|
package/src/Constants/Editor.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export const EDITOR_TYPE__INLINE = 'EDITOR_TYPE__INLINE';
|
|
2
2
|
export const EDITOR_TYPE__WINDOWED = 'EDITOR_TYPE__WINDOWED';
|
|
3
3
|
export const EDITOR_TYPE__SIDE = 'EDITOR_TYPE__SIDE';
|
|
4
|
+
export const EDITOR_TYPE__INLINE_SIDE = 'EDITOR_TYPE__INLINE_SIDE';
|
|
4
5
|
export const EDITOR_TYPE__SMART = 'EDITOR_TYPE__SMART';
|
|
5
6
|
export const EDITOR_TYPE__PLAIN = 'EDITOR_TYPE__PLAIN';
|
|
6
7
|
export const EDITOR_MODE__VIEW = 'EDITOR_MODE__VIEW';
|