@onehat/ui 0.2.51 → 0.2.53

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.51",
3
+ "version": "0.2.53",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -19,7 +19,7 @@
19
19
  },
20
20
  "license": "UNLICENSED",
21
21
  "dependencies": {
22
- "@onehat/data": "^1.16.0",
22
+ "@onehat/data": "^1.16.9",
23
23
  "@hookform/resolvers": "^2.9.11",
24
24
  "ckeditor5-custom-build": "file:ckeditor5",
25
25
  "js-cookie": "^3.0.1",
@@ -1,4 +1,4 @@
1
- import React, { useState, } from 'react';
1
+ import React, { useState, useEffect, useId, } from 'react';
2
2
  import {
3
3
  Column,
4
4
  Row,
@@ -12,6 +12,8 @@ import {
12
12
  UI_MODE_REACT_NATIVE,
13
13
  CURRENT_MODE,
14
14
  } from '../../Constants/UiModes.js';
15
+ import getSaved from '../../Functions/getSaved.js';
16
+ import setSaved from '../../Functions/setSaved.js';
15
17
  import Splitter from './Splitter.js';
16
18
 
17
19
  export default function Container(props) {
@@ -31,15 +33,49 @@ export default function Container(props) {
31
33
  isWestCollapsed,
32
34
  setIsWestCollapsed,
33
35
  } = props,
36
+ id = useId(),
34
37
  canResize = CURRENT_MODE === UI_MODE_WEB,
35
- [localIsNorthCollapsed, setLocalIsNorthCollapsed] = useState(north ? north.props.startsCollapsed : false),
36
- [localIsSouthCollapsed, setLocalIsSouthCollapsed] = useState(south ? south.props.startsCollapsed : false),
37
- [localIsEastCollapsed, setLocalIsEastCollapsed] = useState(east ? east.props.startsCollapsed : false),
38
- [localIsWestCollapsed, setLocalIsWestCollapsed] = useState(west ? west.props.startsCollapsed : false),
39
- [northHeight, setNorthHeight] = useState(north ? north.props.h : 0),
40
- [southHeight, setSouthHeight] = useState(south ? south.props.h : 0),
41
- [eastWidth, setEastWidth] = useState(east ? east.props.w : 0),
42
- [westWidth, setWestWidth] = useState(west ? west.props.w : 0),
38
+ [isReady, setIsReady] = useState(false),
39
+ [localIsNorthCollapsed, setLocalIsNorthCollapsedRaw] = useState(north ? north.props.startsCollapsed : false),
40
+ [localIsSouthCollapsed, setLocalIsSouthCollapsedRaw] = useState(south ? south.props.startsCollapsed : false),
41
+ [localIsEastCollapsed, setLocalIsEastCollapsedRaw] = useState(east ? east.props.startsCollapsed : false),
42
+ [localIsWestCollapsed, setLocalIsWestCollapsedRaw] = useState(west ? west.props.startsCollapsed : false),
43
+ [northHeight, setNorthHeightRaw] = useState(north ? north.props.h : 0),
44
+ [southHeight, setSouthHeightRaw] = useState(south ? south.props.h : 0),
45
+ [eastWidth, setEastWidthRaw] = useState(east ? east.props.w : 0),
46
+ [westWidth, setWestWidthRaw] = useState(west ? west.props.w : 0),
47
+ setLocalIsNorthCollapsed = (bool) => {
48
+ setLocalIsNorthCollapsedRaw(bool);
49
+ setSaved(id + '-localIsNorthCollapsed', bool);
50
+ },
51
+ setLocalIsSouthCollapsed = (bool) => {
52
+ setLocalIsSouthCollapsedRaw(bool);
53
+ setSaved(id + '-localIsSouthCollapsed', bool);
54
+ },
55
+ setLocalIsEastCollapsed = (bool) => {
56
+ setLocalIsEastCollapsedRaw(bool);
57
+ setSaved(id + '-localIsEastCollapsed', bool);
58
+ },
59
+ setLocalIsWestCollapsed = (bool) => {
60
+ setLocalIsWestCollapsedRaw(bool);
61
+ setSaved(id + '-localIsWestCollapsed', bool);
62
+ },
63
+ setNorthHeight = (height) => {
64
+ setNorthHeightRaw(height);
65
+ setSaved(id + '-northHeight', height);
66
+ },
67
+ setSouthHeight = (height) => {
68
+ setSouthHeightRaw(height);
69
+ setSaved(id + '-southHeight', height);
70
+ },
71
+ setEastWidth = (width) => {
72
+ setEastWidthRaw(width);
73
+ setSaved(id + '-eastWidth', width);
74
+ },
75
+ setWestWidth = (width) => {
76
+ setWestWidthRaw(width);
77
+ setSaved(id + '-westWidth', width);
78
+ },
43
79
  onNorthResize = (delta) => {
44
80
  const newHeight = northHeight + delta;
45
81
  setNorthHeight(newHeight);
@@ -56,6 +92,68 @@ export default function Container(props) {
56
92
  const newWidth = westWidth + delta;
57
93
  setWestWidth(newWidth);
58
94
  };
95
+
96
+ useEffect(() => {
97
+ // Restore saved settings
98
+ (async () => {
99
+ let key, val;
100
+ key = id + '-localIsNorthCollapsed';
101
+ val = await getSaved(key);
102
+ if (!_.isNil(val)) {
103
+ setLocalIsNorthCollapsedRaw(val);
104
+ }
105
+
106
+ key = id + '-localIsSouthCollapsed';
107
+ val = await getSaved(key);
108
+ if (!_.isNil(val)) {
109
+ setLocalIsSouthCollapsedRaw(val);
110
+ }
111
+
112
+ key = id + '-localIsEastCollapsed';
113
+ val = await getSaved(key);
114
+ if (!_.isNil(val)) {
115
+ setLocalIsEastCollapsedRaw(val);
116
+ }
117
+
118
+ key = id + '-localIsWestCollapsed';
119
+ val = await getSaved(key);
120
+ if (!_.isNil(val)) {
121
+ setLocalIsWestCollapsedRaw(val);
122
+ }
123
+
124
+ key = id + '-northHeight';
125
+ val = await getSaved(key);
126
+ if (!_.isNil(val)) {
127
+ setNorthHeightRaw(val);
128
+ }
129
+
130
+ key = id + '-southHeight';
131
+ val = await getSaved(key);
132
+ if (!_.isNil(val)) {
133
+ setSouthHeightRaw(val);
134
+ }
135
+
136
+ key = id + '-eastWidth';
137
+ val = await getSaved(key);
138
+ if (!_.isNil(val)) {
139
+ setEastWidthRaw(val);
140
+ }
141
+
142
+ key = id + '-westWidth';
143
+ val = await getSaved(key);
144
+ if (!_.isNil(val)) {
145
+ setWestWidthRaw(val);
146
+ }
147
+
148
+ if (!isReady) {
149
+ setIsReady(true);
150
+ }
151
+ })();
152
+ }, []);
153
+
154
+ if (!isReady) {
155
+ return null;
156
+ }
59
157
 
60
158
  let componentProps = {},
61
159
  centerComponent = null,
@@ -1,4 +1,4 @@
1
- import React, { useState, } from 'react';
1
+ import React, { useState, useEffect, useId, } from 'react';
2
2
  import {
3
3
  Button,
4
4
  Column,
@@ -16,6 +16,8 @@ import IconButton from '../Buttons/IconButton.js';
16
16
  import Minimize from '../Icons/Minimize.js';
17
17
  import Maximize from '../Icons/Maximize.js';
18
18
  import Panel from './Panel.js';
19
+ import getSaved from '../../Functions/getSaved.js';
20
+ import setSaved from '../../Functions/setSaved.js';
19
21
  import _ from 'lodash';
20
22
 
21
23
 
@@ -30,9 +32,12 @@ export default function TabPanel(props) {
30
32
  startsCollapsed = true,
31
33
  onChangeCurrentTab,
32
34
  onChangeIsCollapsed,
35
+ saveCurrentTab = true,
33
36
  ...propsToPass
34
37
  } = props,
35
38
  styles = UiGlobals.styles,
39
+ id = useId(),
40
+ [isReady, setIsReady] = useState(false),
36
41
  [currentTab, setCurrentTabRaw] = useState(initialTab),
37
42
  [isCollapsed, setIsCollapsedRaw] = useState(startsCollapsed),
38
43
  setIsCollapsed = (isCollapsed) => {
@@ -40,22 +45,25 @@ export default function TabPanel(props) {
40
45
  if (onChangeIsCollapsed) {
41
46
  onChangeIsCollapsed(isCollapsed);
42
47
  }
48
+ if (setSaved) {
49
+ setSaved(id + '-isCollapsed', isCollapsed);
50
+ }
43
51
  },
44
52
  setCurrentTab = (ix) => {
45
53
  setCurrentTabRaw(ix);
46
54
  if (onChangeCurrentTab) {
47
55
  onChangeCurrentTab(ix);
48
56
  }
57
+ if (setSaved && saveCurrentTab) {
58
+ setSaved(id + '-currentTab', ix);
59
+ }
49
60
  },
50
61
  getButtonProps = () => {
51
62
  const
52
63
  iconProps = {
53
64
  size: 'md',
54
65
  },
55
- textProps = {
56
- ml: '-8px',
57
- mr: '8px',
58
- },
66
+ textProps = {},
59
67
  buttonProps = {
60
68
  bg: styles.TAB_BG,
61
69
  color: styles.TAB_COLOR,
@@ -279,54 +287,81 @@ export default function TabPanel(props) {
279
287
  onToggleCollapse = () => {
280
288
  setIsCollapsed(!isCollapsed);
281
289
  };
282
- if (direction === VERTICAL) {
283
- return <Panel {...propsToPass}>
284
- <Row flex={1} w="100%">
285
- <Column
286
- alignItems="center"
287
- justifyContent="flex-start"
288
- py={2}
289
- pl={isCollapsed ? 1 : 4}
290
- bg={styles.TAB_BAR_BG}
291
- w={isCollapsed ? '50px' : tabWidth}
292
- >
293
- {renderTabs()}
294
- <Column flex={1} w="100%" justifyContent="flex-end">
295
- {renderToggleButton()}
296
- </Column>
297
- </Column>
298
- <Column
299
- alignItems="center"
300
- justifyContent="flex-start"
301
- flex={1}
302
- >
303
- {renderCurrentTabContent()}
304
- </Column>
305
- </Row>
306
- </Panel>;
307
- }
308
290
 
309
- // HORIZONTAL
310
- return <Panel flex={1} w="100%" {...propsToPass} {...props._panel}>
311
- <Column flex={1} w="100%">
312
- <Row
291
+ useEffect(() => {
292
+ // Restore saved settings
293
+ (async () => {
294
+ let key, val;
295
+ key = id + '-isCollapsed';
296
+ val = await getSaved(key);
297
+ if (!_.isNil(val)) {
298
+ setIsCollapsed(val);
299
+ }
300
+
301
+ key = id + '-currentTab';
302
+ val = await getSaved(key);
303
+ if (!_.isNil(val)) {
304
+ setCurrentTab(val);
305
+ }
306
+
307
+ if (!isReady) {
308
+ setIsReady(true);
309
+ }
310
+ })();
311
+ }, []);
312
+
313
+ if (!isReady) {
314
+ return null;
315
+ }
316
+
317
+ if (direction === VERTICAL) {
318
+ return <Panel {...propsToPass}>
319
+ <Row flex={1} w="100%">
320
+ <Column
313
321
  alignItems="center"
314
322
  justifyContent="flex-start"
315
- p={2}
316
- pb={0}
323
+ py={2}
324
+ pl={isCollapsed ? 1 : 4}
317
325
  bg={styles.TAB_BAR_BG}
318
- h={isCollapsed ? '30px' : tabHeight}
326
+ w={isCollapsed ? '50px' : tabWidth}
319
327
  >
320
328
  {renderTabs()}
321
- <Row flex={1} h="100%" justifyContent="flex-end">
322
- <Row h="100%">
323
- {renderToggleButton()}
324
- </Row>
325
- </Row>
326
- </Row>
327
- <Row flex={1}>
329
+ <Column flex={1} w="100%" justifyContent="flex-end">
330
+ {renderToggleButton()}
331
+ </Column>
332
+ </Column>
333
+ <Column
334
+ alignItems="center"
335
+ justifyContent="flex-start"
336
+ flex={1}
337
+ >
328
338
  {renderCurrentTabContent()}
329
- </Row>
330
- </Column>
339
+ </Column>
340
+ </Row>
331
341
  </Panel>;
342
+ }
343
+
344
+ // HORIZONTAL
345
+ return <Panel flex={1} w="100%" {...propsToPass} {...props._panel}>
346
+ <Column flex={1} w="100%">
347
+ <Row
348
+ alignItems="center"
349
+ justifyContent="flex-start"
350
+ p={2}
351
+ pb={0}
352
+ bg={styles.TAB_BAR_BG}
353
+ h={isCollapsed ? '30px' : tabHeight}
354
+ >
355
+ {renderTabs()}
356
+ <Row flex={1} h="100%" justifyContent="flex-end">
357
+ <Row h="100%">
358
+ {renderToggleButton()}
359
+ </Row>
360
+ </Row>
361
+ </Row>
362
+ <Row flex={1}>
363
+ {renderCurrentTabContent()}
364
+ </Row>
365
+ </Column>
366
+ </Panel>;
332
367
  }
@@ -1,4 +1,4 @@
1
- import React, { useState, useMemo, } from 'react';
1
+ import React, { useState, useEffect, useMemo, useId, } from 'react';
2
2
  import {
3
3
  HORIZONTAL,
4
4
  VERTICAL,
@@ -12,6 +12,8 @@ import Container from '../Container/Container.js';
12
12
  import Panel from '../Panel/Panel.js';
13
13
  import TabPanel from '../Panel/TabPanel.js';
14
14
  import UploadDownload from '../Panel/UploadDownload.js';
15
+ import getSaved from '../../Functions/getSaved.js';
16
+ import setSaved from '../../Functions/setSaved.js';
15
17
  import _ from 'lodash';
16
18
 
17
19
  export default function DataMgt(props) {
@@ -40,12 +42,26 @@ export default function DataMgt(props) {
40
42
  } = props;
41
43
 
42
44
  const
45
+ id = useId(),
43
46
  // westRef = useRef(),
44
- [isWestCollapsed, setIsWestCollapsed] = useState(westStartsCollapsed),
45
- [isEastCollapsed, setIsEastCollapsed] = useState(eastStartsCollapsed),
46
- [isFullscreen, setIsFullscreen] = useState(false),
47
+ [isReady, setIsReady] = useState(false),
48
+ [isWestCollapsed, setIsWestCollapsedRaw] = useState(westStartsCollapsed),
49
+ [isEastCollapsed, setIsEastCollapsedRaw] = useState(eastStartsCollapsed),
50
+ [isFullscreen, setIsFullscreenRaw] = useState(false),
47
51
  [westSelected, setWestSelectedRaw] = useState(),
48
52
  [centerSelected, setCenterSelected] = useState(),
53
+ setIsWestCollapsed = (bool) => {
54
+ setIsWestCollapsedRaw(bool);
55
+ setSaved(id + '-isWestCollapsed', bool);
56
+ },
57
+ setIsEastCollapsed = (bool) => {
58
+ setIsEastCollapsedRaw(bool);
59
+ setSaved(id + '-isEastCollapsed', bool);
60
+ },
61
+ setIsFullscreen = (bool) => {
62
+ setIsFullscreenRaw(bool);
63
+ setSaved(id + '-isFullscreen', isFullscreen);
64
+ },
49
65
  setWestSelected = (selected) => {
50
66
  setWestSelectedRaw(selected);
51
67
  setCenterSelected(); // clear selection in center
@@ -85,6 +101,51 @@ export default function DataMgt(props) {
85
101
  }
86
102
  };
87
103
 
104
+ useEffect(() => {
105
+ if (!getSaved) {
106
+ setIsReady(true);
107
+ return () => {};
108
+ }
109
+
110
+ // Restore saved settings
111
+ (async () => {
112
+
113
+ let key, val;
114
+ key = id + '-isWestCollapsed';
115
+ val = await getSaved(key);
116
+ if (!_.isNil(val)) {
117
+ setIsWestCollapsedRaw(val);
118
+ }
119
+
120
+ key = id + '-isEastCollapsed';
121
+ val = await getSaved(key);
122
+ if (!_.isNil(val)) {
123
+ setIsEastCollapsedRaw(val);
124
+ }
125
+
126
+ key = id + '-isFullscreen';
127
+ val = await getSaved(key);
128
+ if (!_.isNil(val)) {
129
+ setIsFullscreenRaw(val);
130
+ }
131
+
132
+ key = id + '-westSelected';
133
+ val = await getSaved(key);
134
+ if (!_.isNil(val)) {
135
+ setWestSelectedRaw(val);
136
+ }
137
+
138
+ key = id + '-centerSelected';
139
+ val = await getSaved(key);
140
+ if (!_.isNil(val)) {
141
+ setCenterSelectedRaw(val);
142
+ }
143
+
144
+ if (!isReady) {
145
+ setIsReady(true);
146
+ }
147
+ })();
148
+ }, []);
88
149
 
89
150
  // REGIONS -------------------------------------------------------
90
151
  // [ ] [ ] [ ]
@@ -238,6 +299,10 @@ export default function DataMgt(props) {
238
299
  }
239
300
  }
240
301
 
302
+ if (!isReady) {
303
+ return null;
304
+ }
305
+
241
306
  return <Container
242
307
  west={west}
243
308
  isWestCollapsed={isWestCollapsed}
@@ -246,5 +311,7 @@ export default function DataMgt(props) {
246
311
  east={east}
247
312
  isEastCollapsed={isEastCollapsed}
248
313
  setIsEastCollapsed={setIsEastCollapsed}
314
+ getSaved={getSaved}
315
+ setSaved={setSaved}
249
316
  />;
250
317
  }
@@ -0,0 +1,10 @@
1
+ import oneHatData from '@onehat/data';
2
+ import UiGlobals from '../UiGlobals.js';
3
+
4
+ export default async function deleteSaved(key) {
5
+ const Repo = oneHatData.getRepository(UiGlobals.uiSavesRepo);
6
+ if (!Repo) {
7
+ return null;
8
+ }
9
+ await Repo.deleteById(key);
10
+ }
@@ -0,0 +1,38 @@
1
+ import oneHatData from '@onehat/data';
2
+ import UiGlobals from '../UiGlobals.js';
3
+ import _ from 'lodash';
4
+
5
+ export default async function getSaved(key) {
6
+ const
7
+ Repo = oneHatData.getRepository(UiGlobals.uiSavesRepo),
8
+ entity = Repo?.getById(key);
9
+
10
+ if (!entity) {
11
+ return null;
12
+ }
13
+
14
+ let value = entity.value;
15
+
16
+ if (entity.isJson) {
17
+ value = JSON.parse(value);
18
+ if (entity.isOneBuild) {
19
+ // Convert the data to an actual entity (or entities) of the correct type
20
+ const
21
+ Repository = oneHatData.getRepository(entity.model),
22
+ entities = [];
23
+ let i, data, entity;
24
+ if (_.isArray(value)) {
25
+ for (i = 0; i = value.length; i++) {
26
+ data = value[i];
27
+ entity = await Repository.createStandaloneEntity(data);
28
+ entities.push(entity);
29
+ }
30
+ value = entities;
31
+ } else {
32
+ value = await Repository.createStandaloneEntity(value);
33
+ }
34
+
35
+ }
36
+ }
37
+ return value;
38
+ }
@@ -0,0 +1,56 @@
1
+ import oneHatData from '@onehat/data';
2
+ import UiGlobals from '../UiGlobals.js';
3
+ import _ from 'lodash';
4
+
5
+ export default async function setSaved(key, value) {
6
+ const
7
+ Repo = oneHatData.getRepository(UiGlobals.uiSavesRepo),
8
+ entity = Repo?.getById(key);
9
+
10
+ if (!entity) {
11
+ return null;
12
+ }
13
+
14
+ let isOneBuild = false,
15
+ isJson = false,
16
+ model = null;
17
+ if (!_.isNil(value) && typeof value !== 'string') {
18
+ if (_.isArray(value)) {
19
+ const objects = value;
20
+ if (objects[0]?.getDataForNewEntity) {
21
+ model = objects[0].repository.name;
22
+ isOneBuild = true;
23
+ }
24
+ const rawValues = [];
25
+ _.each(objects, (obj) => {
26
+ rawValues.push(isOneBuild ? obj.getDataForNewEntity() : obj);
27
+ });
28
+ value = JSON.stringify(rawValues);
29
+ } else {
30
+ if (value.getDataForNewEntity) {
31
+ model = value.repository.name;
32
+ value = value.getDataForNewEntity();
33
+ isOneBuild = true;
34
+ }
35
+ value = JSON.stringify(value);
36
+ }
37
+ isJson = true;
38
+ }
39
+ if (entity) {
40
+ entity.setValues({
41
+ value,
42
+ isOneBuild,
43
+ isJson,
44
+ model,
45
+ });
46
+ await Repo.save(entity);
47
+ } else {
48
+ await Repo.add({
49
+ key,
50
+ value,
51
+ isOneBuild,
52
+ isJson,
53
+ model,
54
+ });
55
+ }
56
+ }
@@ -0,0 +1,5 @@
1
+ import UiGlobals from '../UiGlobals.js';
2
+
3
+ export default function setUiSavesRepo(name) {
4
+ UiGlobals.uiSavesRepo = name;
5
+ }