@onehat/ui 0.2.81 → 0.3.0

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.81",
3
+ "version": "0.3.0",
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.19.0",
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
@@ -287,12 +287,14 @@ function Form(props) {
287
287
  return <Element key={ix} title={title} {...defaults} {...propsToPass} {...editorTypeProps}>{children}</Element>;
288
288
  }
289
289
 
290
+ if (!label && Repository && model[name].title) {
291
+ label = model[name].title;
292
+ }
293
+
290
294
  if (isViewOnly || !isEditable) {
291
- const Text = getComponentFromType('Text');
292
- if (!label && Repository && model.titles?.[name]) {
293
- label = model.titles[name];
294
- }
295
- const value = (record && record[name]) || (startingValues && startingValues[name]) || null;
295
+ const
296
+ Text = getComponentFromType('Text'),
297
+ value = (record && record[name]) || (startingValues && startingValues[name]) || null;
296
298
  let element = <Text
297
299
  value={value}
298
300
  {...propsToPass}
@@ -307,9 +309,6 @@ function Form(props) {
307
309
  return <Row key={ix} px={2} pb={1}>{element}</Row>;
308
310
  }
309
311
 
310
- if (!label && Repository && model.titles?.[name]) {
311
- label = model.titles[name];
312
- }
313
312
 
314
313
 
315
314
  // // These rules are for fields *outside* the model
@@ -427,7 +426,7 @@ function Form(props) {
427
426
  fontWeight="bold"
428
427
  >{title}</Text>;
429
428
  }
430
- return <Column key={'ancillary-' + ix} px={2} pb={1}>{title}{element}</Column>;
429
+ return <Column key={'ancillary-' + ix} mx={2} my={5}>{title}{element}</Column>;
431
430
  });
432
431
  }
433
432
  return components;
@@ -499,7 +498,7 @@ function Form(props) {
499
498
  // for all other editor types
500
499
  formComponents = buildFromItems();
501
500
  const formAncillaryComponents = buildAncillary();
502
- editor = <ScrollView flex={1} width="100%" pb={1}>
501
+ editor = <ScrollView _web={{ height: 1 }} width="100%" pb={1}>
503
502
  <Row>{formComponents}</Row>
504
503
  <Column pt={4}>{formAncillaryComponents}</Column>
505
504
  </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
  }
@@ -1,4 +1,3 @@
1
- import { useState, } from 'react';
2
1
  import {
3
2
  EDITOR_TYPE__SIDE,
4
3
  } from '../../Constants/Editor.js';
@@ -13,9 +12,7 @@ export default function withSideEditor(WrappedComponent, isTree = false) {
13
12
  Editor,
14
13
  editorProps = {},
15
14
  sideFlex = 100,
16
- } = props,
17
- [selection, setSelection] = useState(null);
18
-
15
+ } = props;
19
16
 
20
17
  if (!Editor) {
21
18
  throw Error('Editor is not defined');
@@ -25,13 +22,13 @@ export default function withSideEditor(WrappedComponent, isTree = false) {
25
22
  center={<WrappedComponent
26
23
  isTree={isTree}
27
24
  {...props}
28
- onSelectionChange={setSelection}
29
25
  />}
30
26
  east={<Editor
27
+ {...props}
31
28
  editorType={EDITOR_TYPE__SIDE}
32
29
  flex={sideFlex}
33
- selection={selection} // This needs to be whatever the selection is in the center component
34
- // {...props}
30
+ borderLeftWidth={1}
31
+ borderLeftColor="#ccc"
35
32
  {...editorProps}
36
33
  />}
37
34
  />;
@@ -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
  }
@@ -1,4 +1,4 @@
1
- import React, { useState, useEffect, useMemo, useId, } from 'react';
1
+ import React, { useState, useEffect, } from 'react';
2
2
  import {
3
3
  Column,
4
4
  Row,
@@ -20,8 +20,8 @@ export default function ManagerScreen(props) {
20
20
  title,
21
21
  sideModeComponent,
22
22
  fullModeComponent,
23
+ id,
23
24
  } = props,
24
- id = useId(),
25
25
  [isReady, setIsReady] = useState(false),
26
26
  [mode, setModeRaw] = useState(MODE_FULL),
27
27
  setMode = (newMode) => {
@@ -64,6 +64,8 @@ export default function ManagerScreen(props) {
64
64
  <Row
65
65
  h="80px"
66
66
  py={2}
67
+ borderBottomWidth={2}
68
+ borderBottomColor="#ccc"
67
69
  >
68
70
  <Text p={4} fontSize="26" fontWeight={700}>{title}</Text>
69
71
  <IconButton
@@ -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
+ }