@onehat/ui 0.2.80 → 0.2.84

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.80",
3
+ "version": "0.2.84",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -25,12 +25,14 @@
25
25
  },
26
26
  "license": "UNLICENSED",
27
27
  "dependencies": {
28
- "@onehat/data": "^1.18.7",
29
- "@hookform/resolvers": "^3.1.1",
28
+ "@onehat/data": "^1.18.12",
29
+ "@hookform/resolvers": "^3.3.1",
30
30
  "@k-renwick/colour-mixer": "^1.2.1",
31
+ "@reduxjs/toolkit": "^1.9.5",
31
32
  "js-cookie": "^3.0.5",
32
33
  "native-base": "^3.4.28",
33
34
  "react-hook-form": "^7.45.0",
35
+ "react-redux": "^8.1.2",
34
36
  "yup": "^1.2.0"
35
37
  },
36
38
  "peerDependencies": {
@@ -1,3 +1,6 @@
1
+ import {
2
+ Box,
3
+ } from 'native-base';
1
4
  import {
2
5
  EDITOR_MODE__VIEW,
3
6
  } from '../../Constants/Editor.js';
@@ -25,10 +28,11 @@ export default function Editor(props) {
25
28
  } = props;
26
29
 
27
30
  if (_.isEmpty(selection)) {
28
- return null;
31
+ return null; // hide the editor when no selection
32
+ return <Box {...props} bg="#ddd" />;
29
33
  }
30
34
 
31
- if (Repository.isRemotePhantomMode && selection.length === 1 && editorMode === EDITOR_MODE__VIEW) {
35
+ if (Repository?.isRemotePhantomMode && selection.length === 1 && editorMode === EDITOR_MODE__VIEW) {
32
36
  return <Viewer
33
37
  {...props}
34
38
  record={selection[0]}
@@ -31,6 +31,7 @@ export default function Viewer(props) {
31
31
  onDelete,
32
32
  } = props,
33
33
  styles = UiGlobals.styles,
34
+ flex = props.flex || 1,
34
35
  buildAncillary = () => {
35
36
  let components = [];
36
37
  if (ancillaryItems.length) {
@@ -55,14 +56,14 @@ export default function Viewer(props) {
55
56
  fontWeight="bold"
56
57
  >{title}</Text>;
57
58
  }
58
- return <Column key={'ancillary-' + ix} px={2} pb={1}>{title}{element}</Column>;
59
+ return <Column key={'ancillary-' + ix} my={5}>{title}{element}</Column>;
59
60
  });
60
61
  }
61
62
  return components;
62
63
  };
63
64
 
64
- return <Column flex={1} w="100%">
65
- <ScrollView flex={1} w="100%">
65
+ return <Column flex={flex} {...props}>
66
+ <ScrollView width="100%" _web={{ height: 1 }}>
66
67
  <Column m={2}>
67
68
  {onEditMode && <Row mb={4} justifyContent="flex-end">
68
69
  <Button
@@ -427,7 +427,7 @@ function Form(props) {
427
427
  fontWeight="bold"
428
428
  >{title}</Text>;
429
429
  }
430
- return <Column key={'ancillary-' + ix} px={2} pb={1}>{title}{element}</Column>;
430
+ return <Column key={'ancillary-' + ix} mx={2} my={5}>{title}{element}</Column>;
431
431
  });
432
432
  }
433
433
  return components;
@@ -499,7 +499,7 @@ function Form(props) {
499
499
  // for all other editor types
500
500
  formComponents = buildFromItems();
501
501
  const formAncillaryComponents = buildAncillary();
502
- editor = <ScrollView flex={1} width="100%" pb={1}>
502
+ editor = <ScrollView _web={{ height: 1 }} width="100%" pb={1}>
503
503
  <Row>{formComponents}</Row>
504
504
  <Column pt={4}>{formAncillaryComponents}</Column>
505
505
  </ScrollView>;
@@ -774,6 +774,8 @@ function GridComponent(props) {
774
774
  {...testProps('Grid')}
775
775
  w="100%"
776
776
  bg={bg}
777
+ borderWidth={styles.GRID_BORDER_WIDTH}
778
+ borderColor={styles.GRID_BORDER_COLOR}
777
779
  {...sizeProps}
778
780
  >
779
781
  {topToolbar}
@@ -52,6 +52,17 @@ export default function withEditor(WrappedComponent, isTree = false) {
52
52
  [isEditorShown, setIsEditorShown] = useState(false),
53
53
  [isEditorViewOnly, setIsEditorViewOnly] = useState(false),
54
54
  [lastSelection, setLastSelection] = useState(),
55
+ setSelectionDecorated = (newSelection) => {
56
+ function doIt() {
57
+ setSelection(newSelection);
58
+ }
59
+ const formState = editorStateRef.current;
60
+ if (!_.isEmpty(formState?.dirtyFields) && newSelection !== selection && editorMode === EDITOR_MODE__EDIT) {
61
+ confirm('This record has unsaved changes. Are you sure you want to cancel editing? Changes will be lost.', doIt);
62
+ } else {
63
+ doIt();
64
+ }
65
+ },
55
66
  getListeners = () => {
56
67
  return listeners.current;
57
68
  },
@@ -342,6 +353,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
342
353
  disableDelete={disableDelete}
343
354
  disableDuplicate={disableDuplicate}
344
355
  disableView ={disableView}
356
+ setSelection={setSelectionDecorated}
345
357
  />;
346
358
  };
347
359
  }
@@ -19,13 +19,18 @@ export default function withSideEditor(WrappedComponent, isTree = false) {
19
19
  }
20
20
 
21
21
  return <Container
22
- center={<WrappedComponent {...props} />}
22
+ center={<WrappedComponent
23
+ isTree={isTree}
24
+ {...props}
25
+ />}
23
26
  east={<Editor
27
+ {...props}
24
28
  editorType={EDITOR_TYPE__SIDE}
25
29
  flex={sideFlex}
26
- {...props}
30
+ borderLeftWidth={1}
31
+ borderLeftColor="#ccc"
27
32
  {...editorProps}
28
33
  />}
29
34
  />;
30
- }, isTree);
35
+ });
31
36
  }
@@ -0,0 +1,28 @@
1
+ import * as React from "react"
2
+ import Svg, { Defs, G, Path } from "react-native-svg"
3
+ import { Icon } from 'native-base';
4
+
5
+ function SvgComponent(props) {
6
+ return (
7
+ <Icon
8
+ id="Layer_2"
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ viewBox="0 0 237.17 235.06"
11
+ {...props}
12
+ >
13
+ <Defs></Defs>
14
+ <G id="Layer_1-2">
15
+ <Path
16
+ className="cls-1"
17
+ d="M237.17 235.06H0V0h237.17v235.06zM13.46 221.59H223.7V13.47H13.47V221.6z"
18
+ />
19
+ <Path
20
+ className="cls-1"
21
+ d="M200.77 192.33H36.4V42.74h164.36v149.59zm-150.9-13.47H187.3V56.2H49.87v122.66z"
22
+ />
23
+ </G>
24
+ </Icon>
25
+ )
26
+ }
27
+
28
+ export default SvgComponent
@@ -0,0 +1,23 @@
1
+ import * as React from "react"
2
+ import Svg, { G, Path } from "react-native-svg"
3
+ import { Icon } from 'native-base';
4
+
5
+ function SvgComponent(props) {
6
+ return (
7
+ <Icon
8
+ id="Layer_2"
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ viewBox="0 0 237.17 235.06"
11
+ {...props}
12
+ >
13
+ <G id="Layer_1-2">
14
+ <Path
15
+ d="M85.22 235.06H0V0h85.22v235.06zm-71.75-13.47h58.29V13.47H13.47V221.6zM237.17 235.06H109.74V0h127.43v235.06zm-113.96-13.47h100.5V13.47h-100.5V221.6z"
16
+ strokeWidth={0}
17
+ />
18
+ </G>
19
+ </Icon>
20
+ )
21
+ }
22
+
23
+ export default SvgComponent
@@ -28,7 +28,11 @@ export function GridPanel(props) {
28
28
  }
29
29
  }
30
30
 
31
- return <Panel {...props} {..._panel}>
31
+ if (!props._panel) {
32
+ props._panel = {};
33
+ }
34
+
35
+ return <Panel {...props} {...props._panel}>
32
36
  <WhichGrid {...props} />
33
37
  </Panel>;
34
38
  }
@@ -0,0 +1,94 @@
1
+ import React, { useState, useEffect, } from 'react';
2
+ import {
3
+ Column,
4
+ Row,
5
+ Text,
6
+ } from 'native-base';
7
+ import IconButton from '../Buttons/IconButton';
8
+ import FullWidth from '../Icons/FullWidth';
9
+ import SideBySide from '../Icons/SideBySide';
10
+ import getSaved from '../../Functions/getSaved.js';
11
+ import setSaved from '../../Functions/setSaved.js';
12
+ import _ from 'lodash';
13
+
14
+ const
15
+ MODE_FULL = 'MODE_FULL',
16
+ MODE_SIDE = 'MODE_SIDE';
17
+
18
+ export default function ManagerScreen(props) {
19
+ const {
20
+ title,
21
+ sideModeComponent,
22
+ fullModeComponent,
23
+ id,
24
+ } = props,
25
+ [isReady, setIsReady] = useState(false),
26
+ [mode, setModeRaw] = useState(MODE_FULL),
27
+ setMode = (newMode) => {
28
+ if (newMode === mode) {
29
+ return; // no change
30
+ }
31
+ setModeRaw(newMode);
32
+ setSaved(id + '-mode', newMode);
33
+ };
34
+
35
+ useEffect(() => {
36
+ // Restore saved settings
37
+ (async () => {
38
+
39
+ let key, val;
40
+ key = id + '-mode';
41
+ val = await getSaved(key);
42
+ if (!_.isNil(val)) {
43
+ setMode(val);
44
+ }
45
+
46
+ if (!isReady) {
47
+ setIsReady(true);
48
+ }
49
+ })();
50
+ }, []);
51
+
52
+ if (!isReady) {
53
+ return null;
54
+ }
55
+
56
+ let whichComponent;
57
+ if (mode === MODE_FULL) {
58
+ whichComponent = fullModeComponent;
59
+ } else if (mode === MODE_SIDE) {
60
+ whichComponent = sideModeComponent;
61
+ }
62
+
63
+ return <Column flex={1} w="100%">
64
+ <Row
65
+ h="80px"
66
+ py={2}
67
+ borderBottomWidth={2}
68
+ borderBottomColor="#ccc"
69
+ >
70
+ <Text p={4} fontSize="26" fontWeight={700}>{title}</Text>
71
+ <IconButton
72
+ icon={FullWidth}
73
+ _icon={{
74
+ size: '30px',
75
+ color: mode === MODE_FULL ? 'primary.100' : '#000',
76
+ }}
77
+ disabled={mode === MODE_FULL}
78
+ onPress={() => setMode(MODE_FULL)}
79
+ tooltip="Full Width"
80
+ />
81
+ <IconButton
82
+ icon={SideBySide}
83
+ _icon={{
84
+ size: '30px',
85
+ color: mode === MODE_SIDE ? 'primary.100' : '#000',
86
+ }}
87
+ disabled={mode === MODE_SIDE}
88
+ onPress={() => setMode(MODE_SIDE)}
89
+ tooltip="Side Editor"
90
+ />
91
+ </Row>
92
+ {whichComponent}
93
+ </Column>;
94
+ }
@@ -32,7 +32,6 @@ export default function TabBar(props) {
32
32
  startsCollapsed = true,
33
33
  onChangeCurrentTab,
34
34
  onChangeIsCollapsed,
35
- saveCurrentTab = true,
36
35
  ...propsToPass
37
36
  } = props,
38
37
  styles = UiGlobals.styles,
@@ -55,15 +54,16 @@ export default function TabBar(props) {
55
54
  return currentTabIx;
56
55
  },
57
56
  setCurrentTab = (ix) => {
57
+ if ((useLocal && ix === currentTabIxLocal) || ix === currentTabIx) {
58
+ return; // no change
59
+ }
58
60
  if (useLocal) {
59
61
  setCurrentTabIxLocal(ix);
62
+ setSaved(id + '-currentTabIx', ix);
60
63
  }
61
64
  if (onChangeCurrentTab) {
62
65
  onChangeCurrentTab(ix);
63
66
  }
64
- if (saveCurrentTab) {
65
- setSaved(id + '-currentTabIx', ix);
66
- }
67
67
  },
68
68
  onToggleCollapse = () => {
69
69
  setIsCollapsed(!isCollapsed);
@@ -310,7 +310,7 @@ export default function TabBar(props) {
310
310
  setIsCollapsed(val);
311
311
  }
312
312
 
313
- if (saveCurrentTab) {
313
+ if (useLocal) {
314
314
  key = id + '-currentTabIx';
315
315
  val = await getSaved(key);
316
316
  if (!_.isNil(val)) {
@@ -63,6 +63,8 @@ const defaults = {
63
63
  GRID_ROW_HOVER_BG: 'hover',
64
64
  GRID_ROW_SELECTED_BG: 'selected',
65
65
  GRID_ROW_SELECTED_HOVER_BG: 'selectedHover',
66
+ GRID_BORDER_WIDTH: 1,
67
+ GRID_BORDER_COLOR: 'trueGray.300',
66
68
  ICON_BUTTON_BG: 'trueGray.200:alpha.0',
67
69
  ICON_BUTTON_BG_DISABLED: 'trueGray.200',
68
70
  ICON_BUTTON_BG_HOVER: '#000:alpha.20',
@@ -0,0 +1,40 @@
1
+ import { useSelector, useDispatch } from 'react-redux'
2
+ import _ from 'lodash';
3
+
4
+
5
+ // Usage example:
6
+ // const [
7
+ // {
8
+ // user,
9
+ // isLoading,
10
+ // },
11
+ // dispatch
12
+ // ] = useRedux([
13
+ // 'user',
14
+ // 'isLoading',
15
+ // ]);
16
+
17
+
18
+ export default function useRedux(properties) {
19
+ let values = {};
20
+ _.forEach(properties, (property) => {
21
+ values[property] = useSelector((state) => getPropertyFromState(property, state));
22
+ });
23
+
24
+ return [
25
+ values,
26
+ useDispatch(),
27
+ ];
28
+ };
29
+
30
+ function getPropertyFromState(property, state) {
31
+ let found;
32
+ const reducers = _.keys(state);
33
+ _.each(reducers, (reducer) => {
34
+ if (state[reducer].hasOwnProperty(property)) {
35
+ found = state[reducer][property];
36
+ return false; // break
37
+ }
38
+ });
39
+ return found;
40
+ }