@onehat/ui 0.2.50 → 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 +2 -2
- package/src/Components/Buttons/IconButton.js +2 -1
- package/src/Components/Container/Container.js +107 -9
- package/src/Components/Form/Field/Date.js +1 -0
- package/src/Components/Hoc/withFilters.js +93 -65
- package/src/Components/Panel/TabPanel.js +82 -47
- package/src/Components/Screens/DataMgt.js +71 -4
- package/src/Constants/Styles.js +1 -0
- package/src/Functions/deleteSaved.js +10 -0
- package/src/Functions/getSaved.js +38 -0
- package/src/Functions/setSaved.js +56 -0
- package/src/Functions/setUiSavesRepo.js +5 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onehat/ui",
|
|
3
|
-
"version": "0.2.
|
|
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.
|
|
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",
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
Spinner,
|
|
6
6
|
Tooltip,
|
|
7
7
|
} from 'native-base';
|
|
8
|
+
import styles from '../../Constants/Styles.js';
|
|
8
9
|
|
|
9
10
|
const IconButton = React.forwardRef((props, ref) => {
|
|
10
11
|
const {
|
|
@@ -29,7 +30,7 @@ const IconButton = React.forwardRef((props, ref) => {
|
|
|
29
30
|
alignItems="center"
|
|
30
31
|
p={2}
|
|
31
32
|
_disabled={{
|
|
32
|
-
bg:
|
|
33
|
+
bg: styles.ICON_BUTTON_BG_DISABLED,
|
|
33
34
|
}}
|
|
34
35
|
{...props}
|
|
35
36
|
>
|
|
@@ -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
|
-
[
|
|
36
|
-
[
|
|
37
|
-
[
|
|
38
|
-
[
|
|
39
|
-
[
|
|
40
|
-
[
|
|
41
|
-
[
|
|
42
|
-
[
|
|
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 { useState, useEffect, } from 'react';
|
|
1
|
+
import { useState, useEffect, useId, } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Column,
|
|
4
4
|
Modal,
|
|
@@ -34,6 +34,8 @@ export default function withFilters(WrappedComponent) {
|
|
|
34
34
|
customFilters = [], // of shape: { title, type, field, value, getRepoFilters(value) }
|
|
35
35
|
minFilters = 3,
|
|
36
36
|
maxFilters = 6,
|
|
37
|
+
getSaved,
|
|
38
|
+
setSaved,
|
|
37
39
|
|
|
38
40
|
// withData
|
|
39
41
|
Repository,
|
|
@@ -54,6 +56,7 @@ export default function withFilters(WrappedComponent) {
|
|
|
54
56
|
excludeFields: modelExcludeFields,
|
|
55
57
|
filteringDisabled: modelFilteringDisabled,
|
|
56
58
|
} = Repository.getSchema().model,
|
|
59
|
+
id = useId(),
|
|
57
60
|
|
|
58
61
|
// determine the starting filters
|
|
59
62
|
startingFilters = !_.isEmpty(customFilters) ? customFilters : // custom filters override component filters
|
|
@@ -103,7 +106,7 @@ export default function withFilters(WrappedComponent) {
|
|
|
103
106
|
}
|
|
104
107
|
|
|
105
108
|
const
|
|
106
|
-
[filters,
|
|
109
|
+
[filters, setFiltersRaw] = useState(formattedStartingFilters), // array of formatted filters
|
|
107
110
|
[slots, setSlots] = useState(startingSlots), // array of field names user is currently filtering on; blank slots have a null entry in array
|
|
108
111
|
[modalFilters, setModalFilters] = useState([]),
|
|
109
112
|
[modalSlots, setModalSlots] = useState([]),
|
|
@@ -144,6 +147,29 @@ export default function withFilters(WrappedComponent) {
|
|
|
144
147
|
setModalFilters(newFilters);
|
|
145
148
|
setModalSlots(newSlots);
|
|
146
149
|
},
|
|
150
|
+
setFilters = (filters, doSetSlots = true, save = true) => {
|
|
151
|
+
setFiltersRaw(filters);
|
|
152
|
+
|
|
153
|
+
if (doSetSlots) {
|
|
154
|
+
const newSlots = [];
|
|
155
|
+
_.each(filters, (filter, ix) => {
|
|
156
|
+
if (searchAllText && ix === 0) {
|
|
157
|
+
return; // skip
|
|
158
|
+
}
|
|
159
|
+
newSlots.push(filter.field);
|
|
160
|
+
});
|
|
161
|
+
if (newSlots.length < minFilters) {
|
|
162
|
+
// Add more slots until we get to minFilters
|
|
163
|
+
for(let i = newSlots.length; i < minFilters; i++) {
|
|
164
|
+
newSlots.push(null);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
setSlots(newSlots);
|
|
168
|
+
}
|
|
169
|
+
if (save && setSaved) {
|
|
170
|
+
setSaved(id + '-filters', filters);
|
|
171
|
+
}
|
|
172
|
+
},
|
|
147
173
|
onFilterChangeValue = (field, value) => {
|
|
148
174
|
// handler for when a filter value changes
|
|
149
175
|
const newFilters = [];
|
|
@@ -153,7 +179,7 @@ export default function withFilters(WrappedComponent) {
|
|
|
153
179
|
}
|
|
154
180
|
newFilters.push(filter);
|
|
155
181
|
});
|
|
156
|
-
setFilters(newFilters);
|
|
182
|
+
setFilters(newFilters, false);
|
|
157
183
|
},
|
|
158
184
|
onClearFilters = () => {
|
|
159
185
|
// Clears values for all active filters
|
|
@@ -162,7 +188,7 @@ export default function withFilters(WrappedComponent) {
|
|
|
162
188
|
filter.value = null;
|
|
163
189
|
newFilters.push(filter);
|
|
164
190
|
});
|
|
165
|
-
setFilters(newFilters);
|
|
191
|
+
setFilters(newFilters, false);
|
|
166
192
|
},
|
|
167
193
|
getFilterByField = (field) => {
|
|
168
194
|
return _.find(filters, (filter) => {
|
|
@@ -240,61 +266,73 @@ export default function withFilters(WrappedComponent) {
|
|
|
240
266
|
};
|
|
241
267
|
|
|
242
268
|
useEffect(() => {
|
|
243
|
-
|
|
244
|
-
|
|
269
|
+
(async () => {
|
|
270
|
+
|
|
271
|
+
// Whenever the filters change in some way, make repository conform to these new filters
|
|
272
|
+
const newRepoFilters = [];
|
|
273
|
+
let filtersToUse = filters
|
|
274
|
+
|
|
275
|
+
if (!isReady && getSaved) {
|
|
276
|
+
const savedFilters = await getSaved(id + '-filters');
|
|
277
|
+
if (!_.isEmpty(savedFilters)) {
|
|
278
|
+
// load saved filters
|
|
279
|
+
filtersToUse = savedFilters;
|
|
280
|
+
setFilters(savedFilters, true, false); // false to skip save
|
|
281
|
+
}
|
|
282
|
+
}
|
|
245
283
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
284
|
+
if (isUsingCustomFilters) {
|
|
285
|
+
_.each(filtersToUse, (filter) => {
|
|
286
|
+
const repoFiltersFromFilter = filter.getRepoFilters(value);
|
|
287
|
+
_.each(repoFiltersFromFilter, (repoFilter) => { // one custom filter might generate multiple filters for the repository
|
|
288
|
+
newRepoFilters.push(repoFilter);
|
|
289
|
+
});
|
|
251
290
|
});
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
291
|
+
} else {
|
|
292
|
+
const newFilterNames = [];
|
|
293
|
+
_.each(filtersToUse, (filter) => {
|
|
294
|
+
const {
|
|
295
|
+
field,
|
|
296
|
+
value,
|
|
297
|
+
} = filter,
|
|
298
|
+
isFilterRange = getIsFilterRange(field);
|
|
299
|
+
if (isFilterRange) {
|
|
300
|
+
if (!!value) {
|
|
301
|
+
const
|
|
302
|
+
highField = field + ' <=',
|
|
303
|
+
lowField = field + ' >=',
|
|
304
|
+
highValue = value.high,
|
|
305
|
+
lowValue = value.low;
|
|
306
|
+
newFilterNames.push(highField);
|
|
307
|
+
newFilterNames.push(lowField);
|
|
308
|
+
newRepoFilters.push({ name: highField, value: highValue, });
|
|
309
|
+
newRepoFilters.push({ name: lowField, value: lowValue, });
|
|
310
|
+
}
|
|
311
|
+
} else {
|
|
312
|
+
newFilterNames.push(field);
|
|
313
|
+
newRepoFilters.push({ name: field, value, });
|
|
272
314
|
}
|
|
273
|
-
}
|
|
274
|
-
newFilterNames.push(field);
|
|
275
|
-
newRepoFilters.push({ name: field, value, });
|
|
276
|
-
}
|
|
277
|
-
});
|
|
315
|
+
});
|
|
278
316
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
317
|
+
// Go through previousFilterNames and see if any are no longer used.
|
|
318
|
+
_.each(previousFilterNames, (name) => {
|
|
319
|
+
if (!inArray(name, newFilterNames)) {
|
|
320
|
+
newRepoFilters.push({ name, value: null, }); // no longer used, so set it to null so it'll be deleted
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
setPreviousFilterNames(newFilterNames);
|
|
324
|
+
}
|
|
287
325
|
|
|
288
|
-
|
|
326
|
+
Repository.filter(newRepoFilters, null, false); // false so other filters remain
|
|
289
327
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
if (!isReady) {
|
|
295
|
-
setIsReady(true);
|
|
296
|
-
}
|
|
328
|
+
if (searchAllText && Repository.searchAncillary && !Repository.hasBaseParam('searchAncillary')) {
|
|
329
|
+
Repository.setBaseParam('searchAncillary', true);
|
|
330
|
+
}
|
|
297
331
|
|
|
332
|
+
if (!isReady) {
|
|
333
|
+
setIsReady(true);
|
|
334
|
+
}
|
|
335
|
+
})();
|
|
298
336
|
}, [filters]);
|
|
299
337
|
|
|
300
338
|
if (!isReady) {
|
|
@@ -324,6 +362,8 @@ export default function withFilters(WrappedComponent) {
|
|
|
324
362
|
}}
|
|
325
363
|
ml={1}
|
|
326
364
|
onPress={() => {
|
|
365
|
+
const f = filters;
|
|
366
|
+
const s = slots;
|
|
327
367
|
setModalFilters(filters);
|
|
328
368
|
setModalSlots(slots);
|
|
329
369
|
setIsFilterSelectorShown(true);
|
|
@@ -420,9 +460,7 @@ export default function withFilters(WrappedComponent) {
|
|
|
420
460
|
onSave={(data, e) => {
|
|
421
461
|
// Conform filters to this new choice of filters
|
|
422
462
|
|
|
423
|
-
const
|
|
424
|
-
newFilters = [],
|
|
425
|
-
newSlots = [];
|
|
463
|
+
const newFilters = [];
|
|
426
464
|
|
|
427
465
|
if (searchAllText) {
|
|
428
466
|
newFilters.push(filters[0]);
|
|
@@ -435,20 +473,10 @@ export default function withFilters(WrappedComponent) {
|
|
|
435
473
|
}
|
|
436
474
|
|
|
437
475
|
const newFilter = getFormattedFilter(field);
|
|
438
|
-
|
|
439
476
|
newFilters.push(newFilter);
|
|
440
|
-
newSlots.push(field);
|
|
441
477
|
});
|
|
442
478
|
|
|
443
|
-
if (newSlots.length < minFilters) {
|
|
444
|
-
// Add more slots until we get to minFilters
|
|
445
|
-
for(let i = newSlots.length; i < minFilters; i++) {
|
|
446
|
-
newSlots.push(null);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
|
|
450
479
|
setFilters(newFilters);
|
|
451
|
-
setSlots(newSlots);
|
|
452
480
|
|
|
453
481
|
// Close the modal
|
|
454
482
|
setIsFilterSelectorShown(false);
|
|
@@ -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
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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
|
-
|
|
316
|
-
|
|
323
|
+
py={2}
|
|
324
|
+
pl={isCollapsed ? 1 : 4}
|
|
317
325
|
bg={styles.TAB_BAR_BG}
|
|
318
|
-
|
|
326
|
+
w={isCollapsed ? '50px' : tabWidth}
|
|
319
327
|
>
|
|
320
328
|
{renderTabs()}
|
|
321
|
-
<
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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
|
-
</
|
|
330
|
-
</
|
|
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
|
-
[
|
|
45
|
-
[
|
|
46
|
-
[
|
|
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
|
}
|
package/src/Constants/Styles.js
CHANGED
|
@@ -61,6 +61,7 @@ const defaults = {
|
|
|
61
61
|
GRID_TOOLBAR_ITEMS_COLOR: 'trueGray.800',
|
|
62
62
|
GRID_TOOLBAR_ITEMS_DISABLED_COLOR: 'disabled',
|
|
63
63
|
GRID_TOOLBAR_ITEMS_ICON_SIZE: 'sm',
|
|
64
|
+
ICON_BUTTON_BG_DISABLED: 'trueGray.200',
|
|
64
65
|
PANEL_FOOTER_BG: 'primary.100', // :alpha.50
|
|
65
66
|
PANEL_HEADER_BG: 'primary.100',
|
|
66
67
|
PANEL_HEADER_BG_VERTICAL: 'primary.100',
|
|
@@ -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
|
+
}
|