@onehat/ui 0.3.243 → 0.3.246

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.243",
3
+ "version": "0.3.246",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -204,7 +204,7 @@ function Container(props) {
204
204
  componentProps.setIsCollapsed = setIsNorthCollapsed || setLocalIsNorthCollapsed;
205
205
  componentProps.onLayout = (e) => {
206
206
  const height = parseFloat(e.nativeEvent.layout.height);
207
- if (height !== northHeight) {
207
+ if (height && height !== northHeight) {
208
208
  setNorthHeight(height);
209
209
  }
210
210
  };
@@ -228,7 +228,7 @@ function Container(props) {
228
228
  componentProps.setIsCollapsed = setIsSouthCollapsed || setLocalIsSouthCollapsed;
229
229
  componentProps.onLayout = (e) => {
230
230
  const height = parseFloat(e.nativeEvent.layout.height);
231
- if (height !== southHeight) {
231
+ if (height && height !== southHeight) {
232
232
  setSouthHeight(height);
233
233
  }
234
234
  };
@@ -252,7 +252,7 @@ function Container(props) {
252
252
  componentProps.setIsCollapsed = setIsEastCollapsed || setLocalIsEastCollapsed;
253
253
  componentProps.onLayout = (e) => {
254
254
  const width = parseFloat(e.nativeEvent.layout.width);
255
- if (width !== eastWidth) {
255
+ if (width && width !== eastWidth) {
256
256
  setEastWidth(width);
257
257
  }
258
258
  };
@@ -276,7 +276,7 @@ function Container(props) {
276
276
  componentProps.setIsCollapsed = setIsWestCollapsed || setLocalIsWestCollapsed;
277
277
  componentProps.onLayout = (e) => {
278
278
  const width = parseFloat(e.nativeEvent.layout.width);
279
- if (width !== westWidth) {
279
+ if (width && width !== westWidth) {
280
280
  setWestWidth(width);
281
281
  }
282
282
  };
@@ -38,7 +38,7 @@ export function ComboComponent(props) {
38
38
  autoFocus = false,
39
39
  tooltipRef = null,
40
40
  tooltip = null,
41
- menuMinWidth = 150,
41
+ menuMinWidth,
42
42
  disableDirectEntry = false,
43
43
  hideMenuOnSelection = true,
44
44
  showXButton = false,
@@ -126,8 +126,14 @@ export function ComboComponent(props) {
126
126
  if (rect.left !== left) {
127
127
  setLeft(rect.left);
128
128
  }
129
- if (rect.width !== width) {
130
- setWidth(rect.width);
129
+
130
+ let widthToSet = rect.width,
131
+ minWidthToUse = menuMinWidth || styles.FORM_COMBO_MENU_MIN_WIDTH;
132
+ if (widthToSet < minWidthToUse) {
133
+ widthToSet = minWidthToUse;
134
+ }
135
+ if (widthToSet !== width) {
136
+ setWidth(widthToSet);
131
137
  }
132
138
  }
133
139
  if (Repository && !Repository.isLoaded) {
@@ -807,7 +813,6 @@ export function ComboComponent(props) {
807
813
  top={top + 'px'}
808
814
  left={left + 'px'}
809
815
  w={width + 'px'}
810
- minWidth={menuMinWidth}
811
816
  overflow="auto"
812
817
  bg="#fff"
813
818
  >
@@ -136,7 +136,7 @@ function Form(props) {
136
136
  forceUpdate = useForceUpdate(),
137
137
  [previousRecord, setPreviousRecord] = useState(record),
138
138
  [containerWidth, setContainerWidth] = useState(),
139
- initialValues = _.merge(startingValues, (record && !record.isDestroyed ? record.submitValues : {})),
139
+ initialValues = _.merge(startingValues, (record && !record.isDestroyed ? record.submitValues : {})),
140
140
  defaultValues = isMultiple ? getNullFieldValues(initialValues, Repository) : initialValues, // when multiple entities, set all default values to null
141
141
  validatorToUse = validator || (isMultiple ? disableRequiredYupFields(Repository?.schema?.model?.validator) : Repository?.schema?.model?.validator) || yup.object(),
142
142
  {
@@ -198,12 +198,16 @@ function Form(props) {
198
198
  useSelectorId = false,
199
199
  getDynamicProps,
200
200
  getIsRequired,
201
+ isHidden = false,
201
202
  ...propsToPass
202
203
  } = config,
203
204
  type,
204
205
  editorTypeProps = {},
205
206
  viewerTypeProps = {};
206
207
 
208
+ if (isHidden) {
209
+ return;
210
+ }
207
211
  if (editField) {
208
212
  // Sometimes, columns will be configured to display one field
209
213
  // and edit a different field
@@ -0,0 +1,123 @@
1
+ import { useRef, } from 'react';
2
+ import Button from '../Buttons/Button.js';
3
+ import CheckboxButton from '../Buttons/CheckboxButton.js';
4
+ import Grid from './Grid.js';
5
+ import Panel from '../Panel/Panel.js';
6
+ import Toolbar from '../Toolbar/Toolbar.js';
7
+ import useAdjustedWindowSize from '../../Hooks/useAdjustedWindowSize.js';
8
+ import useForceUpdate from '../../Hooks/useForceUpdate.js';
9
+ import _ from 'lodash';
10
+
11
+ // helper for Grid
12
+
13
+ export default function ColumnSelectorWindow(props) {
14
+ const
15
+ {
16
+ onClose,
17
+ columnsConfig,
18
+ setColumnsConfig,
19
+ } = props,
20
+ forceUpdate = useForceUpdate(),
21
+ localColumnsConfig = useRef(columnsConfig),
22
+ [width, height] = useAdjustedWindowSize(300, 500),
23
+ onSave = () => {
24
+ const newColumnsConfig = _.cloneDeep(localColumnsConfig.current);
25
+ setColumnsConfig(newColumnsConfig);
26
+ onClose();
27
+ },
28
+ onShowColumn = (ix) => {
29
+ const newColumnsConfig = _.cloneDeep(localColumnsConfig.current);
30
+ newColumnsConfig[ix].isHidden = false;
31
+ localColumnsConfig.current = newColumnsConfig;
32
+ forceUpdate();
33
+ },
34
+ onHideColumn = (ix) => {
35
+ const newColumnsConfig = _.cloneDeep(localColumnsConfig.current);
36
+ newColumnsConfig[ix].isHidden = true;
37
+ localColumnsConfig.current = newColumnsConfig;
38
+ forceUpdate();
39
+ },
40
+ columnData = _.map(localColumnsConfig.current, (config, ix) => {
41
+ const {
42
+ fieldName,
43
+ header = _.upperFirst(fieldName),
44
+ } = config;
45
+ return [
46
+ ix,
47
+ header,
48
+ ];
49
+ });
50
+ return <Panel
51
+ {...props}
52
+ title="Column Selector"
53
+ reference="columnSelectorWindow"
54
+ isCollapsible={false}
55
+ bg="#fff"
56
+ w={width}
57
+ h={height}
58
+ flex={null}
59
+ >
60
+ <Grid
61
+ showHeaders={false}
62
+ showHovers={false}
63
+ shadow={1}
64
+ autoAdjustPageSizeToHeight={false}
65
+ disableWithSelection={true}
66
+ data={columnData}
67
+ flex={1}
68
+ alternateRowBackgrounds={false}
69
+ columnsConfig={[
70
+ {
71
+ header: 'Show',
72
+ fieldName: 'is_shown',
73
+ sortable: false,
74
+ isEditable: false,
75
+ reorderable: false,
76
+ resizable: false,
77
+ w: '50px',
78
+ renderer: (datum) => {
79
+ const
80
+ [ix, header] = datum,
81
+ columnConfig = localColumnsConfig.current[ix],
82
+ isHidden = columnConfig.isHidden;
83
+ return <CheckboxButton
84
+ isChecked={!isHidden}
85
+ onPress={() => {
86
+ if (isHidden) {
87
+ onShowColumn(ix);
88
+ } else {
89
+ onHideColumn(ix);
90
+ }
91
+ }}
92
+ />;
93
+ }
94
+ },
95
+ {
96
+ header: 'Column',
97
+ fieldName: 1, // ix
98
+ sortable: false,
99
+ isEditable: false,
100
+ reorderable: false,
101
+ resizable: false,
102
+ flex: 3,
103
+ }
104
+ ]}
105
+ />
106
+ <Toolbar
107
+ justifyContent="flex-end">
108
+ <Button
109
+ key="cancelBtn"
110
+ variant="ghost"
111
+ onPress={onClose}
112
+ color="#fff"
113
+ mr={2}
114
+ >Cancel</Button>
115
+ <Button
116
+ key="saveBtn"
117
+ onPress={onSave}
118
+ color="#fff"
119
+ >Save</Button>
120
+ </Toolbar>
121
+ </Panel>;
122
+ }
123
+
@@ -3,6 +3,7 @@ import {
3
3
  Box,
4
4
  Column,
5
5
  FlatList,
6
+ Modal,
6
7
  Pressable,
7
8
  Icon,
8
9
  Row,
@@ -16,9 +17,6 @@ import {
16
17
  import {
17
18
  v4 as uuid,
18
19
  } from 'uuid';
19
- import {
20
- VERTICAL,
21
- } from '../../Constants/Directions.js';
22
20
  import {
23
21
  DROP_POSITION_BEFORE,
24
22
  DROP_POSITION_AFTER,
@@ -44,22 +42,22 @@ import withMultiSelection from '../Hoc/withMultiSelection.js';
44
42
  import withSelection from '../Hoc/withSelection.js';
45
43
  import withWindowedEditor from '../Hoc/withWindowedEditor.js';
46
44
  import withInlineEditor from '../Hoc/withInlineEditor.js';
47
- import withInlineSideEditor from '../Hoc/withInlineSideEditor.js';
48
45
  import getSaved from '../../Functions/getSaved.js';
49
46
  import setSaved from '../../Functions/setSaved.js';
50
47
  import getIconButtonFromConfig from '../../Functions/getIconButtonFromConfig.js';
51
48
  import testProps from '../../Functions/testProps.js';
52
49
  import nbToRgb from '../../Functions/nbToRgb.js';
53
50
  import Loading from '../Messages/Loading.js';
54
- import GridHeaderRow from '../Grid/GridHeaderRow.js';
51
+ import GridHeaderRow from './GridHeaderRow.js';
55
52
  import GridRow, { DragSourceDropTargetGridRow, DragSourceGridRow, DropTargetGridRow } from './GridRow.js';
56
53
  import IconButton from '../Buttons/IconButton.js';
57
54
  import ExpandButton from '../Buttons/ExpandButton.js';
58
55
  import PaginationToolbar from '../Toolbar/PaginationToolbar.js';
59
- import NoRecordsFound from '../Grid/NoRecordsFound.js';
56
+ import NoRecordsFound from './NoRecordsFound.js';
60
57
  import Toolbar from '../Toolbar/Toolbar.js';
61
58
  import NoReorderRows from '../Icons/NoReorderRows.js';
62
59
  import ReorderRows from '../Icons/ReorderRows.js';
60
+ import ColumnSelectorWindow from './ColumnSelectorWindow.js';
63
61
  import _ from 'lodash';
64
62
 
65
63
 
@@ -213,6 +211,7 @@ function GridComponent(props) {
213
211
  [localColumnsConfig, setLocalColumnsConfigRaw] = useState([]),
214
212
  [isDragMode, setIsDragMode] = useState(false),
215
213
  [dragRow, setDragRow] = useState(),
214
+ [isColumnSelectorShown, setIsColumnSelectorShown] = useState(false),
216
215
  getIsExpanded = (index) => {
217
216
  return !!expandedRowsRef.current[index];
218
217
  },
@@ -422,6 +421,7 @@ function GridComponent(props) {
422
421
  isHovered={isHovered}
423
422
  isInlineEditorShown={isInlineEditorShown}
424
423
  areRowsDragSource={areRowsDragSource}
424
+ showColumnsSelector={() => setIsColumnSelectorShown(true)}
425
425
  />;
426
426
  }
427
427
 
@@ -524,6 +524,7 @@ function GridComponent(props) {
524
524
  size: 'sm',
525
525
  }}
526
526
  py={0}
527
+ tooltip="Expand/Contract Row"
527
528
  />
528
529
  {rowComponent}
529
530
  </Row>
@@ -784,7 +785,7 @@ function GridComponent(props) {
784
785
  sortable = true,
785
786
  w,
786
787
  flex,
787
- ...propsToPass
788
+ isHidden = false,
788
789
  } = columnConfig,
789
790
 
790
791
  config = {
@@ -801,8 +802,8 @@ function GridComponent(props) {
801
802
  sortable,
802
803
  w,
803
804
  flex,
804
- showDragHandles: false,
805
- ...propsToPass,
805
+ isHidden,
806
+ isOver: false,
806
807
  };
807
808
 
808
809
  if (!(config.w || config.width) && !config.flex) {
@@ -1013,6 +1014,23 @@ function GridComponent(props) {
1013
1014
  gridContainerBorderProps.borderTopColor = 'trueGray.300';
1014
1015
  }
1015
1016
 
1017
+ let columnSelector = null;
1018
+ if (isColumnSelectorShown) {
1019
+ const onCloseColumnSelector = () => {
1020
+ setIsColumnSelectorShown(false);
1021
+ };
1022
+ columnSelector = <Modal
1023
+ isOpen={true}
1024
+ onClose={onCloseColumnSelector}
1025
+ >
1026
+ <ColumnSelectorWindow
1027
+ onClose={onCloseColumnSelector}
1028
+ columnsConfig={localColumnsConfig}
1029
+ setColumnsConfig={setLocalColumnsConfig}
1030
+ />
1031
+ </Modal>;
1032
+ }
1033
+
1016
1034
  grid = <Column
1017
1035
  {...testProps('Grid')}
1018
1036
  testID="outerContainer"
@@ -1034,7 +1052,7 @@ function GridComponent(props) {
1034
1052
  minHeight={40}
1035
1053
  {...gridContainerBorderProps}
1036
1054
  onClick={() => {
1037
- if (!isDragMode && !isInlineEditorShown) {
1055
+ if (!isDragMode && !isInlineEditorShown && deselectAll) {
1038
1056
  deselectAll();
1039
1057
  }
1040
1058
  }}
@@ -1044,6 +1062,8 @@ function GridComponent(props) {
1044
1062
 
1045
1063
  {listFooterComponent}
1046
1064
 
1065
+ {columnSelector}
1066
+
1047
1067
  </Column>
1048
1068
 
1049
1069
  if (isDropTarget) {
@@ -21,6 +21,7 @@ import useBlocking from '../../Hooks/useBlocking.js';
21
21
  import AngleRight from '../Icons/AngleRight.js';
22
22
  import HeaderReorderHandle from './HeaderReorderHandle.js';
23
23
  import HeaderResizeHandle from './HeaderResizeHandle.js';
24
+ import HeaderColumnSelectorHandle from './HeaderColumnSelectorHandle.js';
24
25
  import SortDown from '../Icons/SortDown.js';
25
26
  import SortUp from '../Icons/SortUp.js';
26
27
  import _ from 'lodash';
@@ -43,6 +44,7 @@ export default function GridHeaderRow(props) {
43
44
  isHovered,
44
45
  isInlineEditorShown,
45
46
  areRowsDragSource,
47
+ showColumnsSelector,
46
48
  } = props,
47
49
  styles = UiGlobals.styles,
48
50
  sortFn = Repository && Repository.getSortFn(),
@@ -82,7 +84,7 @@ export default function GridHeaderRow(props) {
82
84
  return;
83
85
  }
84
86
  const columnsConfig = _.clone(localColumnsConfig); // work with a copy, so that setter forces rerender
85
- columnsConfig[ix].showDragHandles = true;
87
+ columnsConfig[ix].isOver = true;
86
88
  setLocalColumnsConfig(columnsConfig);
87
89
  },
88
90
  onHeaderMouseLeave = (e, ix) => {
@@ -90,7 +92,7 @@ export default function GridHeaderRow(props) {
90
92
  return;
91
93
  }
92
94
  const columnsConfig = _.clone(localColumnsConfig); // work with a copy, so that setter forces rerender
93
- columnsConfig[ix].showDragHandles = false;
95
+ columnsConfig[ix].isOver = false;
94
96
  setLocalColumnsConfig(columnsConfig);
95
97
  },
96
98
  onColumnReorderDragStart = (info, e, proxy, node) => {
@@ -246,7 +248,7 @@ export default function GridHeaderRow(props) {
246
248
  setColumnsConfig(columnsConfig);
247
249
 
248
250
  if (dragColumnSlot) {
249
- dragColumnSlot.marker.remove();
251
+ dragColumnSlot.marker?.remove();
250
252
  }
251
253
  setDragColumnSlot(null);
252
254
  },
@@ -299,7 +301,8 @@ export default function GridHeaderRow(props) {
299
301
  sortable,
300
302
  w,
301
303
  flex,
302
- showDragHandles,
304
+ isOver = false,
305
+ isHidden = false,
303
306
  } = config,
304
307
  isSorter = sortable && canColumnsSort && sortField === fieldName,
305
308
  isReorderable = canColumnsReorder && reorderable,
@@ -308,6 +311,9 @@ export default function GridHeaderRow(props) {
308
311
  borderRightWidth: 2,
309
312
  borderRightColor: '#fff',
310
313
  };
314
+ if (isHidden) {
315
+ return null;
316
+ }
311
317
 
312
318
  if (all.length === 1) {
313
319
  propsToPass.w = '100%';
@@ -360,7 +366,7 @@ export default function GridHeaderRow(props) {
360
366
  onMouseLeave={(e) => onHeaderMouseLeave(e, ix)}
361
367
  {...propsToPass}
362
368
  >
363
- {isReorderable && showDragHandles &&
369
+ {isReorderable && isOver &&
364
370
  <HeaderReorderHandle
365
371
  key="HeaderReorderHandle"
366
372
  mode={HORIZONTAL}
@@ -409,7 +415,13 @@ export default function GridHeaderRow(props) {
409
415
  color="trueGray.500"
410
416
  />}
411
417
 
412
- {isResizable && showDragHandles &&
418
+ {isOver && UiGlobals.mode === UI_MODE_WEB && // only works for web for now
419
+ <HeaderColumnSelectorHandle
420
+ key="HeaderColumnSelectorHandle"
421
+ showColumnsSelector={showColumnsSelector}
422
+ />}
423
+
424
+ {isResizable && isOver &&
413
425
  <HeaderResizeHandle
414
426
  key="HeaderResizeHandle"
415
427
  mode={HORIZONTAL}
@@ -46,6 +46,9 @@ function GridRow(props) {
46
46
  const renderColumns = (item) => {
47
47
  if (_.isArray(columnsConfig)) {
48
48
  return _.map(columnsConfig, (config, key, all) => {
49
+ if (config.isHidden) {
50
+ return null;
51
+ }
49
52
  const propsToPass = columnProps[key] || {};
50
53
  if (all.length === 1) {
51
54
  propsToPass.w = '100%';
@@ -86,7 +89,7 @@ function GridRow(props) {
86
89
  'sortable',
87
90
  'w',
88
91
  'flex',
89
- 'showDragHandles',
92
+ 'isOver',
90
93
  ]);
91
94
 
92
95
  if (!extraProps._web) {
@@ -0,0 +1,25 @@
1
+ import {
2
+ Icon,
3
+ Pressable,
4
+ } from 'native-base';
5
+ import Gear from '../Icons/Gear.js';
6
+ import _ from 'lodash';
7
+
8
+ export default function HeaderColumnSelectorHandle(props) {
9
+ const {
10
+ showColumnsSelector,
11
+ } = props;
12
+ return <Pressable
13
+ testID="HeaderColumnSelectorHandle"
14
+ bg="trueGray.100"
15
+ _hover={{ bg: 'trueGray.200' }}
16
+ _pressed={{ bg: 'trueGray.300' }}
17
+ h="100%"
18
+ w={3}
19
+ alignItems="center"
20
+ justifyContent="center"
21
+ onPress={showColumnsSelector}
22
+ >
23
+ <Icon as={Gear} testID="handle" size="xs" w="100%" h="100%" color="#ccc" />
24
+ </Pressable>;
25
+ }
@@ -48,7 +48,7 @@ export default function withComponent(WrappedComponent) {
48
48
  });
49
49
 
50
50
  useEffect(() => {
51
- if (parent && !parent.hasChild(selfRef.current)) {
51
+ if (parent && !parent?.hasChild(selfRef.current)) {
52
52
  parent.registerChild(selfRef.current);
53
53
  }
54
54
  return () => {
@@ -25,6 +25,7 @@ export default function withSideEditor(WrappedComponent, isTree = false) {
25
25
  Editor,
26
26
  editorProps = {},
27
27
  sideFlex = 100,
28
+ isResizable = false,
28
29
 
29
30
  // withComponent
30
31
  self,
@@ -40,7 +41,16 @@ export default function withSideEditor(WrappedComponent, isTree = false) {
40
41
  throw Error('Editor is not defined');
41
42
  }
42
43
 
44
+ if (isResizable) {
45
+ editorProps.w = 500;
46
+ editorProps.isResizable = true;
47
+ } else {
48
+ editorProps.flex = sideFlex;
49
+ }
50
+
43
51
  return <Container
52
+ parent={self}
53
+ reference="SideEditor"
44
54
  center={<WrappedComponent
45
55
  isTree={isTree}
46
56
  isSideEditor={true}
@@ -49,7 +59,6 @@ export default function withSideEditor(WrappedComponent, isTree = false) {
49
59
  east={<Editor
50
60
  {...propsToPass}
51
61
  editorType={EDITOR_TYPE__SIDE}
52
- flex={sideFlex}
53
62
  borderLeftWidth={1}
54
63
  borderLeftColor="#ccc"
55
64
  {...editorProps}
@@ -26,6 +26,7 @@ function ManagerScreen(props) {
26
26
  styles = UiGlobals.styles,
27
27
  id = props.id || props.self?.path,
28
28
  [isRendered, setIsRendered] = useState(false),
29
+ [isModeSet, setIsModeSet] = useState(false),
29
30
  [allowSideBySide, setAllowSideBySide] = useState(false),
30
31
  [mode, setModeRaw] = useState(MODE_FULL),
31
32
  setMode = (newMode) => {
@@ -63,6 +64,7 @@ function ManagerScreen(props) {
63
64
  setMode(val);
64
65
  }
65
66
  }
67
+ setIsModeSet(true);
66
68
  })();
67
69
  }, [isRendered]);
68
70
 
@@ -81,7 +83,7 @@ function ManagerScreen(props) {
81
83
  }
82
84
 
83
85
  return <Column maxHeight="100vh" overflow="hidden" flex={1} w="100%" onLayout={onLayout}>
84
- {isRendered &&
86
+ {isRendered && isModeSet &&
85
87
  <>
86
88
  <Row
87
89
  h="80px"
@@ -70,6 +70,9 @@ function TabBar(props) {
70
70
  }
71
71
  return; // no change
72
72
  }
73
+ if (tabs[currentTabIx].content) {
74
+ tabs[currentTabIx].content = null; // free up memory by clearing rendered content
75
+ }
73
76
  if (useLocal) {
74
77
  setCurrentTabIxLocal(ix);
75
78
 
@@ -20,6 +20,7 @@ const defaults = {
20
20
  FORM_COMBO_INPUT_BG: WHITE,
21
21
  FORM_COMBO_INPUT_FOCUS_BG: FOCUS,
22
22
  FORM_COMBO_MENU_HEIGHT: 250,
23
+ FORM_COMBO_MENU_MIN_WIDTH: 250,
23
24
  FORM_COMBO_TRIGGER_BG: WHITE,
24
25
  FORM_COMBO_TRIGGER_HOVER_BG: 'trueGray.300',
25
26
  FORM_DATE_ICON_BG: 'primary.200',
@@ -2,5 +2,5 @@ import UiGlobals from '../UiGlobals.js';
2
2
  import _ from 'lodash';
3
3
 
4
4
  export default function registerComponents(newComponents) {
5
- _.merge(UiGlobals.components, newComponents);
5
+ _.assign(UiGlobals.components, newComponents); // use assign instead of merge, so that it overwrites existing properties; however this does not work recursively
6
6
  }