@onehat/ui 0.2.77 → 0.2.79

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.77",
3
+ "version": "0.2.79",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -1,7 +1,5 @@
1
1
  import {
2
2
  EDITOR_MODE__VIEW,
3
- EDITOR_MODE__ADD,
4
- EDITOR_MODE__EDIT,
5
3
  } from '../../Constants/Editor.js';
6
4
  import _ from 'lodash';
7
5
 
@@ -16,7 +14,7 @@ export default function Editor(props) {
16
14
  onEditorClose: onClose,
17
15
  onEditorDelete: onDelete,
18
16
  editorMode,
19
- setEditorMode,
17
+ onEditMode,
20
18
 
21
19
  // withData
22
20
  Repository,
@@ -24,13 +22,7 @@ export default function Editor(props) {
24
22
  // withSelection
25
23
  selection,
26
24
 
27
- } = props,
28
- onEditMode = () => {
29
- setEditorMode(EDITOR_MODE__EDIT);
30
- },
31
- onViewMode = () => {
32
- setEditorMode(EDITOR_MODE__VIEW);
33
- };
25
+ } = props;
34
26
 
35
27
  if (_.isEmpty(selection)) {
36
28
  return null;
@@ -42,7 +34,7 @@ export default function Editor(props) {
42
34
  record={selection[0]}
43
35
  onEditMode={isViewOnly ? null : onEditMode}
44
36
  onClose={onClose}
45
- // onDelete={onDelete}
37
+ onDelete={onDelete}
46
38
  />;
47
39
  }
48
40
 
@@ -52,7 +44,6 @@ export default function Editor(props) {
52
44
  return <Form
53
45
  {...props}
54
46
  record={selection}
55
- onViewMode={onViewMode}
56
47
  onCancel={onCancel}
57
48
  onSave={onSave}
58
49
  onClose={onClose}
@@ -20,7 +20,12 @@ export default function Viewer(props) {
20
20
  const {
21
21
  additionalViewButtons = [],
22
22
  ancillaryItems = [],
23
+ viewerCanDelete = false,
24
+
25
+ // withData
23
26
  record,
27
+
28
+ // withEditor
24
29
  onEditMode,
25
30
  onClose,
26
31
  onDelete,
@@ -80,19 +85,18 @@ export default function Viewer(props) {
80
85
  </Column>
81
86
  </ScrollView>
82
87
  <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>}
88
+ {onDelete && viewerCanDelete &&
89
+ <Row flex={1} justifyContent="flex-start">
90
+ <Button
91
+ key="deleteBtn"
92
+ onPress={onDelete}
93
+ bg="warning"
94
+ _hover={{
95
+ bg: 'warningHover',
96
+ }}
97
+ color="#fff"
98
+ >Delete</Button>
99
+ </Row>}
96
100
  <Button.Group space={2}>
97
101
  <Button
98
102
  key="closeBtn"
@@ -12,12 +12,15 @@ import {
12
12
  } from '../../../../Constants/UiModes.js';
13
13
  import UiGlobals from '../../../../UiGlobals.js';
14
14
  import Input from '../Input.js';
15
+ import withAlert from '../../../Hoc/withAlert.js';
15
16
  import withData from '../../../Hoc/withData.js';
16
17
  import withEvents from '../../../Hoc/withEvents.js';
18
+ import withPresetButtons from '../../../Hoc/withPresetButtons.js';
17
19
  import withSelection from '../../../Hoc/withSelection.js';
18
20
  import withValue from '../../../Hoc/withValue.js';
21
+ import withWindowedEditor from '../../../Hoc/withWindowedEditor.js';
19
22
  import emptyFn from '../../../../Functions/emptyFn.js';
20
- import { Grid } from '../../../Grid/Grid.js';
23
+ import { Grid, WindowedGridEditor } from '../../../Grid/Grid.js';
21
24
  import IconButton from '../../../Buttons/IconButton.js';
22
25
  import CaretDown from '../../../Icons/CaretDown.js';
23
26
  import _ from 'lodash';
@@ -26,7 +29,7 @@ import _ from 'lodash';
26
29
  // The default export is *with* the HOC. A separate *raw* component is
27
30
  // exported which can be combined with many HOCs for various functionality.
28
31
 
29
- export function Combo(props) {
32
+ export function ComboComponent(props) {
30
33
  const {
31
34
  additionalButtons,
32
35
  autoFocus = false,
@@ -36,7 +39,9 @@ export function Combo(props) {
36
39
  menuMinWidth = 150,
37
40
  disableDirectEntry = false,
38
41
  disablePagination = true,
42
+ hideMenuOnSelection = true,
39
43
  _input = {},
44
+ isEditor = false,
40
45
 
41
46
  // withValue
42
47
  value,
@@ -341,6 +346,8 @@ export function Combo(props) {
341
346
  if (tooltipRef) {
342
347
  refProps.ref = tooltipRef;
343
348
  }
349
+
350
+ const WhichGrid = isEditor ? WindowedGridEditor : Grid;
344
351
 
345
352
  let comboComponent = <Row {...refProps} justifyContent="center" alignItems="center" h={styles.FORM_COMBO_HEIGHT} flex={1} onLayout={() => setIsRendered(true)}>
346
353
  {disableDirectEntry ?
@@ -474,7 +481,7 @@ export function Combo(props) {
474
481
  borderTopWidth={0}
475
482
  p={0}
476
483
  >
477
- <Grid
484
+ <WhichGrid
478
485
  showHeaders={false}
479
486
  showHovers={true}
480
487
  shadow={1}
@@ -489,13 +496,16 @@ export function Combo(props) {
489
496
  };
490
497
  }}
491
498
  {...props}
499
+ disablePresetButtons={!isEditor}
492
500
  disablePagination={disablePagination}
493
501
  fireEvent={onEvent}
494
502
  setSelection={(selection) => {
495
503
  // Decorator fn to add local functionality
496
504
  // Close the menu when row is selected on grid
497
505
  setSelection(selection);
498
- hideMenu();
506
+ if (hideMenuOnSelection) {
507
+ hideMenu();
508
+ }
499
509
  }}
500
510
  selectionMode={selectionMode}
501
511
  setValue={(value) => {
@@ -515,13 +525,30 @@ export function Combo(props) {
515
525
  return comboComponent;
516
526
  }
517
527
 
518
- export default
519
- // withEvents(
520
- withData(
528
+ export const Combo = withData(
521
529
  withValue(
522
530
  withSelection(
523
- Combo
531
+ ComboComponent
524
532
  )
525
533
  )
526
534
  );
527
- // );
535
+
536
+
537
+
538
+ function withAdditionalProps(WrappedComponent) {
539
+ return (props) => {
540
+ return <WrappedComponent
541
+ isEditor={true}
542
+ hideMenuOnSelection={false}
543
+ disableView={true}
544
+ disableCopy={true}
545
+ disableDuplicate={true}
546
+ disablePrint={true}
547
+ {...props}
548
+ />;
549
+ };
550
+ }
551
+
552
+ export const ComboEditor = withAdditionalProps(Combo);
553
+
554
+ export default Combo;
@@ -87,6 +87,7 @@ function Form(props) {
87
87
  onSave,
88
88
  onClose,
89
89
  onDelete,
90
+ editorStateRef,
90
91
 
91
92
  // DataMgt
92
93
  selectorId,
@@ -148,6 +149,11 @@ function Form(props) {
148
149
  borderRightColor: 'trueGray.200',
149
150
  px: 1,
150
151
  };
152
+
153
+ if (editorType === EDITOR_TYPE__INLINE) {
154
+ columnProps.minWidth = styles.INLINE_EDITOR_MIN_WIDTH;
155
+ }
156
+
151
157
  _.each(columnsConfig, (config, ix) => {
152
158
  let {
153
159
  fieldName,
@@ -159,7 +165,11 @@ function Form(props) {
159
165
  } = config;
160
166
 
161
167
  if (!isEditable) {
162
- const renderedValue = renderer ? renderer(record) : record[fieldName];
168
+ let renderedValue = renderer ? renderer(record) : record[fieldName];
169
+ if (_.isBoolean(renderedValue)) {
170
+ renderedValue = renderedValue.toString();
171
+ }
172
+ renderedValue += "\n(not editable)";
163
173
  elements.push(<Box key={ix} w={w} flex={flex} {...columnProps}>
164
174
  <Text numberOfLines={1} ellipsizeMode="head">{renderedValue}</Text>
165
175
  </Box>);
@@ -200,13 +210,7 @@ function Form(props) {
200
210
  }
201
211
  }
202
212
  const Element = getComponentFromType(editor);
203
- if (!Element) {
204
- debugger;
205
- // LEFT OFF HERE
206
- // Trying inline editor, based on columnsConfig
207
- // Getting an error that the OrdersEditor is missing users__email.
208
- // Why is this even on the OrdersEditor? Runner?
209
- }
213
+
210
214
  let element = <Element
211
215
  name={name}
212
216
  value={value}
@@ -450,6 +454,10 @@ function Form(props) {
450
454
  // if (Repository && (!record || _.isEmpty(record))) {
451
455
  // return null;
452
456
  // }
457
+
458
+ if (!_.isNil(editorStateRef)) {
459
+ editorStateRef.current = formState; // Update state so HOC can know what's going on
460
+ }
453
461
 
454
462
  const sizeProps = {};
455
463
  if (!flex && !h && !w) {
@@ -517,6 +525,12 @@ function Form(props) {
517
525
  if (_.isEmpty(formState.dirtyFields) && !record?.isRemotePhantom) {
518
526
  isSaveDisabled = true;
519
527
  }
528
+
529
+ if (editorType === EDITOR_TYPE__INLINE) {
530
+ buttonGroupProps.position = 'fixed';
531
+ 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
532
+ footerProps.alignItems = 'flex-start';
533
+ }
520
534
 
521
535
  return <Column {...sizeProps} onLayout={onLayout}>
522
536
 
@@ -532,13 +546,7 @@ function Form(props) {
532
546
  {isSingle && editorMode === EDITOR_MODE__EDIT && onViewMode &&
533
547
  <Button
534
548
  key="viewBtn"
535
- onPress={() => {
536
- if (!_.isEmpty(formState.dirtyFields)) {
537
- confirm('This record has unsaved changes. Are you sure you want to switch to "View" mode? Changes will be lost.', onViewMode);
538
- } else {
539
- onViewMode();
540
- }
541
- }}
549
+ onPress={onViewMode}
542
550
  leftIcon={<Icon as={Eye} color="#fff" size="sm" />}
543
551
  color="#fff"
544
552
  >To View</Button>}
@@ -551,19 +559,18 @@ function Form(props) {
551
559
  {editor}
552
560
 
553
561
  <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>}
562
+ {onDelete && editorMode === EDITOR_MODE__EDIT &&
563
+ <Row flex={1} justifyContent="flex-start">
564
+ <Button
565
+ key="deleteBtn"
566
+ onPress={onDelete}
567
+ bg="warning"
568
+ _hover={{
569
+ bg: 'warningHover',
570
+ }}
571
+ color="#fff"
572
+ >Delete</Button>
573
+ </Row>}
567
574
  <Button.Group space={2} {...buttonGroupProps}>
568
575
 
569
576
  {!isViewOnly && <IconButton
@@ -579,13 +586,7 @@ function Form(props) {
579
586
  {!isViewOnly && onCancel && <Button
580
587
  key="cancelBtn"
581
588
  variant="ghost"
582
- onPress={() => {
583
- if (!_.isEmpty(formState.dirtyFields)) {
584
- confirm('This record has unsaved changes. Are you sure you want to cancel editing? Changes will be lost.', onCancel);
585
- } else {
586
- onCancel();
587
- }
588
- }}
589
+ onPress={onCancel}
589
590
  color="#fff"
590
591
  >Cancel</Button>}
591
592
  {!isViewOnly && onSave && <Button
@@ -127,8 +127,8 @@ function GridComponent(props) {
127
127
  selectorSelected,
128
128
 
129
129
  // withInlineEditor
130
- inlineEditorRef,
131
- onScrollRow,
130
+ inlineEditor = null,
131
+ isInlineEditorShown = false,
132
132
  onEditorRowClick,
133
133
 
134
134
  } = props,
@@ -137,8 +137,8 @@ function GridComponent(props) {
137
137
  gridRef = useRef(),
138
138
  [isReady, setIsReady] = useState(false),
139
139
  [isLoading, setIsLoading] = useState(false),
140
- [isReorderMode, setIsReorderMode] = useState(false),
141
140
  [localColumnsConfig, setLocalColumnsConfigRaw] = useState([]),
141
+ [isDragMode, setIsDragMode] = useState(false),
142
142
  [dragRowSlot, setDragRowSlot] = useState(null),
143
143
  [dragRowIx, setDragRowIx] = useState(),
144
144
  setLocalColumnsConfig = (config) => {
@@ -148,6 +148,9 @@ function GridComponent(props) {
148
148
  }
149
149
  },
150
150
  onRowClick = (item, e) => {
151
+ if (isInlineEditorShown) {
152
+ return;
153
+ }
151
154
  const
152
155
  {
153
156
  shiftKey,
@@ -223,8 +226,8 @@ function GridComponent(props) {
223
226
  if (canRowsReorder) {
224
227
  items.unshift(<IconButton
225
228
  key="reorderBtn"
226
- onPress={() => setIsReorderMode(!isReorderMode)}
227
- icon={<Icon as={isReorderMode ? NoReorderRows : ReorderRows} color={styles.GRID_TOOLBAR_ITEMS_COLOR} />}
229
+ onPress={() => setIsDragMode(!isDragMode)}
230
+ icon={<Icon as={isDragMode ? NoReorderRows : ReorderRows} color={styles.GRID_TOOLBAR_ITEMS_COLOR} />}
228
231
  />);
229
232
  }
230
233
  return items;
@@ -233,6 +236,9 @@ function GridComponent(props) {
233
236
  if (row.item.isDestroyed) {
234
237
  return null;
235
238
  }
239
+ if (row.item.id === 'inlineEditor') {
240
+ return inlineEditor;
241
+ }
236
242
 
237
243
  let {
238
244
  item,
@@ -248,7 +254,7 @@ function GridComponent(props) {
248
254
  if (e.preventDefault && e.cancelable) {
249
255
  e.preventDefault();
250
256
  }
251
- if (isHeaderRow || isReorderMode) {
257
+ if (isHeaderRow || isDragMode) {
252
258
  return
253
259
  }
254
260
  switch (e.detail) {
@@ -275,7 +281,7 @@ function GridComponent(props) {
275
281
  if (e.preventDefault && e.cancelable) {
276
282
  e.preventDefault();
277
283
  }
278
- if (isHeaderRow || isReorderMode) {
284
+ if (isHeaderRow || isDragMode) {
279
285
  return
280
286
  }
281
287
 
@@ -309,6 +315,7 @@ function GridComponent(props) {
309
315
  setSelection={setSelection}
310
316
  gridRef={gridRef}
311
317
  isHovered={isHovered}
318
+ isInlineEditorShown={isInlineEditorShown}
312
319
  />;
313
320
  }
314
321
 
@@ -331,7 +338,7 @@ function GridComponent(props) {
331
338
  }
332
339
  let WhichGridRow = GridRow,
333
340
  rowReorderProps = {};
334
- if (canRowsReorder && isReorderMode) {
341
+ if (canRowsReorder && isDragMode) {
335
342
  WhichGridRow = ReorderableGridRow;
336
343
  rowReorderProps = {
337
344
  mode: VERTICAL,
@@ -353,6 +360,7 @@ function GridComponent(props) {
353
360
  hideNavColumn={hideNavColumn}
354
361
  bg={bg}
355
362
  item={item}
363
+ isInlineEditorShown={isInlineEditorShown}
356
364
  {...rowReorderProps}
357
365
  />;
358
366
  }}
@@ -660,10 +668,10 @@ function GridComponent(props) {
660
668
  ...propsToPass,
661
669
  };
662
670
 
663
- if (!config.w && !config.flex) {
671
+ if (!(config.w || config.width) && !config.flex) {
664
672
  // Neither is set
665
673
  config.w = 100; // default
666
- } else if (config.flex && config.width) {
674
+ } else if (config.flex && (config.w || config.width)) {
667
675
  // Both are set. Width overrules flex.
668
676
  delete config.flex;
669
677
  }
@@ -729,7 +737,7 @@ function GridComponent(props) {
729
737
 
730
738
  }, [selectorId, selectorSelected]);
731
739
 
732
- const footerToolbarItemComponents = useMemo(() => getFooterToolbarItems(), [additionalToolbarButtons, isReorderMode]);
740
+ const footerToolbarItemComponents = useMemo(() => getFooterToolbarItems(), [additionalToolbarButtons, isDragMode]);
733
741
 
734
742
  if (!isReady) {
735
743
  return null;
@@ -741,6 +749,9 @@ function GridComponent(props) {
741
749
  if (showHeaders) {
742
750
  rowData.unshift({ id: 'headerRow' });
743
751
  }
752
+ if (inlineEditor) {
753
+ rowData.push({ id: 'inlineEditor' }); // make editor the last row so it can scroll with all other rows
754
+ }
744
755
  const initialNumToRender = rowData.length || 10;
745
756
 
746
757
  // headers & footers
@@ -768,7 +779,7 @@ function GridComponent(props) {
768
779
  {topToolbar}
769
780
 
770
781
  <Column w="100%" flex={1} borderTopWidth={isLoading ? 2 : 1} borderTopColor={isLoading ? '#f00' : 'trueGray.300'} onClick={() => {
771
- if (!isReorderMode) {
782
+ if (!isDragMode && !isInlineEditorShown) {
772
783
  deselectAll();
773
784
  }
774
785
  }}>
@@ -781,8 +792,8 @@ function GridComponent(props) {
781
792
  nestedScrollEnabled={true}
782
793
  contentContainerStyle={{
783
794
  overflow: 'auto',
784
- borderWidth: isReorderMode ? styles.REORDER_BORDER_WIDTH : 0,
785
- borderColor: isReorderMode ? styles.REORDER_BORDER_COLOR : null,
795
+ borderWidth: isDragMode ? styles.REORDER_BORDER_WIDTH : 0,
796
+ borderColor: isDragMode ? styles.REORDER_BORDER_COLOR : null,
786
797
  borderStyle: styles.REORDER_BORDER_STYLE,
787
798
  flex: 1,
788
799
  }}
@@ -40,6 +40,7 @@ export default function GridHeaderRow(props) {
40
40
  setSelection,
41
41
  gridRef,
42
42
  isHovered,
43
+ isInlineEditorShown,
43
44
  } = props,
44
45
  styles = UiGlobals.styles,
45
46
  sortFn = Repository && Repository.getSortFn(),
@@ -316,6 +317,10 @@ export default function GridHeaderRow(props) {
316
317
  }
317
318
  }
318
319
 
320
+ if (isInlineEditorShown) {
321
+ propsToPass.minWidth = styles.INLINE_EDITOR_MIN_WIDTH;
322
+ }
323
+
319
324
  return <Pressable
320
325
  key={ix}
321
326
  onPress={(e) => {
@@ -453,6 +458,7 @@ export default function GridHeaderRow(props) {
453
458
  isSortDirectionAsc,
454
459
  sortFn,
455
460
  sortField,
461
+ isInlineEditorShown,
456
462
  ]);
457
463
  }
458
464
 
@@ -23,6 +23,7 @@ export default function GridRow(props) {
23
23
  hideNavColumn,
24
24
  bg,
25
25
  item,
26
+ isInlineEditorShown,
26
27
  } = props,
27
28
  styles = UiGlobals.styles,
28
29
  isPhantom = item.isPhantom,
@@ -44,6 +45,10 @@ export default function GridRow(props) {
44
45
  propsToPass.p = 1;
45
46
  propsToPass.justifyContent = 'center';
46
47
 
48
+ if (isInlineEditorShown) {
49
+ propsToPass.minWidth = styles.INLINE_EDITOR_MIN_WIDTH;
50
+ }
51
+
47
52
  let value;
48
53
  if (_.isPlainObject(config)) {
49
54
  if (config.renderer) {
@@ -64,6 +69,16 @@ export default function GridRow(props) {
64
69
  'showDragHandles',
65
70
  ]);
66
71
 
72
+ if (!extraProps._web) {
73
+ extraProps._web = {};
74
+ }
75
+ if (!extraProps._web.style) {
76
+ extraProps._web.style = {};
77
+ }
78
+ extraProps._web.style = {
79
+ userSelect: 'none',
80
+ };
81
+
67
82
  return <Row key={key} {...propsToPass} {...extraProps}>{config.renderer(item)}</Row>;
68
83
  }
69
84
  if (config.fieldName) {
@@ -98,7 +113,9 @@ export default function GridRow(props) {
98
113
  overflow="hidden"
99
114
  textOverflow="ellipsis"
100
115
  alignSelf="center"
101
- style={{ userSelect: 'none', }}
116
+ style={{
117
+ userSelect: 'none',
118
+ }}
102
119
  fontSize={styles.GRID_CELL_FONTSIZE}
103
120
  px={styles.GRID_CELL_PX}
104
121
  py={styles.GRID_CELL_PY}
@@ -141,6 +158,7 @@ export default function GridRow(props) {
141
158
  item,
142
159
  isPhantom,
143
160
  hash, // this is an easy way to determine if the data has changed and the item needs to be rerendered
161
+ isInlineEditorShown,
144
162
  ]);
145
163
  }
146
164
 
@@ -64,6 +64,12 @@ export default function withContextMenu(WrappedComponent) {
64
64
  };
65
65
  icon = React.cloneElement(icon, {...iconProps});
66
66
  }
67
+
68
+ // <div style={{
69
+ // userSelect: 'none',
70
+ // }}>
71
+ // </div>
72
+
67
73
  return <Pressable
68
74
  key={ix}
69
75
  onPress={() => {
@@ -79,6 +85,10 @@ export default function withContextMenu(WrappedComponent) {
79
85
  bg: '#ffc',
80
86
  }}
81
87
  isDisabled={isDisabled}
88
+ style={{
89
+ userSelect: 'none',
90
+ }}
91
+ userSelect="none"
82
92
  >
83
93
  {icon}
84
94
  <Text
@@ -86,6 +96,10 @@ export default function withContextMenu(WrappedComponent) {
86
96
  color={isDisabled ? 'disabled' : 'trueGray.800'}
87
97
  numberOfLines={1}
88
98
  ellipsizeMode="head"
99
+ style={{
100
+ userSelect: 'none',
101
+ }}
102
+ userSelect="none"
89
103
  >{text}</Text>
90
104
  </Pressable>;
91
105
  });
@@ -96,6 +110,7 @@ export default function withContextMenu(WrappedComponent) {
96
110
  flex={1}
97
111
  py={2}
98
112
  px={4}
113
+ userSelect="none"
99
114
  >id: {selection?.[0]?.id}</Text>);
100
115
  }
101
116
  setContextMenuItemComponents(contextMenuItemComponents);
@@ -47,10 +47,10 @@ export default function withEditor(WrappedComponent, isTree = false) {
47
47
  hideAlert,
48
48
  } = props,
49
49
  listeners = useRef({}),
50
+ editorStateRef = useRef(),
50
51
  [currentRecord, setCurrentRecord] = useState(null),
51
52
  [isEditorShown, setIsEditorShown] = useState(false),
52
53
  [isEditorViewOnly, setIsEditorViewOnly] = useState(false),
53
- [isModalShown, setIsModalShown] = useState(false),
54
54
  [lastSelection, setLastSelection] = useState(),
55
55
  getListeners = () => {
56
56
  return listeners.current;
@@ -115,7 +115,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
115
115
  setEditorMode(EDITOR_MODE__EDIT);
116
116
  setIsEditorShown(true);
117
117
  },
118
- onDelete = async () => {
118
+ onDelete = async (cb) => {
119
119
  if (getListeners().onBeforeDelete) {
120
120
  const listenerResult = await getListeners().onBeforeDelete();
121
121
  if (listenerResult === false) {
@@ -126,7 +126,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
126
126
  isSingle = selection.length === 1,
127
127
  firstSelection = selection[0],
128
128
  isTree = firstSelection?.isTree,
129
- hasChildren = firstSelection?.hasChildren,
129
+ hasChildren = isTree ? firstSelection?.hasChildren : false,
130
130
  isPhantom = firstSelection?.isPhantom;
131
131
 
132
132
  if (isSingle && isTree && hasChildren) {
@@ -135,10 +135,10 @@ export default function withEditor(WrappedComponent, isTree = false) {
135
135
  message: 'The node you have selected for deletion has children. ' +
136
136
  'Should these children be moved up to this node\'s parent, or be deleted?',
137
137
  buttons: [
138
- <Button colorScheme="danger" onPress={onMoveChildren} key="moveBtn">
138
+ <Button colorScheme="danger" onPress={() => onMoveChildren(cb)} key="moveBtn">
139
139
  Move Children
140
140
  </Button>,
141
- <Button colorScheme="danger" onPress={onDeleteChildren} key="deleteBtn">
141
+ <Button colorScheme="danger" onPress={() => onDeleteChildren(cb)} key="deleteBtn">
142
142
  Delete Children
143
143
  </Button>
144
144
  ],
@@ -146,21 +146,21 @@ export default function withEditor(WrappedComponent, isTree = false) {
146
146
  });
147
147
  } else
148
148
  if (isSingle && isPhantom) {
149
- deleteRecord();
149
+ deleteRecord(cb);
150
150
  } else {
151
151
  const identifier = getRecordIdentifier(selection);
152
- confirm('Are you sure you want to delete the ' + identifier, deleteRecord);
152
+ confirm('Are you sure you want to delete the ' + identifier, () => deleteRecord(cb));
153
153
  }
154
154
  },
155
- onMoveChildren = () => {
155
+ onMoveChildren = (cb) => {
156
156
  hideAlert();
157
- deleteRecord(true);
157
+ deleteRecord(true, cb);
158
158
  },
159
- onDeleteChildren = () => {
159
+ onDeleteChildren = (cb) => {
160
160
  hideAlert();
161
- deleteRecord();
161
+ deleteRecord(false, cb);
162
162
  },
163
- deleteRecord = async (moveSubtreeUp) => {
163
+ deleteRecord = async (moveSubtreeUp, cb) => {
164
164
  if (getListeners().onBeforeDeleteSave) {
165
165
  await getListeners().onBeforeDeleteSave(selection);
166
166
  }
@@ -172,6 +172,9 @@ export default function withEditor(WrappedComponent, isTree = false) {
172
172
  if (getListeners().onAfterDelete) {
173
173
  await getListeners().onAfterDelete(selection);
174
174
  }
175
+ if (cb) {
176
+ cb();
177
+ }
175
178
  },
176
179
  viewRecord = async () => {
177
180
  if (!userCanView) {
@@ -237,31 +240,32 @@ export default function withEditor(WrappedComponent, isTree = false) {
237
240
  await getListeners().onAfterEdit(what);
238
241
  }
239
242
  },
240
- onEditorCancel = async () => {
241
- const
242
- isSingle = selection.length === 1,
243
- isPhantom = selection[0] && selection[0].isPhantom;
244
- if (isSingle && isPhantom) {
245
- await deleteRecord();
243
+ onEditorCancel = () => {
244
+ async function doIt() {
245
+ const
246
+ isSingle = selection.length === 1,
247
+ isPhantom = selection[0] && selection[0].isPhantom;
248
+ if (isSingle && isPhantom) {
249
+ await deleteRecord();
250
+ }
251
+ setEditorMode(EDITOR_MODE__VIEW);
252
+ setIsEditorShown(false);
253
+ }
254
+ const formState = editorStateRef.current;
255
+ if (!_.isEmpty(formState.dirtyFields)) {
256
+ confirm('This record has unsaved changes. Are you sure you want to cancel editing? Changes will be lost.', doIt);
257
+ } else {
258
+ doIt();
246
259
  }
247
- setEditorMode(EDITOR_MODE__VIEW);
248
- setIsEditorShown(false);
249
260
  },
250
261
  onEditorClose = () => {
251
262
  setIsEditorShown(false);
252
263
  },
253
264
  onEditorDelete = async () => {
254
- if (getListeners().onBeforeDeleteSave) {
255
- await getListeners().onBeforeDeleteSave(selection);
256
- }
257
-
258
- await deleteRecord();
259
- setEditorMode(EDITOR_MODE__VIEW);
260
- setIsEditorShown(false);
261
-
262
- if (getListeners().onAfterDelete) {
263
- await getListeners().onAfterDelete(selection);
264
- }
265
+ onDelete(() => {
266
+ setEditorMode(EDITOR_MODE__VIEW);
267
+ setIsEditorShown(false);
268
+ });
265
269
  },
266
270
  calculateEditorMode = () => {
267
271
  let mode = EDITOR_MODE__VIEW;
@@ -279,6 +283,20 @@ export default function withEditor(WrappedComponent, isTree = false) {
279
283
  }
280
284
  }
281
285
  return mode;
286
+ },
287
+ onEditMode = () => {
288
+ setEditorMode(EDITOR_MODE__EDIT);
289
+ },
290
+ onViewMode = () => {
291
+ function doIt() {
292
+ setEditorMode(EDITOR_MODE__VIEW);
293
+ }
294
+ const formState = editorStateRef.current;
295
+ if (!_.isEmpty(formState.dirtyFields)) {
296
+ confirm('This record has unsaved changes. Are you sure you want to switch to "View" mode? Changes will be lost.', doIt);
297
+ } else {
298
+ doIt();
299
+ }
282
300
  };
283
301
 
284
302
  useEffect(() => {
@@ -301,7 +319,9 @@ export default function withEditor(WrappedComponent, isTree = false) {
301
319
  isEditorShown={isEditorShown}
302
320
  isEditorViewOnly={isEditorViewOnly}
303
321
  editorMode={editorMode}
304
- setEditorMode={setEditorMode}
322
+ onEditMode={onEditMode}
323
+ onViewMode={onViewMode}
324
+ editorStateRef={editorStateRef}
305
325
  setIsEditorShown={setIsEditorShown}
306
326
  onAdd={(!userCanEdit || disableAdd) ? null : onAdd}
307
327
  onEdit={(!userCanEdit || disableEdit) ? null : onEdit}
@@ -1,5 +1,6 @@
1
1
  import React, { useState, useEffect, useRef, } from 'react';
2
2
  import {
3
+ Box,
3
4
  Column,
4
5
  Modal,
5
6
  Row,
@@ -28,6 +29,7 @@ export default function withInlineEditor(WrappedComponent) {
28
29
  onEditorCancel,
29
30
  onEditorSave,
30
31
  onEditorClose,
32
+ editorStateRef,
31
33
 
32
34
  // withSelection
33
35
  selection,
@@ -36,6 +38,7 @@ export default function withInlineEditor(WrappedComponent) {
36
38
  Repository,
37
39
  } = props,
38
40
  styles = UiGlobals.styles,
41
+ maskRef = useRef(),
39
42
  inlineEditorRef = useRef(),
40
43
  [localColumnsConfig, setLocalColumnsConfig] = useState([]),
41
44
  [currentRow, setCurrentRow] = useState(),
@@ -56,35 +59,52 @@ export default function withInlineEditor(WrappedComponent) {
56
59
  },
57
60
  moveEditor = (currentRow) => {
58
61
  const
59
- bounds = currentRow.getBoundingClientRect(),
60
- r = inlineEditorRef.current.style;
61
- r.top = bounds.top -8 + 'px';
62
- r.left = bounds.left + 'px';
63
- r.width = bounds.width + 'px';
62
+ rowBounds = currentRow.getBoundingClientRect(),
63
+ editor = inlineEditorRef.current,
64
+ editorStyle = editor.style,
65
+ editorBounds = editor.parentElement.getBoundingClientRect(), // reference parentElement, because this doesn't change based on last moveEditor call
66
+ delta = editorBounds.top - rowBounds.top;
67
+
68
+ editorStyle.top = (-1 * delta) -20 + 'px';
64
69
  };
65
70
 
71
+ if (isEditorShown && selection.length < 1) {
72
+ throw new Error('Lost the selection!');
73
+ }
66
74
  if (isEditorShown && selection.length !== 1) {
67
75
  throw new Error('Can only edit one at a time with inline editor!');
68
76
  }
69
77
  if (UiGlobals.mode === UI_MODE_REACT_NATIVE) {
70
78
  throw new Error('Not yet implemented for RN.');
71
79
  }
72
-
73
- return <>
74
- <WrappedComponent
75
- {...props}
76
- inlineEditorRef={inlineEditorRef}
77
- onChangeColumnsConfig={onChangeColumnsConfig}
78
- onEditorRowClick={onRowClick}
79
- />
80
- {useEditor && editorType === EDITOR_TYPE__INLINE && Repository &&
81
- <Modal
82
- isOpen={isEditorShown}
83
- onClose={() => setIsEditorShown(false)}
84
- >
85
- <Column position="absolute" ref={inlineEditorRef}>
80
+
81
+ let inlineEditor = null;
82
+ if (useEditor && Repository) {
83
+ inlineEditor = <>
84
+ {isEditorShown && <Box
85
+ ref={maskRef}
86
+ position="fixed"
87
+ w="100vw"
88
+ h="100vh"
89
+ top="0"
90
+ left="0"
91
+ bg="#000"
92
+ opacity={0.35}
93
+ zIndex={0}
94
+ onClick={(e) => {
95
+ e.preventDefault();
96
+ e.stopPropagation();
97
+ onEditorCancel();
98
+ }}
99
+ ></Box>}
100
+ <Column
101
+ ref={inlineEditorRef}
102
+ position="absolute"
103
+ zIndex={10}
104
+ >
86
105
  {isEditorShown && <Form
87
- editorType={EDITOR_TYPE__INLINE}
106
+ editorType={EDITOR_TYPE__INLINE}
107
+ editorStateRef={editorStateRef}
88
108
  record={selection[0]}
89
109
  Repository={Repository}
90
110
  isMultiple={selection.length > 1}
@@ -113,7 +133,15 @@ export default function withInlineEditor(WrappedComponent) {
113
133
  px={0}
114
134
  />}
115
135
  </Column>
116
- </Modal>}
117
- </>;
136
+ </>;
137
+ }
138
+
139
+ return <WrappedComponent
140
+ {...props}
141
+ onChangeColumnsConfig={onChangeColumnsConfig}
142
+ onEditorRowClick={onRowClick}
143
+ inlineEditor={inlineEditor}
144
+ isInlineEditorShown={isEditorShown}
145
+ />;
118
146
  });
119
147
  }
@@ -24,6 +24,12 @@ const presetButtons = [
24
24
 
25
25
  export default function withPresetButtons(WrappedComponent, isGrid = false) {
26
26
  return (props) => {
27
+
28
+ if (props.disablePresetButtons) {
29
+ // bypass everything
30
+ return <WrappedComponent {...props} />;
31
+ }
32
+
27
33
  const {
28
34
  // extract and pass
29
35
  contextMenuItems,
@@ -13,7 +13,7 @@ import BooleanCombo from './Form/Field/Combo/BooleanCombo.js';
13
13
  import CheckboxGroup from './Form/Field/CheckboxGroup/CheckboxGroup.js';
14
14
  import Color from './Form/Field/Color.js';
15
15
  import Combo from './Form/Field/Combo/Combo.js';
16
- // import ComboEditor from '../Components/Form/Field/Combo/ComboEditor.js';
16
+ // import { ComboEditor } from '../Components/Form/Field/Combo/Combo.js';
17
17
  import Container from './Container/Container.js';
18
18
  import DataMgt from './Screens/DataMgt.js';
19
19
  import Date from './Form/Field/Date.js';
@@ -67,6 +67,7 @@ const defaults = {
67
67
  ICON_BUTTON_BG_DISABLED: 'trueGray.200',
68
68
  ICON_BUTTON_BG_HOVER: '#000:alpha.20',
69
69
  ICON_BUTTON_BG_PRESSED: '#000:alpha.30',
70
+ INLINE_EDITOR_MIN_WIDTH: 150,
70
71
  PANEL_FOOTER_BG: 'primary.100', // :alpha.50
71
72
  PANEL_HEADER_BG: 'primary.100',
72
73
  PANEL_HEADER_BG_VERTICAL: 'primary.100',
@@ -1,22 +0,0 @@
1
- import withAlert from '../../../Hoc/withAlert.js';
2
- import withData from '../../../Hoc/withData.js';
3
- import withPresetButtons from '../../../Hoc/withPresetButtons.js';
4
- import withSelection from '../../../Hoc/withSelection.js';
5
- import withValue from '../../../Hoc/withValue.js';
6
- import withWindowedEditor from '../../../Hoc/withWindowedEditor.js';
7
- import { Combo } from './Combo.js';
8
-
9
- export default
10
- // withAlert(
11
- withData(
12
- withValue(
13
- withSelection(
14
- withWindowedEditor(
15
- withPresetButtons(
16
- Combo
17
- )
18
- )
19
- )
20
- )
21
- );
22
- //);