@onehat/ui 0.2.51 → 0.2.54
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 +3 -2
- package/src/Components/Buttons/CheckboxButton.js +15 -0
- package/src/Components/Buttons/IconButton.js +15 -4
- package/src/Components/Container/Container.js +107 -9
- package/src/Components/Form/Field/Combo/Combo.js +1 -0
- package/src/Components/Grid/Grid.js +14 -10
- package/src/Components/Hoc/withFilters.js +10 -7
- package/src/Components/Panel/TabPanel.js +80 -47
- package/src/Components/Screens/DataMgt.js +69 -4
- package/src/Components/Toolbar/Pagination.js +0 -3
- package/src/Components/Toolbar/PaginationToolbar.js +13 -2
- package/src/Constants/Styles.js +3 -0
- package/src/Functions/deleteSaved.js +10 -0
- package/src/Functions/getSaved.js +38 -0
- package/src/Functions/nbToRgb.js +63 -0
- package/src/Functions/setSaved.js +55 -0
- package/src/Functions/setThemeOverrides.js +5 -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.54",
|
|
4
4
|
"description": "Base UI for OneHat apps",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -19,8 +19,9 @@
|
|
|
19
19
|
},
|
|
20
20
|
"license": "UNLICENSED",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@onehat/data": "^1.16.
|
|
22
|
+
"@onehat/data": "^1.16.10",
|
|
23
23
|
"@hookform/resolvers": "^2.9.11",
|
|
24
|
+
"@k-renwick/colour-mixer": "^1.2.1",
|
|
24
25
|
"ckeditor5-custom-build": "file:ckeditor5",
|
|
25
26
|
"js-cookie": "^3.0.1",
|
|
26
27
|
"native-base": "^3.4.25",
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import IconButton from './IconButton.js';
|
|
2
|
+
import SquareCheck from '../Icons/SquareCheck.js';
|
|
3
|
+
import Square from '../Icons/Square.js';
|
|
4
|
+
|
|
5
|
+
export default function CheckboxButton(props) {
|
|
6
|
+
const {
|
|
7
|
+
isChecked,
|
|
8
|
+
} = props;
|
|
9
|
+
|
|
10
|
+
return <IconButton
|
|
11
|
+
icon={isChecked ? SquareCheck : Square }
|
|
12
|
+
{...props}
|
|
13
|
+
/>;
|
|
14
|
+
}
|
|
15
|
+
|
|
@@ -17,10 +17,14 @@ const IconButton = React.forwardRef((props, ref) => {
|
|
|
17
17
|
tooltipPlacement = 'bottom',
|
|
18
18
|
} = props;
|
|
19
19
|
const propsIcon = props._icon || {};
|
|
20
|
-
let icon = props.icon || <Icon {...propsIcon}
|
|
20
|
+
let icon = props.icon || <Icon {...propsIcon} />,
|
|
21
|
+
ret;
|
|
21
22
|
if (isLoading) {
|
|
22
23
|
icon = <Spinner {..._spinner} />;
|
|
23
24
|
}
|
|
25
|
+
if (!React.isValidElement(icon)) {
|
|
26
|
+
icon = <Icon as={icon} {...propsIcon} />;
|
|
27
|
+
}
|
|
24
28
|
const pressable = <Pressable
|
|
25
29
|
ref={ref}
|
|
26
30
|
borderRadius="md"
|
|
@@ -29,16 +33,23 @@ const IconButton = React.forwardRef((props, ref) => {
|
|
|
29
33
|
justifyContent="center"
|
|
30
34
|
alignItems="center"
|
|
31
35
|
p={2}
|
|
36
|
+
// bg={styles.ICON_BUTTON_BG}
|
|
37
|
+
_hover={{
|
|
38
|
+
bg: styles.ICON_BUTTON_BG_HOVER,
|
|
39
|
+
}}
|
|
32
40
|
_disabled={{
|
|
33
41
|
bg: styles.ICON_BUTTON_BG_DISABLED,
|
|
34
42
|
}}
|
|
43
|
+
_pressed={{
|
|
44
|
+
bg: styles.ICON_BUTTON_BG_PRESSED,
|
|
45
|
+
}}
|
|
35
46
|
{...props}
|
|
36
47
|
>
|
|
37
|
-
|
|
48
|
+
{icon}
|
|
38
49
|
</Pressable>;
|
|
39
|
-
|
|
50
|
+
ret = pressable;
|
|
40
51
|
if (tooltip) {
|
|
41
|
-
ret = <Tooltip label={tooltip} placement={tooltipPlacement}>{
|
|
52
|
+
ret = <Tooltip label={tooltip} placement={tooltipPlacement}>{ret}</Tooltip>;
|
|
42
53
|
}
|
|
43
54
|
return ret;
|
|
44
55
|
});
|
|
@@ -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,
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
DROP_POSITION_BEFORE,
|
|
22
22
|
DROP_POSITION_AFTER,
|
|
23
23
|
} from '../../Constants/Grid.js';
|
|
24
|
+
import * as colourMixer from '@k-renwick/colour-mixer'
|
|
24
25
|
import UiGlobals from '../../UiGlobals.js';
|
|
25
26
|
import useForceUpdate from '../../Hooks/useForceUpdate.js';
|
|
26
27
|
import withContextMenu from '../Hoc/withContextMenu.js';
|
|
@@ -35,6 +36,7 @@ import withSelection from '../Hoc/withSelection.js';
|
|
|
35
36
|
import withWindowedEditor from '../Hoc/withWindowedEditor.js';
|
|
36
37
|
import withInlineEditor from '../Hoc/withInlineEditor.js';
|
|
37
38
|
import testProps from '../../Functions/testProps.js';
|
|
39
|
+
import nbToRgb from '../../Functions/nbToRgb.js';
|
|
38
40
|
import GridHeaderRow from './GridHeaderRow.js';
|
|
39
41
|
import GridRow, { ReorderableGridRow } from './GridRow.js';
|
|
40
42
|
import IconButton from '../Buttons/IconButton.js';
|
|
@@ -45,6 +47,7 @@ import NoReorderRows from '../Icons/NoReorderRows.js';
|
|
|
45
47
|
import ReorderRows from '../Icons/ReorderRows.js';
|
|
46
48
|
import _ from 'lodash';
|
|
47
49
|
|
|
50
|
+
|
|
48
51
|
// Grid requires the use of HOC withSelection() whenever it's used.
|
|
49
52
|
// The default export is *with* the HOC. A separate *raw* component is
|
|
50
53
|
// exported which can be combined with many HOCs for various functionality.
|
|
@@ -67,7 +70,7 @@ export function Grid(props) {
|
|
|
67
70
|
columnsConfig = [], // json configurations for each column
|
|
68
71
|
|
|
69
72
|
columnProps = {},
|
|
70
|
-
getRowProps = () => {
|
|
73
|
+
getRowProps = (item) => {
|
|
71
74
|
return {
|
|
72
75
|
borderBottomWidth: 1,
|
|
73
76
|
borderBottomColor: 'trueGray.500',
|
|
@@ -348,19 +351,20 @@ export function Grid(props) {
|
|
|
348
351
|
/>;
|
|
349
352
|
}
|
|
350
353
|
|
|
351
|
-
let bg = styles.GRID_ROW_BG
|
|
354
|
+
let bg = rowProps.bg || styles.GRID_ROW_BG,
|
|
355
|
+
mixWith;
|
|
352
356
|
if (isSelected) {
|
|
353
357
|
if (showHovers && isHovered) {
|
|
354
|
-
|
|
358
|
+
mixWith = styles.GRID_ROW_SELECTED_HOVER_BG;
|
|
355
359
|
} else {
|
|
356
|
-
|
|
357
|
-
}
|
|
358
|
-
} else {
|
|
359
|
-
if (showHovers && isHovered) {
|
|
360
|
-
bg = styles.GRID_ROW_HOVER_BG;
|
|
361
|
-
} else {
|
|
362
|
-
bg = styles.GRID_ROW_BG;
|
|
360
|
+
mixWith = styles.GRID_ROW_SELECTED_BG;
|
|
363
361
|
}
|
|
362
|
+
} else if (showHovers && isHovered) {
|
|
363
|
+
mixWith = styles.GRID_ROW_HOVER_BG;
|
|
364
|
+
}
|
|
365
|
+
if (mixWith) {
|
|
366
|
+
const mixWithObj = nbToRgb(mixWith);
|
|
367
|
+
bg = colourMixer.blend(bg, 0.9, mixWithObj.color);
|
|
364
368
|
}
|
|
365
369
|
let WhichGridRow = GridRow,
|
|
366
370
|
rowReorderProps = {};
|
|
@@ -12,6 +12,8 @@ import FormPanel from '../Panel/FormPanel.js';
|
|
|
12
12
|
import Ban from '../Icons/Ban.js';
|
|
13
13
|
import Gear from '../Icons/Gear.js';
|
|
14
14
|
import Toolbar from '../Toolbar/Toolbar.js';
|
|
15
|
+
import getSaved from '../../Functions/getSaved.js';
|
|
16
|
+
import setSaved from '../../Functions/setSaved.js';
|
|
15
17
|
import UiGlobals from '../../UiGlobals.js';
|
|
16
18
|
import _ from 'lodash';
|
|
17
19
|
|
|
@@ -34,8 +36,6 @@ export default function withFilters(WrappedComponent) {
|
|
|
34
36
|
customFilters = [], // of shape: { title, type, field, value, getRepoFilters(value) }
|
|
35
37
|
minFilters = 3,
|
|
36
38
|
maxFilters = 6,
|
|
37
|
-
getSaved,
|
|
38
|
-
setSaved,
|
|
39
39
|
|
|
40
40
|
// withData
|
|
41
41
|
Repository,
|
|
@@ -166,7 +166,7 @@ export default function withFilters(WrappedComponent) {
|
|
|
166
166
|
}
|
|
167
167
|
setSlots(newSlots);
|
|
168
168
|
}
|
|
169
|
-
if (save
|
|
169
|
+
if (save) {
|
|
170
170
|
setSaved(id + '-filters', filters);
|
|
171
171
|
}
|
|
172
172
|
},
|
|
@@ -204,9 +204,12 @@ export default function withFilters(WrappedComponent) {
|
|
|
204
204
|
const filter = getFilterByField(field);
|
|
205
205
|
return filter?.type;
|
|
206
206
|
},
|
|
207
|
-
getIsFilterRange = (
|
|
208
|
-
|
|
207
|
+
getIsFilterRange = (filter) => {
|
|
208
|
+
let field = _.isString(filter) ? filter : filter.field;
|
|
209
209
|
const filterType = getFilterType(field);
|
|
210
|
+
if (filterType?.type) {
|
|
211
|
+
return inArray(filterType.type, ['NumberRange', 'DateRange'])
|
|
212
|
+
}
|
|
210
213
|
return inArray(filterType, ['NumberRange', 'DateRange']);
|
|
211
214
|
},
|
|
212
215
|
renderFilters = () => {
|
|
@@ -272,7 +275,7 @@ export default function withFilters(WrappedComponent) {
|
|
|
272
275
|
const newRepoFilters = [];
|
|
273
276
|
let filtersToUse = filters
|
|
274
277
|
|
|
275
|
-
if (!isReady
|
|
278
|
+
if (!isReady) {
|
|
276
279
|
const savedFilters = await getSaved(id + '-filters');
|
|
277
280
|
if (!_.isEmpty(savedFilters)) {
|
|
278
281
|
// load saved filters
|
|
@@ -295,7 +298,7 @@ export default function withFilters(WrappedComponent) {
|
|
|
295
298
|
field,
|
|
296
299
|
value,
|
|
297
300
|
} = filter,
|
|
298
|
-
isFilterRange = getIsFilterRange(
|
|
301
|
+
isFilterRange = getIsFilterRange(filter);
|
|
299
302
|
if (isFilterRange) {
|
|
300
303
|
if (!!value) {
|
|
301
304
|
const
|
|
@@ -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,23 @@ export default function TabPanel(props) {
|
|
|
40
45
|
if (onChangeIsCollapsed) {
|
|
41
46
|
onChangeIsCollapsed(isCollapsed);
|
|
42
47
|
}
|
|
48
|
+
setSaved(id + '-isCollapsed', isCollapsed);
|
|
43
49
|
},
|
|
44
50
|
setCurrentTab = (ix) => {
|
|
45
51
|
setCurrentTabRaw(ix);
|
|
46
52
|
if (onChangeCurrentTab) {
|
|
47
53
|
onChangeCurrentTab(ix);
|
|
48
54
|
}
|
|
55
|
+
if (saveCurrentTab) {
|
|
56
|
+
setSaved(id + '-currentTab', ix);
|
|
57
|
+
}
|
|
49
58
|
},
|
|
50
59
|
getButtonProps = () => {
|
|
51
60
|
const
|
|
52
61
|
iconProps = {
|
|
53
62
|
size: 'md',
|
|
54
63
|
},
|
|
55
|
-
textProps = {
|
|
56
|
-
ml: '-8px',
|
|
57
|
-
mr: '8px',
|
|
58
|
-
},
|
|
64
|
+
textProps = {},
|
|
59
65
|
buttonProps = {
|
|
60
66
|
bg: styles.TAB_BG,
|
|
61
67
|
color: styles.TAB_COLOR,
|
|
@@ -279,54 +285,81 @@ export default function TabPanel(props) {
|
|
|
279
285
|
onToggleCollapse = () => {
|
|
280
286
|
setIsCollapsed(!isCollapsed);
|
|
281
287
|
};
|
|
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
288
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
289
|
+
useEffect(() => {
|
|
290
|
+
// Restore saved settings
|
|
291
|
+
(async () => {
|
|
292
|
+
let key, val;
|
|
293
|
+
key = id + '-isCollapsed';
|
|
294
|
+
val = await getSaved(key);
|
|
295
|
+
if (!_.isNil(val)) {
|
|
296
|
+
setIsCollapsed(val);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
key = id + '-currentTab';
|
|
300
|
+
val = await getSaved(key);
|
|
301
|
+
if (!_.isNil(val)) {
|
|
302
|
+
setCurrentTab(val);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (!isReady) {
|
|
306
|
+
setIsReady(true);
|
|
307
|
+
}
|
|
308
|
+
})();
|
|
309
|
+
}, []);
|
|
310
|
+
|
|
311
|
+
if (!isReady) {
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (direction === VERTICAL) {
|
|
316
|
+
return <Panel {...propsToPass}>
|
|
317
|
+
<Row flex={1} w="100%">
|
|
318
|
+
<Column
|
|
313
319
|
alignItems="center"
|
|
314
320
|
justifyContent="flex-start"
|
|
315
|
-
|
|
316
|
-
|
|
321
|
+
py={2}
|
|
322
|
+
pl={isCollapsed ? 1 : 4}
|
|
317
323
|
bg={styles.TAB_BAR_BG}
|
|
318
|
-
|
|
324
|
+
w={isCollapsed ? '50px' : tabWidth}
|
|
319
325
|
>
|
|
320
326
|
{renderTabs()}
|
|
321
|
-
<
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
327
|
+
<Column flex={1} w="100%" justifyContent="flex-end">
|
|
328
|
+
{renderToggleButton()}
|
|
329
|
+
</Column>
|
|
330
|
+
</Column>
|
|
331
|
+
<Column
|
|
332
|
+
alignItems="center"
|
|
333
|
+
justifyContent="flex-start"
|
|
334
|
+
flex={1}
|
|
335
|
+
>
|
|
328
336
|
{renderCurrentTabContent()}
|
|
329
|
-
</
|
|
330
|
-
</
|
|
337
|
+
</Column>
|
|
338
|
+
</Row>
|
|
331
339
|
</Panel>;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// HORIZONTAL
|
|
343
|
+
return <Panel flex={1} w="100%" {...propsToPass} {...props._panel}>
|
|
344
|
+
<Column flex={1} w="100%">
|
|
345
|
+
<Row
|
|
346
|
+
alignItems="center"
|
|
347
|
+
justifyContent="flex-start"
|
|
348
|
+
p={2}
|
|
349
|
+
pb={0}
|
|
350
|
+
bg={styles.TAB_BAR_BG}
|
|
351
|
+
h={isCollapsed ? '30px' : tabHeight}
|
|
352
|
+
>
|
|
353
|
+
{renderTabs()}
|
|
354
|
+
<Row flex={1} h="100%" justifyContent="flex-end">
|
|
355
|
+
<Row h="100%">
|
|
356
|
+
{renderToggleButton()}
|
|
357
|
+
</Row>
|
|
358
|
+
</Row>
|
|
359
|
+
</Row>
|
|
360
|
+
<Row flex={1}>
|
|
361
|
+
{renderCurrentTabContent()}
|
|
362
|
+
</Row>
|
|
363
|
+
</Column>
|
|
364
|
+
</Panel>;
|
|
332
365
|
}
|
|
@@ -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}
|
|
@@ -12,6 +12,7 @@ export default function PaginationToolbar(props) {
|
|
|
12
12
|
} = props,
|
|
13
13
|
[minimize, setMinimize] = useState(false),
|
|
14
14
|
propsToPass = _.omit(props, 'toolbarItems'),
|
|
15
|
+
showPagination = props.Repository?.totalPages > 1,
|
|
15
16
|
onLayout = (e) => {
|
|
16
17
|
// Note to future self: this is using hard-coded values.
|
|
17
18
|
// Eventually might want to make it responsive to actual sizes
|
|
@@ -30,6 +31,16 @@ export default function PaginationToolbar(props) {
|
|
|
30
31
|
}
|
|
31
32
|
};
|
|
32
33
|
|
|
34
|
+
let toolbarProps = {};
|
|
35
|
+
if (showPagination) {
|
|
36
|
+
toolbarProps = {
|
|
37
|
+
borderLeftWidth: 1,
|
|
38
|
+
borderLeftColor: 'trueGray.400',
|
|
39
|
+
pl: 3,
|
|
40
|
+
ml: 3,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
33
44
|
return <Toolbar
|
|
34
45
|
bg="trueGray.200"
|
|
35
46
|
borderTopWidth={1}
|
|
@@ -37,7 +48,7 @@ export default function PaginationToolbar(props) {
|
|
|
37
48
|
w="100%"
|
|
38
49
|
onLayout={(e) => onLayout(e)}
|
|
39
50
|
>
|
|
40
|
-
<Pagination {...propsToPass} w={toolbarItems.length ? null : '100%'} minimize={minimize} />
|
|
41
|
-
{toolbarItems.length ? <Row flex={1}
|
|
51
|
+
{showPagination && <Pagination {...propsToPass} w={toolbarItems.length ? null : '100%'} minimize={minimize} />}
|
|
52
|
+
{toolbarItems.length ? <Row flex={1} {...toolbarProps}>{toolbarItems}</Row> : null}
|
|
42
53
|
</Toolbar>;
|
|
43
54
|
};
|
package/src/Constants/Styles.js
CHANGED
|
@@ -61,7 +61,10 @@ 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: 'trueGray.200:alpha.0',
|
|
64
65
|
ICON_BUTTON_BG_DISABLED: 'trueGray.200',
|
|
66
|
+
ICON_BUTTON_BG_HOVER: '#000:alpha.20',
|
|
67
|
+
ICON_BUTTON_BG_PRESSED: '#000:alpha.30',
|
|
65
68
|
PANEL_FOOTER_BG: 'primary.100', // :alpha.50
|
|
66
69
|
PANEL_HEADER_BG: 'primary.100',
|
|
67
70
|
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,63 @@
|
|
|
1
|
+
import UiGlobals from '../UiGlobals.js';
|
|
2
|
+
import _ from 'lodash';
|
|
3
|
+
|
|
4
|
+
function isRgb(color) {
|
|
5
|
+
const
|
|
6
|
+
regex = /^#[\w]{3,6}$/,
|
|
7
|
+
matches = color.match(regex);
|
|
8
|
+
return !!matches?.[0];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// 'color' might be in a format NativeBase uses, like '#000:alpha.20' or 'primary.200'
|
|
12
|
+
// Try to convert this to actual RGB colors.
|
|
13
|
+
export default function nbToRgb(color) {
|
|
14
|
+
|
|
15
|
+
if (isRgb(color)) {
|
|
16
|
+
// already in RGB format; simply return it
|
|
17
|
+
return {
|
|
18
|
+
color,
|
|
19
|
+
alpha,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const themeOverrideColors = UiGlobals?.ThemeOverrides?.colors || {};
|
|
24
|
+
if (themeOverrideColors[color]) {
|
|
25
|
+
color = themeOverrideColors[color];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let regex, alpha, matches;
|
|
29
|
+
|
|
30
|
+
regex = /^([\w#\.]+)(:alpha\.([\d]{1,2}))?$/;
|
|
31
|
+
matches = color.match(regex);
|
|
32
|
+
if (matches[3]) {
|
|
33
|
+
// alpha part exists. parse it
|
|
34
|
+
alpha = parseInt(matches[3], 10) / 100;
|
|
35
|
+
}
|
|
36
|
+
if (matches[1]) {
|
|
37
|
+
// color part exists. parse it
|
|
38
|
+
color = matches[1];
|
|
39
|
+
regex = /^(.+)\.([\d]{3})$/;
|
|
40
|
+
matches = color.match(regex);
|
|
41
|
+
if (matches) {
|
|
42
|
+
// color is in dot notation, like 'primary.200'
|
|
43
|
+
color = matches[1];
|
|
44
|
+
const whichValue = parseInt(matches[2], 10);
|
|
45
|
+
if (themeOverrideColors[color]?.[whichValue]) {
|
|
46
|
+
color = themeOverrideColors[color][whichValue];
|
|
47
|
+
}
|
|
48
|
+
} else if (themeOverrideColors[color]) {
|
|
49
|
+
// color is of form 'hover'
|
|
50
|
+
color = themeOverrideColors[color];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!isRgb(color)) {
|
|
54
|
+
color = nbToRgb(color).color;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
color,
|
|
61
|
+
alpha,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
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 Repo = oneHatData.getRepository(UiGlobals.uiSavesRepo);
|
|
7
|
+
if (!Repo) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let isOneBuild = false,
|
|
12
|
+
isJson = false,
|
|
13
|
+
model = null;
|
|
14
|
+
if (!_.isNil(value) && typeof value !== 'string') {
|
|
15
|
+
if (_.isArray(value)) {
|
|
16
|
+
const objects = value;
|
|
17
|
+
if (objects[0]?.getDataForNewEntity) {
|
|
18
|
+
model = objects[0].repository.name;
|
|
19
|
+
isOneBuild = true;
|
|
20
|
+
}
|
|
21
|
+
const rawValues = [];
|
|
22
|
+
_.each(objects, (obj) => {
|
|
23
|
+
rawValues.push(isOneBuild ? obj.getDataForNewEntity() : obj);
|
|
24
|
+
});
|
|
25
|
+
value = JSON.stringify(rawValues);
|
|
26
|
+
} else {
|
|
27
|
+
if (value.getDataForNewEntity) {
|
|
28
|
+
model = value.repository.name;
|
|
29
|
+
value = value.getDataForNewEntity();
|
|
30
|
+
isOneBuild = true;
|
|
31
|
+
}
|
|
32
|
+
value = JSON.stringify(value);
|
|
33
|
+
}
|
|
34
|
+
isJson = true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const entity = Repo.getById(key);
|
|
38
|
+
if (entity) {
|
|
39
|
+
entity.setValues({
|
|
40
|
+
value,
|
|
41
|
+
isOneBuild,
|
|
42
|
+
isJson,
|
|
43
|
+
model,
|
|
44
|
+
});
|
|
45
|
+
await Repo.save(entity);
|
|
46
|
+
} else {
|
|
47
|
+
await Repo.add({
|
|
48
|
+
key,
|
|
49
|
+
value,
|
|
50
|
+
isOneBuild,
|
|
51
|
+
isJson,
|
|
52
|
+
model,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|