@onehat/ui 0.3.238 → 0.3.240

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@onehat/ui",
3
- "version": "0.3.238",
3
+ "version": "0.3.240",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -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
- const
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
- value = _.isNil(props.value) ? '' : props.value; // null value may not actually reset this TextArea, so set it explicitly to empty string
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={props.setValue}
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={value}
64
+ value={localValue}
28
65
  />;
29
66
  },
30
67
  TextAreaField = withComponent(withValue(TextAreaElement));
@@ -237,7 +237,7 @@ function Form(props) {
237
237
  let editorProps = {};
238
238
  if (!editor) {
239
239
  const propertyDef = fieldName && Repository?.getSchema().getPropertyDefinition(fieldName);
240
- editor = propertyDef && propertyDef[fieldName].editorType;
240
+ editor = propertyDef?.editorType;
241
241
  if (_.isPlainObject(editor)) {
242
242
  const {
243
243
  type,
@@ -248,6 +248,13 @@ function Form(props) {
248
248
  editor = type;
249
249
  }
250
250
  }
251
+ if (!editor) {
252
+ editor = 'Text';
253
+ }
254
+ if (!editor.match(/Toggle/)) {
255
+ editorProps.h = '40px'; // Toggle height gets applied incorrectly; just skip it
256
+ }
257
+
251
258
  const Element = getComponentFromType(editor);
252
259
 
253
260
  if (useSelectorId) {
@@ -258,19 +265,20 @@ function Form(props) {
258
265
  let element = <Element
259
266
  name={name}
260
267
  value={value}
261
- setValue={(newValue) => {
268
+ onChangeValue={(newValue) => {
269
+ if (newValue === undefined) {
270
+ newValue = null; // React Hook Form doesn't respond well when setting value to undefined
271
+ }
262
272
  onChange(newValue);
263
- if (onEditorChange) {
273
+ if (typeof onEditorChange !== 'undefined' && onEditorChange) {
264
274
  onEditorChange(newValue, formSetValue, formGetValues, formState);
265
275
  }
266
276
  }}
267
277
  onBlur={onBlur}
268
278
  flex={1}
269
- {...editorProps}
270
279
  parent={self}
271
280
  reference={fieldName}
272
- // {...defaults}
273
- // {...propsToPass}
281
+ {...editorProps}
274
282
  />;
275
283
 
276
284
  // element = <Tooltip key={ix} label={header} placement="bottom">
@@ -516,22 +524,20 @@ function Form(props) {
516
524
  {...editorTypeProps}
517
525
  {...dynamicProps}
518
526
  />;
519
- if (editorType !== EDITOR_TYPE__INLINE) {
520
- let message = null;
521
- if (error) {
522
- message = error.message;
523
- if (label && error.ref?.name) {
524
- message = message.replace(error.ref.name, label);
525
- }
527
+ let message = null;
528
+ if (error) {
529
+ message = error.message;
530
+ if (label && error.ref?.name) {
531
+ message = message.replace(error.ref.name, label);
526
532
  }
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
533
  }
534
+ if (message) {
535
+ message = <Text color="#f00">{message}</Text>;
536
+ }
537
+ element = <Column pt={1} flex={1}>
538
+ {element}
539
+ {message}
540
+ </Column>;
535
541
 
536
542
  if (item.additionalEditButtons) {
537
543
  const buttons = buildAdditionalButtons(item.additionalEditButtons, self, { fieldState, formSetValue, formGetValues, formState });
@@ -830,9 +836,17 @@ function Form(props) {
830
836
  }
831
837
 
832
838
  if (editorType === EDITOR_TYPE__INLINE) {
833
- buttonGroupProps.position = 'fixed';
834
- buttonGroupProps.left = 10; // TODO: I would prefer to have this be centered, but it's a lot more complex than just making it stick to the left
835
- footerProps.alignItems = 'flex-start';
839
+ footerProps.position = 'sticky';
840
+ footerProps.alignSelf = 'flex-start';
841
+ footerProps.justifyContent = 'center';
842
+ footerProps.top = '100px';
843
+ footerProps.left = '20px';
844
+ footerProps.width = '200px';
845
+ footerProps.bg = 'primary.100';
846
+ footerProps.p = 0;
847
+ footerProps.px = 4;
848
+ footerProps.py = 2;
849
+ footerProps.borderBottomRadius = 5;
836
850
  }
837
851
 
838
852
  if (onDelete && editorMode === EDITOR_MODE__EDIT && isSingle) {
@@ -868,11 +882,11 @@ function Form(props) {
868
882
  }
869
883
  }
870
884
 
871
- return <Column {...sizeProps} onLayout={onLayoutDecorated} ref={formRef}>
885
+ return <Column {...sizeProps} onLayout={onLayoutDecorated} ref={formRef} testID="form">
872
886
  {!!containerWidth && <>
873
887
  {editorType === EDITOR_TYPE__INLINE &&
874
- <ScrollView
875
- horizontal={true}
888
+ <Row
889
+ display="inline-block"
876
890
  flex={1}
877
891
  bg="#fff"
878
892
  py={1}
@@ -880,7 +894,7 @@ function Form(props) {
880
894
  borderBottomWidth={5}
881
895
  borderTopColor="primary.100"
882
896
  borderBottomColor="primary.100"
883
- >{editor}</ScrollView>}
897
+ >{editor}</Row>}
884
898
  {editorType !== EDITOR_TYPE__INLINE &&
885
899
  <ScrollView _web={{ minHeight, }} width="100%" pb={1}>
886
900
  {formButtons}
@@ -82,7 +82,7 @@ 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) -20 + 'px';
85
+ editorStyle.top = (-1 * delta) + 'px';
86
86
  };
87
87
 
88
88
  if (isEditorShown && selection.length < 1) {
@@ -100,13 +100,14 @@ export default function withInlineEditor(WrappedComponent) {
100
100
  inlineEditor = <>
101
101
  {isEditorShown && <Box
102
102
  ref={maskRef}
103
+ testID="mask"
103
104
  position="fixed"
104
105
  w="100vw"
105
106
  h="100vh"
106
107
  top="0"
107
108
  left="0"
108
109
  bg="#000"
109
- opacity={0.35}
110
+ opacity={0.3}
110
111
  zIndex={0}
111
112
  onClick={(e) => {
112
113
  e.preventDefault();
@@ -118,39 +119,34 @@ export default function withInlineEditor(WrappedComponent) {
118
119
  ref={inlineEditorRef}
119
120
  position="absolute"
120
121
  zIndex={10}
122
+ testID="inline-editor"
123
+ h={isEditorShown ? '100px' : 0}
124
+ minWidth="100%"
125
+ display="inline-block"
126
+ whiteSpace="nowrap"
121
127
  >
122
- {isEditorShown && <Form
123
- parent={self}
124
- reference="form"
125
- editorType={EDITOR_TYPE__INLINE}
126
- editorStateRef={editorStateRef}
127
- record={selection[0]}
128
- Repository={Repository}
129
- isMultiple={selection.length > 1}
130
- isEditorViewOnly={isEditorViewOnly}
131
- columnsConfig={localColumnsConfig}
132
- onCancel={onEditorCancel}
133
- onSave={onEditorSave}
134
- onClose={onEditorClose}
135
- footerProps={{
136
- justifyContent: 'center',
137
- bg: null, // make bg transparent
138
- p: 0,
139
- }}
140
- buttonGroupProps={{
141
- bg: 'primary.100',
142
- borderBottomRadius: 5,
143
- px: 4,
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
- />}
128
+ {isEditorShown &&
129
+ <Form
130
+ parent={self}
131
+ reference="form"
132
+ editorType={EDITOR_TYPE__INLINE}
133
+ editorStateRef={editorStateRef}
134
+ record={selection[0]}
135
+ Repository={Repository}
136
+ isMultiple={selection.length > 1}
137
+ isEditorViewOnly={isEditorViewOnly}
138
+ columnsConfig={localColumnsConfig}
139
+ onCancel={onEditorCancel}
140
+ onSave={onEditorSave}
141
+ onClose={onEditorClose}
142
+ bg="#fff"
143
+ borderTopWidth={4}
144
+ borderTopColor={styles.GRID_INLINE_EDITOR_BORDER_COLOR}
145
+ borderBottomWidth={4}
146
+ borderBottomColor={styles.GRID_INLINE_EDITOR_BORDER_COLOR}
147
+ py={1}
148
+ px={0}
149
+ />}
154
150
  </Column>
155
151
  </>;
156
152
  }