@onehat/ui 0.3.240 → 0.3.242

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.240",
3
+ "version": "0.3.242",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -173,129 +173,197 @@ function Form(props) {
173
173
  context: { isPhantom },
174
174
  }),
175
175
  buildFromColumnsConfig = () => {
176
- // For InlineEditor
176
+ // Only used in InlineEditor
177
177
  // Build the fields that match the current columnsConfig in the grid
178
178
  const
179
- model = Repository.getSchema().model,
180
179
  elements = [],
181
- columnProps = {
180
+ columnProps = { // props applied to every column
182
181
  justifyContent: 'center',
183
182
  alignItems: 'center',
184
183
  borderRightWidth: 1,
185
184
  borderRightColor: 'trueGray.200',
186
185
  px: 1,
186
+ minWidth: styles.INLINE_EDITOR_MIN_WIDTH,
187
187
  };
188
-
189
- if (editorType === EDITOR_TYPE__INLINE) {
190
- columnProps.minWidth = styles.INLINE_EDITOR_MIN_WIDTH;
191
- }
192
-
193
188
  _.each(columnsConfig, (config, ix) => {
194
189
  let {
195
190
  fieldName,
196
- isEditable,
197
- editor,
191
+ isEditable = false,
192
+ editor = null,
193
+ editField,
198
194
  renderer,
199
195
  w,
200
196
  flex,
197
+ onChange: onEditorChange,
201
198
  useSelectorId = false,
202
- } = config;
203
-
204
- if (!isEditable) {
205
- let renderedValue = renderer ? renderer(record) : record[fieldName];
206
- if (_.isBoolean(renderedValue)) {
207
- renderedValue = renderedValue.toString();
199
+ getDynamicProps,
200
+ getIsRequired,
201
+ ...propsToPass
202
+ } = config,
203
+ type,
204
+ editorTypeProps = {},
205
+ viewerTypeProps = {};
206
+
207
+ if (editField) {
208
+ // Sometimes, columns will be configured to display one field
209
+ // and edit a different field
210
+ fieldName = editField;
211
+ }
212
+ const propertyDef = fieldName && Repository?.getSchema().getPropertyDefinition(fieldName);
213
+ if (propertyDef?.isEditingDisabled && checkIsEditingDisabled) {
214
+ isEditable = false;
215
+ }
216
+ if (!_.isNil(editor)) {
217
+ // if editor is defined on column, use it
218
+ if (_.isString(editor)) {
219
+ type = editor;
220
+ } else if (_.isPlainObject(editor)) {
221
+ const {
222
+ type: t,
223
+ ...p
224
+ } = editor;
225
+ type = t;
226
+ editorTypeProps = p;
208
227
  }
209
- renderedValue += "\n(not editable)";
210
- elements.push(<Box key={ix} w={w} flex={flex} {...columnProps}>
211
- <Text numberOfLines={1} ellipsizeMode="head">{renderedValue}</Text>
212
- </Box>);
213
228
  } else {
214
- elements.push(<Controller
215
- key={'controller-' + ix}
216
- name={fieldName}
217
- // rules={rules}
218
- control={control}
219
- render={(args) => {
220
- const {
221
- field,
222
- fieldState,
223
- // formState,
224
- } = args,
225
- {
226
- onChange,
227
- onBlur,
228
- name,
229
- value,
230
- // ref,
231
- } = field,
232
- {
233
- isTouched,
234
- isDirty,
235
- error,
236
- } = fieldState;
237
- let editorProps = {};
238
- if (!editor) {
239
- const propertyDef = fieldName && Repository?.getSchema().getPropertyDefinition(fieldName);
240
- editor = propertyDef?.editorType;
241
- if (_.isPlainObject(editor)) {
242
- const {
243
- type,
244
- onChange: onEditorChange,
245
- ...p
246
- } = editor;
247
- editorProps = p;
248
- editor = type;
249
- }
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
- }
229
+ // editor is not defined, fall back to property definition
230
+ if (isEditable) {
231
+ const
232
+ {
233
+ type: t,
234
+ ...p
235
+ } = propertyDef?.editorType;
236
+ type = t;
237
+ editorTypeProps = p;
238
+ } else if (propertyDef?.viewerType) {
239
+ const
240
+ {
241
+ type: t,
242
+ ...p
243
+ } = propertyDef?.viewerType;
244
+ type = t;
245
+ viewerTypeProps = p;
246
+ } else {
247
+ type = 'Text';
248
+ }
249
+ }
250
+ const isCombo = type?.match && type.match(/Combo/);
251
+ if (config.hasOwnProperty('autoLoad')) {
252
+ editorTypeProps.autoLoad = config.autoLoad;
253
+ } else {
254
+ if (isCombo && Repository?.isRemote && !Repository?.isLoaded) {
255
+ editorTypeProps.autoLoad = true;
256
+ }
257
+ }
258
+ if (isCombo) {
259
+ // editorTypeProps.showEyeButton = true;
260
+ if (_.isNil(propsToPass.showXButton)) {
261
+ editorTypeProps.showXButton = true;
262
+ }
263
+ }
264
+ const Element = getComponentFromType(type);
257
265
 
258
- const Element = getComponentFromType(editor);
266
+ if (isEditorViewOnly || !isEditable) {
267
+ let value = null;
268
+ if (renderer) {
269
+ value = renderer(record);
270
+ } else {
271
+ if (record?.properties && record.properties[fieldName]) {
272
+ value = record.properties[fieldName].displayValue;
273
+ }
274
+ if (_.isNil(value) && record && record[fieldName]) {
275
+ value = record[fieldName];
276
+ }
277
+ if (_.isNil(value) && startingValues && startingValues[fieldName]) {
278
+ value = startingValues[fieldName];
279
+ }
280
+ }
259
281
 
260
- if (useSelectorId) {
261
- editorProps.selectorId = selectorId;
262
- editorProps.selectorSelected = editorProps;
263
- }
264
-
265
- let element = <Element
266
- name={name}
267
- value={value}
268
- onChangeValue={(newValue) => {
269
- if (newValue === undefined) {
270
- newValue = null; // React Hook Form doesn't respond well when setting value to undefined
271
- }
272
- onChange(newValue);
273
- if (typeof onEditorChange !== 'undefined' && onEditorChange) {
274
- onEditorChange(newValue, formSetValue, formGetValues, formState);
275
- }
276
- }}
277
- onBlur={onBlur}
278
- flex={1}
279
- parent={self}
280
- reference={fieldName}
281
- {...editorProps}
282
- />;
283
-
284
- // element = <Tooltip key={ix} label={header} placement="bottom">
285
- // {element}
286
- // </Tooltip>;
287
- // if (error) {
288
- // element = <Column pt={1} flex={1}>
289
- // {element}
290
- // <Text color="#f00">{error.message}</Text>
291
- // </Column>;
292
- // }
293
-
294
- const dirtyIcon = isDirty && !disableDirtyIcon ? <Icon as={Pencil} size="2xs" color="trueGray.300" position="absolute" top="2px" left="2px" /> : null;
295
- return <Row key={ix} bg={error ? '#fdd' : '#fff'} w={w} flex={flex} {...columnProps}>{dirtyIcon}{element}</Row>;
296
- }}
297
- />);
282
+ let element = <Element
283
+ value={value}
284
+ parent={self}
285
+ reference={fieldName}
286
+ {...propsToPass}
287
+ {...viewerTypeProps}
288
+ />;
289
+ elements.push(<Box key={ix} w={w} flex={flex} {...columnProps}>{element}</Box>);
290
+ return;
298
291
  }
292
+
293
+ elements.push(<Controller
294
+ key={'controller-' + ix}
295
+ name={fieldName}
296
+ // rules={rules}
297
+ control={control}
298
+ render={(args) => {
299
+ const {
300
+ field,
301
+ fieldState,
302
+ // formState,
303
+ } = args,
304
+ {
305
+ onChange,
306
+ onBlur,
307
+ name,
308
+ value,
309
+ // ref,
310
+ } = field,
311
+ {
312
+ isTouched,
313
+ isDirty,
314
+ error,
315
+ } = fieldState;
316
+
317
+ if (isValidElement(Element)) {
318
+ throw new Error('Should not yet be valid React element. Did you use <Element> instead of () => <Element> when defining it?')
319
+ }
320
+
321
+ if (useSelectorId) { // This causes the whole form to use selectorId
322
+ editorTypeProps.selectorId = selectorId;
323
+ }
324
+ if (propsToPass.selectorId || editorTypeProps.selectorId) { // editorTypeProps.selectorId causes just this one field to use selectorId
325
+ if (_.isNil(propsToPass.selectorSelected)) {
326
+ editorTypeProps.selectorSelected = record;
327
+ }
328
+ }
329
+ let dynamicProps = {};
330
+ if (getDynamicProps) {
331
+ dynamicProps = getDynamicProps({ fieldState, formSetValue, formGetValues, formState });
332
+ }
333
+
334
+ if (type.match(/Tag/)) {
335
+ editorTypeProps.overflow = 'auto';
336
+ }
337
+ if (!type.match(/Toggle/)) {
338
+ editorTypeProps.h = '40px';
339
+ }
340
+
341
+ let element = <Element
342
+ name={name}
343
+ value={value}
344
+ onChangeValue={(newValue) => {
345
+ if (newValue === undefined) {
346
+ newValue = null; // React Hook Form doesn't respond well when setting value to undefined
347
+ }
348
+ onChange(newValue);
349
+ if (onEditorChange) {
350
+ onEditorChange(newValue, formSetValue, formGetValues, formState, trigger);
351
+ }
352
+ }}
353
+ onBlur={onBlur}
354
+ flex={1}
355
+ parent={self}
356
+ reference={name}
357
+ {...propsToPass}
358
+ {...editorTypeProps}
359
+ {...dynamicProps}
360
+ />;
361
+
362
+ const dirtyIcon = isDirty && !disableDirtyIcon ? <Icon as={Pencil} size="2xs" color="trueGray.300" position="absolute" top="2px" left="2px" /> : null;
363
+ return <Row key={ix} bg={error ? '#fdd' : '#fff'} w={w} flex={flex} {...columnProps}>{dirtyIcon}{element}</Row>;
364
+ }}
365
+ />);
366
+
299
367
  });
300
368
  return <Row>{elements}</Row>;
301
369
  },
@@ -323,7 +391,8 @@ function Form(props) {
323
391
  getIsRequired,
324
392
  ...propsToPass
325
393
  } = item,
326
- editorTypeProps = {};
394
+ editorTypeProps = {},
395
+ viewerTypeProps = {};
327
396
 
328
397
  if (isHidden) {
329
398
  return null;
@@ -354,6 +423,7 @@ function Form(props) {
354
423
  ...p
355
424
  } = propertyDef?.viewerType;
356
425
  type = t;
426
+ viewerTypeProps = p;
357
427
  } else {
358
428
  type = 'Text';
359
429
  }
@@ -373,9 +443,9 @@ function Form(props) {
373
443
  }
374
444
  }
375
445
  const Element = getComponentFromType(type);
376
- let children;
377
446
 
378
447
  if (inArray(type, ['Column', 'Row', 'FieldSet'])) {
448
+ let children;
379
449
  if (_.isEmpty(items)) {
380
450
  return null;
381
451
  }
@@ -427,6 +497,7 @@ function Form(props) {
427
497
  parent={self}
428
498
  reference={name}
429
499
  {...propsToPass}
500
+ {...viewerTypeProps}
430
501
  />;
431
502
  if (!disableLabels && label) {
432
503
  const labelProps = {};
@@ -487,6 +558,7 @@ function Form(props) {
487
558
  isDirty,
488
559
  error,
489
560
  } = fieldState;
561
+
490
562
  if (isValidElement(Element)) {
491
563
  throw new Error('Should not yet be valid React element. Did you use <Element> instead of () => <Element> when defining it?')
492
564
  }
@@ -901,7 +973,7 @@ function Form(props) {
901
973
  {editor}
902
974
  </ScrollView>}
903
975
 
904
- <Footer justifyContent="flex-end" {...footerProps} {...savingProps}>
976
+ <Footer justifyContent="flex-end" {...footerProps} {...savingProps}>
905
977
  {onDelete && editorMode === EDITOR_MODE__EDIT && isSingle &&
906
978
 
907
979
  <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';
@@ -886,6 +887,7 @@ function GridComponent(props) {
886
887
 
887
888
  if (self) {
888
889
  self.ref = containerRef;
890
+ self.gridRef = gridRef;
889
891
  }
890
892
 
891
893
  isAddingRef.current = isAdding;
@@ -1155,4 +1157,29 @@ export const InlineGridEditor = withComponent(
1155
1157
  )
1156
1158
  );
1157
1159
 
1160
+ // export const InlineSideGridEditor = withComponent(
1161
+ // withAlert(
1162
+ // withEvents(
1163
+ // withData(
1164
+ // withDropTarget(
1165
+ // withMultiSelection(
1166
+ // withSelection(
1167
+ // withInlineSideEditor(
1168
+ // withFilters(
1169
+ // withPresetButtons(
1170
+ // withContextMenu(
1171
+ // GridComponent
1172
+ // )
1173
+ // ),
1174
+ // true // isGrid
1175
+ // )
1176
+ // )
1177
+ // )
1178
+ // )
1179
+ // )
1180
+ // )
1181
+ // )
1182
+ // )
1183
+ // );
1184
+
1158
1185
  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(withWindowedEditor))
34
+ // withAdditionalProps(withEditor(withInlineEditor))
35
35
 
36
- export default function withInlineEditor(WrappedComponent) {
37
- return withAdditionalProps(withEditor((props) => {
36
+ export default function withInlineEditor(WrappedComponent, skipWrappers = false) {
37
+ const InlineEditor = (props) => {
38
38
  const {
39
39
  editorType,
40
40
  isEditorShown = false,
@@ -62,13 +62,6 @@ export default function withInlineEditor(WrappedComponent) {
62
62
  onChangeColumnsConfig = (columnsConfig) => {
63
63
  setLocalColumnsConfig(columnsConfig);
64
64
  },
65
- onRowClick = (item, rowIndex, e) => {
66
- // move the editor up to the appropriate row
67
- const currentRow = e.currentTarget;
68
- moveEditor(currentRow);
69
-
70
- setCurrentRow(currentRow);
71
- },
72
65
  onScreenResize = () => {
73
66
  // TODO: Attach a div with zIndex 0 to body to monitor resize events. THis is handler
74
67
 
@@ -83,10 +76,32 @@ export default function withInlineEditor(WrappedComponent) {
83
76
  delta = editorBounds.top - rowBounds.top;
84
77
 
85
78
  editorStyle.top = (-1 * delta) + 'px';
79
+ },
80
+ onEditorShown = () => {
81
+ // determine which row to move the editor to
82
+ const
83
+ data = self.gridRef.current.props.data, // This is okay, because (for now) the inlineEditor is only for use with grids
84
+ ix = data.indexOf(selection[0]),
85
+ gridRowsContainer = self.gridRef.current._listRef._scrollRef.childNodes[0],
86
+ currentRow = gridRowsContainer.childNodes[ix];
87
+
88
+ // TODO: verify it works if not using a Repository
89
+
90
+ moveEditor(currentRow);
91
+ setCurrentRow(currentRow);
86
92
  };
93
+
94
+ useEffect(() => {
95
+ if (maskRef.current) {
96
+ maskRef.current.focus();
97
+ }
98
+ if (isEditorShown) {
99
+ onEditorShown();
100
+ }
101
+ }, [isEditorShown]);
87
102
 
88
103
  if (isEditorShown && selection.length < 1) {
89
- throw new Error('Lost the selection!');
104
+ return null; // phantom record may have just been deleted
90
105
  }
91
106
  if (isEditorShown && selection.length !== 1) {
92
107
  throw new Error('Can only edit one at a time with inline editor!');
@@ -114,6 +129,12 @@ export default function withInlineEditor(WrappedComponent) {
114
129
  e.stopPropagation();
115
130
  onEditorCancel();
116
131
  }}
132
+ tabIndex={-1}
133
+ onKeyDown={(e) => {
134
+ if (e.key === 'Escape') {
135
+ onEditorCancel();
136
+ }
137
+ }}
117
138
  ></Box>}
118
139
  <Column
119
140
  ref={inlineEditorRef}
@@ -158,5 +179,9 @@ export default function withInlineEditor(WrappedComponent) {
158
179
  inlineEditor={inlineEditor}
159
180
  isInlineEditorShown={isEditorShown}
160
181
  />;
161
- }));
182
+ };
183
+ if (skipWrappers) {
184
+ return InlineEditor; // this is for InlineSideEditor, not yet implemented
185
+ }
186
+ return withAdditionalProps(withEditor(InlineEditor));
162
187
  }
@@ -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
- return withAdditionalProps(withEditor((props) => {
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
- return withAdditionalProps(withEditor((props) => {
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
- }, isTree));
84
+ };
85
+ return withAdditionalProps(withEditor(WindowedEditor, isTree));
85
86
  }
@@ -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';