@onehat/ui 0.2.65 → 0.2.67

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.2.65",
3
+ "version": "0.2.67",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -14,6 +14,7 @@ export default function Editor(props) {
14
14
  onEditorCancel: onCancel,
15
15
  onEditorSave: onSave,
16
16
  onEditorClose: onClose,
17
+ onEditorDelete: onDelete,
17
18
  editorMode,
18
19
  setEditorMode,
19
20
 
@@ -37,11 +38,11 @@ export default function Editor(props) {
37
38
 
38
39
  if (Repository.isRemotePhantomMode && selection.length === 1 && editorMode === EDITOR_MODE__VIEW) {
39
40
  return <Viewer
41
+ {...props}
40
42
  record={selection[0]}
41
- Repository={Repository}
42
43
  onEditMode={isViewOnly ? null : onEditMode}
43
44
  onClose={onClose}
44
- {...props}
45
+ // onDelete={onDelete}
45
46
  />;
46
47
  }
47
48
 
@@ -49,11 +50,12 @@ export default function Editor(props) {
49
50
  // and only show in one column when it's not.
50
51
 
51
52
  return <Form
53
+ {...props}
52
54
  record={selection}
53
55
  onViewMode={onViewMode}
54
56
  onCancel={onCancel}
55
57
  onSave={onSave}
56
58
  onClose={onClose}
57
- {...props}
59
+ onDelete={onDelete}
58
60
  />;
59
61
  }
@@ -0,0 +1,105 @@
1
+ import {
2
+ Button,
3
+ Column,
4
+ Icon,
5
+ ScrollView,
6
+ Row,
7
+ Text,
8
+ } from 'native-base';
9
+ import UiGlobals from '../../UiGlobals.js';
10
+ import getComponentFromType from '../../Functions/getComponentFromType.js';
11
+ import Label from '../Form/Label.js';
12
+ import Pencil from '../Icons/Pencil.js';
13
+ import Footer from '../Layout/Footer.js';
14
+ import _ from 'lodash';
15
+
16
+ // This is a wrapper for the Viewer subcomponent passed to props,
17
+ // that adds buttons and a footer
18
+
19
+ export default function Viewer(props) {
20
+ const {
21
+ additionalViewButtons = [],
22
+ ancillaryItems = [],
23
+ record,
24
+ onEditMode,
25
+ onClose,
26
+ onDelete,
27
+ } = props,
28
+ styles = UiGlobals.styles,
29
+ buildAncillary = () => {
30
+ let components = [];
31
+ if (ancillaryItems.length) {
32
+ components = _.map(ancillaryItems, (item, ix) => {
33
+ let {
34
+ type,
35
+ title = null,
36
+ selectorId,
37
+ ...propsToPass
38
+ } = item;
39
+ const
40
+ Element = getComponentFromType(type),
41
+ element = <Element
42
+ selectorId={selectorId}
43
+ selectorSelected={selectorId ? record : selectorSelected}
44
+ flex={1}
45
+ {...propsToPass}
46
+ />;
47
+ if (title) {
48
+ title = <Text
49
+ fontSize={styles.VIEWER_ANCILLARY_FONTSIZE}
50
+ fontWeight="bold"
51
+ >{title}</Text>;
52
+ }
53
+ return <Column key={'ancillary-' + ix} px={2} pb={1}>{title}{element}</Column>;
54
+ });
55
+ }
56
+ return components;
57
+ };
58
+
59
+ return <Column flex={1} w="100%">
60
+ <ScrollView flex={1} w="100%">
61
+ <Column m={2}>
62
+ {onEditMode && <Row mb={4} justifyContent="flex-end">
63
+ <Button
64
+ key="editBtn"
65
+ onPress={onEditMode}
66
+ leftIcon={<Icon as={Pencil} color="#fff" size="sm" />}
67
+ color="#fff"
68
+ >To Edit</Button>
69
+ </Row>}
70
+
71
+ {!_.isEmpty(additionalViewButtons) &&
72
+ <Row p={2} alignItems="center" justifyContent="flex-end">
73
+ {additionalViewButtons}
74
+ </Row>}
75
+
76
+ {props.children}
77
+
78
+ {buildAncillary()}
79
+
80
+ </Column>
81
+ </ScrollView>
82
+ <Footer justifyContent="flex-end">
83
+ {onDelete && <Row flex={1} justifyContent="flex-start">
84
+ <Button
85
+ key="deleteBtn"
86
+ onPress={() => {
87
+ confirm('Are you sure you want to delete this record?', onDelete);
88
+ }}
89
+ bg="warning"
90
+ _hover={{
91
+ bg: 'warningHover',
92
+ }}
93
+ color="#fff"
94
+ >Delete</Button>
95
+ </Row>}
96
+ <Button.Group space={2}>
97
+ <Button
98
+ key="closeBtn"
99
+ onPress={onClose}
100
+ color="#fff"
101
+ >Close</Button>
102
+ </Button.Group>
103
+ </Footer>
104
+ </Column>;
105
+ }
@@ -22,6 +22,7 @@ import { useForm, Controller } from 'react-hook-form'; // https://react-hook-for
22
22
  import * as yup from 'yup'; // https://github.com/jquense/yup#string
23
23
  import { yupResolver } from '@hookform/resolvers/yup';
24
24
  import useForceUpdate from '../../Hooks/useForceUpdate.js';
25
+ import UiGlobals from '../../UiGlobals.js';
25
26
  import withAlert from '../Hoc/withAlert.js';
26
27
  import withEditor from '../Hoc/withEditor.js';
27
28
  import inArray from '../../Functions/inArray.js';
@@ -57,6 +58,7 @@ function Form(props) {
57
58
  editorType = EDITOR_TYPE__WINDOWED, // EDITOR_TYPE__INLINE | EDITOR_TYPE__WINDOWED | EDITOR_TYPE__SIDE | EDITOR_TYPE__SMART | EDITOR_TYPE__PLAIN
58
59
  startingValues = {},
59
60
  items = [], // Columns, FieldSets, Fields, etc to define the form
61
+ ancillaryItems = [], // additional items which are not controllable form elements, but should appear in the form
60
62
  columnDefaults = {}, // defaults for each Column defined in items (above)
61
63
  columnsConfig, // Which columns are shown in Grid, so the inline editor can match. Used only for EDITOR_TYPE__INLINE
62
64
  validator, // custom validator, mainly for EDITOR_TYPE__PLAIN
@@ -65,8 +67,7 @@ function Form(props) {
65
67
  onBack,
66
68
  onReset,
67
69
  onViewMode,
68
- additionalButtons = [],
69
- ancillaryComponents = [],
70
+ additionalEditButtons = [],
70
71
 
71
72
  // sizing of outer container
72
73
  h,
@@ -83,9 +84,9 @@ function Form(props) {
83
84
  isViewOnly = false,
84
85
  editorMode,
85
86
  onCancel,
86
- onEditorSave,
87
- onSave = onEditorSave,
87
+ onSave,
88
88
  onClose,
89
+ onDelete,
89
90
 
90
91
  // DataMgt
91
92
  selectorId,
@@ -95,6 +96,7 @@ function Form(props) {
95
96
  alert,
96
97
  confirm,
97
98
  } = props,
99
+ styles = UiGlobals.styles,
98
100
  record = props.record?.length === 1 ? props.record[0] : props.record,
99
101
  isMultiple = _.isArray(record),
100
102
  isSingle = !isMultiple, // for convenience
@@ -121,7 +123,7 @@ function Form(props) {
121
123
  mode: 'onChange', // onChange | onBlur | onSubmit | onTouched | all
122
124
  // reValidateMode: 'onChange', // onChange | onBlur | onSubmit
123
125
  defaultValues,
124
- // values: defaultValues, // NOTE: This will cause a looping re-render if not used carefully!
126
+ // values: defaultValues,
125
127
  // resetOptions: {
126
128
  // keepDirtyValues: false, // user-interacted input will be retained
127
129
  // keepErrors: false, // input errors will be retained with value update
@@ -239,9 +241,7 @@ function Form(props) {
239
241
  return <Row>{elements}</Row>;
240
242
  },
241
243
  buildFromItems = () => {
242
- const
243
- regularItems = _.map(items, (item, ix) => buildNextLayer(item, ix, columnDefaults));
244
- return [...regularItems, ...ancillaryComponents];
244
+ return _.map(items, (item, ix) => buildNextLayer(item, ix, columnDefaults));
245
245
  },
246
246
  buildNextLayer = (item, ix, defaults) => {
247
247
  let {
@@ -399,6 +399,35 @@ function Form(props) {
399
399
  }}
400
400
  />;
401
401
  },
402
+ buildAncillary = () => {
403
+ let components = [];
404
+ if (ancillaryItems.length) {
405
+ components = _.map(ancillaryItems, (item, ix) => {
406
+ let {
407
+ type,
408
+ title = null,
409
+ selectorId,
410
+ ...propsToPass
411
+ } = item;
412
+ const
413
+ Element = getComponentFromType(type),
414
+ element = <Element
415
+ selectorId={selectorId}
416
+ selectorSelected={selectorId ? record : selectorSelected}
417
+ flex={1}
418
+ {...propsToPass}
419
+ />;
420
+ if (title) {
421
+ title = <Text
422
+ fontSize={styles.FORM_ANCILLARY_TITLE_FONTSIZE}
423
+ fontWeight="bold"
424
+ >{title}</Text>;
425
+ }
426
+ return <Column key={'ancillary-' + ix} px={2} pb={1}>{title}{element}</Column>;
427
+ });
428
+ }
429
+ return components;
430
+ },
402
431
  onSubmitError = (errors, e) => {
403
432
  debugger;
404
433
  if (editorType === EDITOR_TYPE__INLINE) {
@@ -459,10 +488,12 @@ function Form(props) {
459
488
  borderBottomColor="primary.100"
460
489
  >{formComponents}</ScrollView>;
461
490
  } else {
462
- // for Windowed editor
491
+ // for all other editor types
463
492
  formComponents = buildFromItems();
493
+ const formAncillaryComponents = buildAncillary();
464
494
  editor = <ScrollView flex={1} width="100%" pb={1}>
465
- <Row flex={1}>{formComponents}</Row>
495
+ <Row>{formComponents}</Row>
496
+ <Column pt={4}>{formAncillaryComponents}</Column>
466
497
  </ScrollView>;
467
498
  }
468
499
 
@@ -478,6 +509,14 @@ function Form(props) {
478
509
  editorModeF = isMultiple ? 'Edit Multiple' : 'Edit';
479
510
  break;
480
511
  }
512
+
513
+ let isSaveDisabled = false;
514
+ if (!_.isEmpty(formState.errors)) {
515
+ isSaveDisabled = true;
516
+ }
517
+ if (_.isEmpty(formState.dirtyFields) && !record?.isRemotePhantom) {
518
+ isSaveDisabled = true;
519
+ }
481
520
 
482
521
  return <Column {...sizeProps} onLayout={onLayout}>
483
522
 
@@ -504,15 +543,29 @@ function Form(props) {
504
543
  color="#fff"
505
544
  >To View</Button>}
506
545
  </Row>
507
- {!_.isEmpty(additionalButtons) &&
546
+ {editorMode === EDITOR_MODE__EDIT && !_.isEmpty(additionalEditButtons) &&
508
547
  <Row p={2} alignItems="center" justifyContent="flex-end">
509
- {additionalButtons}
548
+ {additionalEditButtons}
510
549
  </Row>}
511
550
 
512
551
  {editor}
513
552
 
514
553
  <Footer justifyContent="flex-end" {...footerProps}>
554
+ {onDelete && <Row flex={1} justifyContent="flex-start">
555
+ <Button
556
+ key="deleteBtn"
557
+ onPress={() => {
558
+ confirm('Are you sure you want to delete this record?', onDelete);
559
+ }}
560
+ bg="warning"
561
+ _hover={{
562
+ bg: 'warningHover',
563
+ }}
564
+ color="#fff"
565
+ >Delete</Button>
566
+ </Row>}
515
567
  <Button.Group space={2} {...buttonGroupProps}>
568
+
516
569
  {!isViewOnly && <IconButton
517
570
  key="resetBtn"
518
571
  onPress={() => {
@@ -538,7 +591,7 @@ function Form(props) {
538
591
  {!isViewOnly && onSave && <Button
539
592
  key="saveBtn"
540
593
  onPress={(e) => handleSubmit(onSave, onSubmitError)(e)}
541
- isDisabled={!_.isEmpty(formState.errors) || (!isSingle && !record?.isPhantom && !_.isEmpty(formState.dirtyFields))}
594
+ isDisabled={isSaveDisabled}
542
595
  color="#fff"
543
596
  >{editorMode === EDITOR_MODE__ADD ? 'Add' : 'Save'}</Button>}
544
597
  {isViewOnly && onClose && <Button
@@ -85,6 +85,9 @@ export function Grid(props) {
85
85
  bottomToolbar = 'pagination',
86
86
  topToolbar = null,
87
87
  additionalToolbarButtons = [],
88
+ h,
89
+ flex,
90
+ bg,
88
91
 
89
92
  // withEditor
90
93
  onAdd,
@@ -784,10 +787,17 @@ export function Grid(props) {
784
787
  }
785
788
  }
786
789
 
790
+ const sizeProps = {};
791
+ if (!_.isNil(h)) {
792
+ sizeProps.h = h;
793
+ } else {
794
+ sizeProps.flex = flex ?? 1;
795
+ }
787
796
  return <Column
788
797
  {...testProps('Grid')}
789
- flex={1}
790
798
  w="100%"
799
+ bg={bg}
800
+ {...sizeProps}
791
801
  >
792
802
  {topToolbar}
793
803
 
@@ -23,7 +23,7 @@ export default function withEditor(WrappedComponent) {
23
23
  if (selection.length > 1) {
24
24
  return 'records?';
25
25
  }
26
- return 'record' + (selection[0].displayValue ? ' ' + selection[0].displayValue : '') + '?';
26
+ return 'record' + (selection[0].displayValue ? ' "' + selection[0].displayValue + '"' : '') + '?';
27
27
  },
28
28
  record,
29
29
 
@@ -45,10 +45,7 @@ export default function withEditor(WrappedComponent) {
45
45
  [isEditorShown, setIsEditorShown] = useState(false),
46
46
  [isEditorViewOnly, setIsEditorViewOnly] = useState(false),
47
47
  [lastSelection, setLastSelection] = useState(),
48
- addRecord = async () => {
49
- if (!userCanEdit || disableAdd) {
50
- return;
51
- }
48
+ onAdd = async () => {
52
49
  const defaultValues = Repository.getSchema().model.defaultValues;
53
50
  let addValues = _.clone(defaultValues);
54
51
 
@@ -75,30 +72,24 @@ export default function withEditor(WrappedComponent) {
75
72
  setEditorMode(EDITOR_MODE__ADD);
76
73
  setIsEditorShown(true);
77
74
  },
78
- editRecord = () => {
79
- if (!userCanEdit || disableEdit) {
80
- return;
81
- }
75
+ onEdit = () => {
82
76
  setIsEditorViewOnly(false);
83
77
  setEditorMode(EDITOR_MODE__EDIT);
84
78
  setIsEditorShown(true);
85
79
  },
86
- deleteRecord = (e) => {
87
- if (!userCanEdit || disableDelete) {
88
- return;
89
- }
80
+ onDelete = () => {
90
81
  const
91
82
  isSingle = selection.length === 1,
92
83
  isPhantom = selection[0] && selection[0].isPhantom;
93
84
 
94
85
  if (isSingle && isPhantom) {
95
- onDelete();
86
+ deleteRecord();
96
87
  } else {
97
88
  const identifier = getRecordIdentifier(selection);
98
- confirm('Are you sure you want to delete the ' + identifier, onDelete);
89
+ confirm('Are you sure you want to delete the ' + identifier, deleteRecord);
99
90
  }
100
91
  },
101
- onDelete = async () => {
92
+ deleteRecord = async () => {
102
93
  await Repository.delete(selection);
103
94
  if (!Repository.isAutoSave) {
104
95
  await Repository.save();
@@ -160,7 +151,7 @@ export default function withEditor(WrappedComponent) {
160
151
  isSingle = selection.length === 1,
161
152
  isPhantom = selection[0] && selection[0].isPhantom;
162
153
  if (isSingle && isPhantom) {
163
- await onDelete();
154
+ await deleteRecord();
164
155
  }
165
156
  setEditorMode(EDITOR_MODE__VIEW);
166
157
  setIsEditorShown(false);
@@ -168,6 +159,11 @@ export default function withEditor(WrappedComponent) {
168
159
  onEditorClose = () => {
169
160
  setIsEditorShown(false);
170
161
  },
162
+ onEditorDelete = async () => {
163
+ await deleteRecord();
164
+ setEditorMode(EDITOR_MODE__VIEW);
165
+ setIsEditorShown(false);
166
+ },
171
167
  calculateEditorMode = () => {
172
168
  let mode = EDITOR_MODE__VIEW;
173
169
  if (userCanEdit) {
@@ -208,13 +204,14 @@ export default function withEditor(WrappedComponent) {
208
204
  editorMode={editorMode}
209
205
  setEditorMode={setEditorMode}
210
206
  setIsEditorShown={setIsEditorShown}
211
- onAdd={addRecord}
212
- onEdit={editRecord}
213
- onDelete={deleteRecord}
207
+ onAdd={(!userCanEdit || disableAdd) ? null : onAdd}
208
+ onEdit={(!userCanEdit || disableEdit) ? null : onEdit}
209
+ onDelete={(!userCanEdit || disableDelete || (editorMode === EDITOR_MODE__ADD && (selection[0]?.isPhantom || currentRecord?.isPhantom))) ? null : onDelete}
214
210
  onView={viewRecord}
215
211
  onDuplicate={duplicateRecord}
216
212
  onEditorSave={onEditorSave}
217
213
  onEditorCancel={onEditorCancel}
214
+ onEditorDelete={(!userCanEdit || disableDelete || (editorMode === EDITOR_MODE__ADD && (selection[0]?.isPhantom || currentRecord?.isPhantom))) ? null : onEditorDelete}
218
215
  onEditorClose={onEditorClose}
219
216
  useEditor={useEditor}
220
217
  userCanEdit={userCanEdit}
@@ -7,6 +7,7 @@ const
7
7
 
8
8
  const defaults = {
9
9
  FILTER_LABEL_FONTSIZE: DEFAULT_FONTSIZE,
10
+ FORM_ANCILLARY_TITLE_FONTSIZE: 22,
10
11
  FORM_COLOR_READOUT_FONTSIZE: DEFAULT_FONTSIZE,
11
12
  FORM_COLOR_INPUT_BG: WHITE,
12
13
  FORM_COLOR_INPUT_FOCUS_BG: FOCUS,
@@ -99,6 +100,7 @@ const defaults = {
99
100
  TREE_TOOLBAR_ITEMS_COLOR: 'trueGray.800',
100
101
  TREE_TOOLBAR_ITEMS_DISABLED_COLOR: 'disabled',
101
102
  TREE_TOOLBAR_ITEMS_ICON_SIZE: 'sm',
103
+ VIEWER_ANCILLARY_FONTSIZE: 22,
102
104
  };
103
105
 
104
106
  export default defaults;
@@ -23,6 +23,7 @@ const ThemeOverrides = {
23
23
  vip: '#3a78bc',
24
24
  navBlue: '#3b82f6',
25
25
  warning: '#d92b2b',
26
+ warningHover: '#af1313',
26
27
  orange: '#de9000',
27
28
  greens: {
28
29
  100: '#4CAF50',