@onehat/ui 0.2.79 → 0.2.80

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.79",
3
+ "version": "0.2.80",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -1,6 +1,5 @@
1
- import { useEffect, useState, } from 'react';
2
1
  import Panel from './Panel.js';
3
- import Grid, { InlineGridEditor, SideGridEditor, } from '../Grid/Grid.js';
2
+ import Grid, { InlineGridEditor, WindowedGridEditor, SideGridEditor, } from '../Grid/Grid.js';
4
3
  import {
5
4
  EDITOR_TYPE__INLINE,
6
5
  EDITOR_TYPE__WINDOWED,
@@ -10,47 +9,26 @@ import _ from 'lodash';
10
9
 
11
10
  export function GridPanel(props) {
12
11
  const {
12
+ isEditor = false,
13
13
  editorType = EDITOR_TYPE__WINDOWED,
14
- disableTitleChange = false,
15
- selectorSelected,
16
- } = props,
17
- originalTitle = props.title,
18
- [isReady, setIsReady] = useState(disableTitleChange),
19
- [title, setTitle] = useState(originalTitle);
14
+ } = props;
20
15
 
21
- let WhichGrid;
22
- switch(editorType) {
23
- case EDITOR_TYPE__INLINE:
24
- WhichGrid = InlineGridEditor;
25
- break;
26
- case EDITOR_TYPE__WINDOWED:
27
- WhichGrid = Grid;
28
- break;
29
- case EDITOR_TYPE__SIDE:
30
- WhichGrid = SideGridEditor;
31
- break;
32
- }
33
-
34
- useEffect(() => {
35
- if (!disableTitleChange && originalTitle) {
36
- let newTitle = originalTitle;
37
- if (selectorSelected?.[0]?.displayValue) {
38
- newTitle = originalTitle + ' for ' + selectorSelected[0].displayValue;
39
- }
40
- if (newTitle !== title) {
41
- setTitle(newTitle);
42
- }
16
+ let WhichGrid = Grid;
17
+ if (isEditor) {
18
+ switch(editorType) {
19
+ case EDITOR_TYPE__INLINE:
20
+ WhichGrid = InlineGridEditor;
21
+ break;
22
+ case EDITOR_TYPE__WINDOWED:
23
+ WhichGrid = WindowedGridEditor;
24
+ break;
25
+ case EDITOR_TYPE__SIDE:
26
+ WhichGrid = SideGridEditor;
27
+ break;
43
28
  }
44
- if (!isReady) {
45
- setIsReady(true);
46
- }
47
- }, [selectorSelected, disableTitleChange, originalTitle]);
48
-
49
- if (!isReady) {
50
- return null;
51
29
  }
52
30
 
53
- return <Panel {...props} title={title}>
31
+ return <Panel {...props} {..._panel}>
54
32
  <WhichGrid {...props} />
55
33
  </Panel>;
56
34
  }
@@ -1,3 +1,4 @@
1
+ import { useEffect, useState, } from 'react';
1
2
  import {
2
3
  Column,
3
4
  Row,
@@ -33,7 +34,6 @@ function Panel(props) {
33
34
 
34
35
  // Header
35
36
  title = props.model,
36
- titleSuffix = '',
37
37
  showHeader = true,
38
38
  header = null,
39
39
  isClosable = false,
@@ -42,6 +42,7 @@ function Panel(props) {
42
42
  isCollapsed = false,
43
43
  setIsCollapsed,
44
44
  collapseDirection = VERTICAL, // HORIZONTAL, VERTICAL
45
+ disableTitleChange = false,
45
46
 
46
47
  // Content
47
48
  topToolbar = null,
@@ -51,10 +52,32 @@ function Panel(props) {
51
52
 
52
53
  ...propsToPass
53
54
  } = props,
55
+ [titleSuffix, setTitleSuffix] = useState(''),
54
56
  onToggleCollapse = () => {
55
57
  setIsCollapsed(!isCollapsed);
56
58
  };
57
59
 
60
+ if (!disableTitleChange) {
61
+ const
62
+ selectorSelected = props.selectorSelected,
63
+ [isReady, setIsReady] = useState(disableTitleChange);
64
+
65
+ useEffect(() => {
66
+ let titleSuffix = '';
67
+ if (selectorSelected?.[0]?.displayValue) {
68
+ titleSuffix = ' for ' + selectorSelected[0].displayValue;
69
+ }
70
+ setTitleSuffix(titleSuffix);
71
+ if (!isReady) {
72
+ setIsReady(true);
73
+ }
74
+ }, [selectorSelected, disableTitleChange]);
75
+
76
+ if (!isReady) {
77
+ return null;
78
+ }
79
+ }
80
+
58
81
  let headerComponent = header;
59
82
  if (showHeader && title) {
60
83
  headerComponent = <Header
@@ -1,365 +1,9 @@
1
- import React, { useState, useEffect, useId, } from 'react';
2
- import {
3
- Button,
4
- Column,
5
- Icon,
6
- Pressable,
7
- Row,
8
- Text,
9
- } from 'native-base';
10
- import {
11
- HORIZONTAL,
12
- VERTICAL,
13
- } from '../../Constants/Directions.js';
14
- import UiGlobals from '../../UiGlobals.js';
15
- import IconButton from '../Buttons/IconButton.js';
16
- import Minimize from '../Icons/Minimize.js';
17
- import Maximize from '../Icons/Maximize.js';
1
+ import TabBar from '../Tab/TabBar.js'
18
2
  import Panel from './Panel.js';
19
- import getSaved from '../../Functions/getSaved.js';
20
- import setSaved from '../../Functions/setSaved.js';
21
- import _ from 'lodash';
22
3
 
23
4
 
24
5
  export default function TabPanel(props) {
25
- const {
26
- tabs = [],
27
- direction = HORIZONTAL,
28
- tabWidth = 150, // used on VERTICAL mode only
29
- tabHeight = '44px', // used on HORIZONTAL mode only
30
- additionalButtons,
31
- initialTab = 0,
32
- startsCollapsed = true,
33
- onChangeCurrentTab,
34
- onChangeIsCollapsed,
35
- saveCurrentTab = true,
36
- ...propsToPass
37
- } = props,
38
- styles = UiGlobals.styles,
39
- id = useId(),
40
- [isReady, setIsReady] = useState(false),
41
- [currentTab, setCurrentTabRaw] = useState(initialTab),
42
- [isCollapsed, setIsCollapsedRaw] = useState(startsCollapsed),
43
- setIsCollapsed = (isCollapsed) => {
44
- setIsCollapsedRaw(isCollapsed);
45
- if (onChangeIsCollapsed) {
46
- onChangeIsCollapsed(isCollapsed);
47
- }
48
- setSaved(id + '-isCollapsed', isCollapsed);
49
- },
50
- setCurrentTab = (ix) => {
51
- setCurrentTabRaw(ix);
52
- if (onChangeCurrentTab) {
53
- onChangeCurrentTab(ix);
54
- }
55
- if (saveCurrentTab) {
56
- setSaved(id + '-currentTab', ix);
57
- }
58
- },
59
- getButtonProps = () => {
60
- const
61
- iconProps = {
62
- size: 'md',
63
- },
64
- textProps = {},
65
- buttonProps = {
66
- bg: styles.TAB_BG,
67
- color: styles.TAB_COLOR,
68
- fontSize: styles.TAB_FONTSIZE,
69
- textAlign: 'left',
70
- justifyContent: isCollapsed ? 'center' : 'flex-start',
71
- };
72
- switch(direction) {
73
- case VERTICAL:
74
- buttonProps.borderLeftRadius = 4;
75
- buttonProps.borderRightRadius = 0;
76
- buttonProps.w = '100%';
77
- buttonProps.mb = 1;
78
- textProps.w = '100%';
79
- textProps.py = 0;
80
- textProps.pl = 3;
81
- textProps.mb = 1;
82
- break;
83
- case HORIZONTAL:
84
- buttonProps.borderTopRadius = 4;
85
- buttonProps.borderBottomRadius = 0;
86
- textProps.borderTopRadius = 4;
87
- buttonProps.mr = 1;
88
- buttonProps.py = 1;
89
- textProps.mr = 1;
90
- textProps.px = 1;
91
- textProps.py = 1;
92
- break;
93
- default:
94
- }
95
- return {
96
- buttonProps,
97
- textProps,
98
- iconProps,
99
- };
100
- },
101
- renderTabs = () => {
102
- const
103
- {
104
- buttonProps,
105
- textProps,
106
- iconProps,
107
- } = getButtonProps(),
108
- buttons = [];
109
-
110
- _.each(tabs, (tab, ix) => {
111
- if (!tab._icon) {
112
- throw new Error('tab._icon required!');
113
- }
114
- let button;
115
- const
116
- isCurrentTab = ix === currentTab,
117
- thisButtonProps = {};
118
- if (isCollapsed) {
119
- button = <IconButton
120
- key={'tab' + ix}
121
- onPress={() => setCurrentTab(ix)}
122
- {...buttonProps}
123
- // {...thisButtonProps}
124
- _icon={{
125
- color: isCurrentTab ? styles.TAB_ACTIVE_ICON_COLOR : styles.TAB_ICON_COLOR,
126
- ...iconProps,
127
- ...tab._icon,
128
- }}
129
- _hover={{
130
- bg: isCurrentTab? styles.TAB_ACTIVE_HOVER_BG : styles.TAB_HOVER_BG,
131
- }}
132
- bg={isCurrentTab ? styles.TAB_ACTIVE_BG : styles.TAB_BG}
133
- tooltip={tab.title}
134
- />;
135
- } else {
136
- button = <Button
137
- key={'tab' + ix}
138
- onPress={() => setCurrentTab(ix)}
139
- leftIcon={<Icon
140
- color={isCurrentTab ? styles.TAB_ACTIVE_ICON_COLOR : styles.TAB_ICON_COLOR}
141
- {...iconProps}
142
- {...tab._icon}
143
- />}
144
- {...buttonProps}
145
- {...thisButtonProps}
146
- _hover={{
147
- bg: isCurrentTab? styles.TAB_ACTIVE_HOVER_BG : styles.TAB_HOVER_BG,
148
- }}
149
- bg={isCurrentTab ? styles.TAB_ACTIVE_BG : styles.TAB_BG}
150
- >
151
- <Text
152
- color={isCurrentTab ? styles.TAB_ACTIVE_COLOR : styles.TAB_COLOR}
153
- fontSize={styles.TAB_FONTSIZE}
154
- numberOfLines={1}
155
- ellipsizeMode="head"
156
- {...textProps}
157
- >{tab.title}</Text>
158
- </Button>;
159
- }
160
- buttons.push(button);
161
- });
162
-
163
- if (additionalButtons) {
164
- _.each(additionalButtons, (additionalButton, ix) => {
165
- if (!additionalButton._icon) {
166
- throw new Error('additionalButton._icon required!');
167
- }
168
- let button;
169
- const thisButtonProps = {};
170
- if (!ix) {
171
- // First button should have gap before it
172
- switch(direction) {
173
- case VERTICAL:
174
- thisButtonProps.mt = 6;
175
- break;
176
- case HORIZONTAL:
177
- thisButtonProps.ml = 6;
178
- break;
179
- default:
180
- }
181
- }
182
- if (isCollapsed) {
183
- button = <IconButton
184
- key={'additionalBtn' + ix}
185
- onPress={additionalButton.onPress}
186
- {...buttonProps}
187
- {...thisButtonProps}
188
- _icon={{
189
- ...additionalButton._icon,
190
- color: styles.TAB_ICON_COLOR,
191
- }}
192
- _hover={{
193
- bg: styles.TAB_HOVER_BG,
194
- }}
195
- bg={styles.TAB_BG}
196
- tooltip={additionalButton.text}
197
- />;
198
- } else {
199
- button = <Button
200
- key={'additionalBtn' + ix}
201
- onPress={additionalButton.onPress}
202
- leftIcon={<Icon
203
- color={styles.TAB_ICON_COLOR}
204
- {...additionalButton._icon}
205
- />}
206
- _hover={{
207
- bg: styles.TAB_HOVER_BG,
208
- }}
209
- bg={styles.TAB_BG}
210
- {...buttonProps}
211
- {...thisButtonProps}
212
- >
213
- <Text
214
- color={styles.TAB_COLOR}
215
- fontSize={styles.TAB_FONTSIZE}
216
- numberOfLines={1}
217
- ellipsizeMode="head"
218
- {...textProps}
219
- >{additionalButton.text}</Text>
220
- </Button>;
221
- }
222
- buttons.push(button);
223
- });
224
- }
225
-
226
- return buttons;
227
- },
228
- renderCurrentTabContent = () => {
229
- if (tabs[currentTab].content) {
230
- return tabs[currentTab].content;
231
- }
232
- return _.map(tabs[currentTab].items, (item, ix) => {
233
- return React.cloneElement(item, { key: ix });
234
- });
235
- },
236
- renderToggleButton = () => {
237
- const
238
- {
239
- buttonProps,
240
- textProps,
241
- } = getButtonProps();
242
-
243
- let button;
244
- if (isCollapsed) {
245
- button = <IconButton
246
- key="toggleBtn"
247
- onPress={onToggleCollapse}
248
- {...buttonProps}
249
- _icon={{
250
- as: Maximize,
251
- color: styles.TAB_ICON_COLOR,
252
- }}
253
- _hover={{
254
- bg: styles.TAB_HOVER_BG,
255
- }}
256
- bg={styles.TAB_BG}
257
- tooltip={isCollapsed ? 'Expand' : 'Collapse'}
258
- />;
259
- } else {
260
- button = <Button
261
- key="toggleBtn"
262
- onPress={onToggleCollapse}
263
- leftIcon={<Icon
264
- as={Minimize}
265
- color={styles.TAB_ICON_COLOR}
266
- />}
267
- _hover={{
268
- bg: styles.TAB_HOVER_BG,
269
- }}
270
- bg={styles.TAB_BG}
271
- {...buttonProps}
272
- // {...thisButtonProps}
273
- >
274
- <Text
275
- color={styles.TAB_COLOR}
276
- fontSize={styles.TAB_FONTSIZE}
277
- numberOfLines={1}
278
- ellipsizeMode="head"
279
- {...textProps}
280
- >Collapse</Text>
281
- </Button>;
282
- }
283
- return button;
284
- },
285
- onToggleCollapse = () => {
286
- setIsCollapsed(!isCollapsed);
287
- };
288
-
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
319
- alignItems="center"
320
- justifyContent="flex-start"
321
- py={2}
322
- pl={isCollapsed ? 1 : 4}
323
- bg={styles.TAB_BAR_BG}
324
- w={isCollapsed ? '50px' : tabWidth}
325
- >
326
- {renderTabs()}
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
- >
336
- {renderCurrentTabContent()}
337
- </Column>
338
- </Row>
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>
6
+ return <Panel flex={1} w="100%" {...props._panel}>
7
+ <TabBar {...props} />
364
8
  </Panel>;
365
9
  }
@@ -1,38 +1,31 @@
1
- import { useEffect, useState, } from 'react';
2
1
  import Panel from './Panel.js';
3
- import { Tree, } from '../Tree/Tree.js';
2
+ import Tree, { WindowedTreeEditor, SideTreeEditor, } from '../Tree/Tree.js';
3
+ import {
4
+ EDITOR_TYPE__WINDOWED,
5
+ EDITOR_TYPE__SIDE,
6
+ } from '../../Constants/Editor.js';
4
7
  import _ from 'lodash';
5
8
 
6
9
  export function TreePanel(props) {
7
10
  const {
8
- disableTitleChange = false,
9
- selectorSelected,
10
- } = props,
11
- originalTitle = props.title,
12
- [isReady, setIsReady] = useState(disableTitleChange),
13
- [title, setTitle] = useState(originalTitle);
11
+ isEditor = false,
12
+ editorType = EDITOR_TYPE__WINDOWED,
13
+ } = props;
14
14
 
15
- useEffect(() => {
16
- if (!disableTitleChange && originalTitle) {
17
- let newTitle = originalTitle;
18
- if (selectorSelected?.[0]?.displayValue) {
19
- newTitle = originalTitle + ' for ' + selectorSelected[0].displayValue;
20
- }
21
- if (newTitle !== title) {
22
- setTitle(newTitle);
23
- }
15
+ let WhichTree = Tree;
16
+ if (isEditor) {
17
+ switch(editorType) {
18
+ case EDITOR_TYPE__WINDOWED:
19
+ WhichTree = WindowedTreeEditor;
20
+ break;
21
+ case EDITOR_TYPE__SIDE:
22
+ WhichTree = SideTreeEditor;
23
+ break;
24
24
  }
25
- if (!isReady) {
26
- setIsReady(true);
27
- }
28
- }, [selectorSelected, disableTitleChange, originalTitle]);
29
-
30
- if (!isReady) {
31
- return null;
32
25
  }
33
26
 
34
- return <Panel {...props} title={title}>
35
- <Tree {...props} />
27
+ return <Panel {...props._panel}>
28
+ <WhichTree {...props} />
36
29
  </Panel>;
37
30
  }
38
31
 
@@ -0,0 +1,389 @@
1
+ import React, { useState, useEffect, useId, } from 'react';
2
+ import {
3
+ Button,
4
+ Column,
5
+ Icon,
6
+ Row,
7
+ Text,
8
+ } from 'native-base';
9
+ import {
10
+ HORIZONTAL,
11
+ VERTICAL,
12
+ } from '../../Constants/Directions.js';
13
+ import UiGlobals from '../../UiGlobals.js';
14
+ import IconButton from '../Buttons/IconButton.js';
15
+ import Minimize from '../Icons/Minimize.js';
16
+ import Maximize from '../Icons/Maximize.js';
17
+ import getSaved from '../../Functions/getSaved.js';
18
+ import setSaved from '../../Functions/setSaved.js';
19
+ import _ from 'lodash';
20
+
21
+
22
+ export default function TabBar(props) {
23
+ const {
24
+ tabs = [], // { _icon, title, content, path, items, }
25
+ content, // e.g. Expo Router slot
26
+ direction = HORIZONTAL,
27
+ tabWidth = 150, // used on VERTICAL mode only
28
+ tabHeight = '44px', // used on HORIZONTAL mode only
29
+ additionalButtons,
30
+ initialTabIx = 0,
31
+ currentTabIx,
32
+ startsCollapsed = true,
33
+ onChangeCurrentTab,
34
+ onChangeIsCollapsed,
35
+ saveCurrentTab = true,
36
+ ...propsToPass
37
+ } = props,
38
+ styles = UiGlobals.styles,
39
+ id = useId(),
40
+ useLocal = _.isNil(currentTabIx),
41
+ [isReady, setIsReady] = useState(false),
42
+ [currentTabIxLocal, setCurrentTabIxLocal] = useState(initialTabIx),
43
+ [isCollapsed, setIsCollapsedRaw] = useState(startsCollapsed),
44
+ setIsCollapsed = (isCollapsed) => {
45
+ setIsCollapsedRaw(isCollapsed);
46
+ if (onChangeIsCollapsed) {
47
+ onChangeIsCollapsed(isCollapsed);
48
+ }
49
+ setSaved(id + '-isCollapsed', isCollapsed);
50
+ },
51
+ getCurrentTab = () => {
52
+ if (useLocal) {
53
+ return currentTabIxLocal;
54
+ }
55
+ return currentTabIx;
56
+ },
57
+ setCurrentTab = (ix) => {
58
+ if (useLocal) {
59
+ setCurrentTabIxLocal(ix);
60
+ }
61
+ if (onChangeCurrentTab) {
62
+ onChangeCurrentTab(ix);
63
+ }
64
+ if (saveCurrentTab) {
65
+ setSaved(id + '-currentTabIx', ix);
66
+ }
67
+ },
68
+ onToggleCollapse = () => {
69
+ setIsCollapsed(!isCollapsed);
70
+ },
71
+ renderToggleButton = () => {
72
+ const
73
+ {
74
+ buttonProps,
75
+ textProps,
76
+ } = getButtonProps();
77
+
78
+ let button;
79
+ if (isCollapsed) {
80
+ button = <IconButton
81
+ key="toggleBtn"
82
+ onPress={onToggleCollapse}
83
+ {...buttonProps}
84
+ _icon={{
85
+ as: Maximize,
86
+ color: styles.TAB_ICON_COLOR,
87
+ }}
88
+ _hover={{
89
+ bg: styles.TAB_HOVER_BG,
90
+ }}
91
+ bg={styles.TAB_BG}
92
+ tooltip={isCollapsed ? 'Expand' : 'Collapse'}
93
+ />;
94
+ } else {
95
+ button = <Button
96
+ key="toggleBtn"
97
+ onPress={onToggleCollapse}
98
+ leftIcon={<Icon
99
+ as={Minimize}
100
+ color={styles.TAB_ICON_COLOR}
101
+ />}
102
+ _hover={{
103
+ bg: styles.TAB_HOVER_BG,
104
+ }}
105
+ bg={styles.TAB_BG}
106
+ {...buttonProps}
107
+ // {...thisButtonProps}
108
+ >
109
+ <Text
110
+ color={styles.TAB_COLOR}
111
+ fontSize={styles.TAB_FONTSIZE}
112
+ numberOfLines={1}
113
+ ellipsizeMode="head"
114
+ {...textProps}
115
+ >Collapse</Text>
116
+ </Button>;
117
+ }
118
+ return button;
119
+ },
120
+ getButtonProps = () => {
121
+ const
122
+ iconProps = {
123
+ size: 'md',
124
+ },
125
+ textProps = {},
126
+ buttonProps = {
127
+ bg: styles.TAB_BG,
128
+ color: styles.TAB_COLOR,
129
+ fontSize: styles.TAB_FONTSIZE,
130
+ textAlign: 'left',
131
+ justifyContent: isCollapsed ? 'center' : 'flex-start',
132
+ };
133
+ switch(direction) {
134
+ case VERTICAL:
135
+ buttonProps.borderLeftRadius = 4;
136
+ buttonProps.borderRightRadius = 0;
137
+ buttonProps.w = '100%';
138
+ buttonProps.mb = 1;
139
+ textProps.w = '100%';
140
+ textProps.py = 0;
141
+ textProps.pl = 3;
142
+ textProps.mb = 1;
143
+ break;
144
+ case HORIZONTAL:
145
+ buttonProps.borderTopRadius = 4;
146
+ buttonProps.borderBottomRadius = 0;
147
+ textProps.borderTopRadius = 4;
148
+ buttonProps.mr = 1;
149
+ buttonProps.py = 1;
150
+ textProps.mr = 1;
151
+ textProps.px = 1;
152
+ textProps.py = 1;
153
+ break;
154
+ default:
155
+ }
156
+ return {
157
+ buttonProps,
158
+ textProps,
159
+ iconProps,
160
+ };
161
+ },
162
+ renderTabs = () => {
163
+ const
164
+ {
165
+ buttonProps,
166
+ textProps,
167
+ iconProps,
168
+ } = getButtonProps(),
169
+ buttons = [];
170
+
171
+ _.each(tabs, (tab, ix) => {
172
+ if (!tab._icon) {
173
+ throw new Error('tab._icon required!');
174
+ }
175
+ let button;
176
+ const
177
+ isCurrentTab = ix === getCurrentTab(),
178
+ thisButtonProps = {};
179
+ if (isCollapsed) {
180
+ button = <IconButton
181
+ key={'tab' + ix}
182
+ onPress={() => setCurrentTab(ix)}
183
+ {...buttonProps}
184
+ // {...thisButtonProps}
185
+ _icon={{
186
+ color: isCurrentTab ? styles.TAB_ACTIVE_ICON_COLOR : styles.TAB_ICON_COLOR,
187
+ ...iconProps,
188
+ ...tab._icon,
189
+ }}
190
+ _hover={{
191
+ bg: isCurrentTab? styles.TAB_ACTIVE_HOVER_BG : styles.TAB_HOVER_BG,
192
+ }}
193
+ bg={isCurrentTab ? styles.TAB_ACTIVE_BG : styles.TAB_BG}
194
+ tooltip={tab.title}
195
+ />;
196
+ } else {
197
+ button = <Button
198
+ key={'tab' + ix}
199
+ onPress={() => setCurrentTab(ix)}
200
+ leftIcon={<Icon
201
+ color={isCurrentTab ? styles.TAB_ACTIVE_ICON_COLOR : styles.TAB_ICON_COLOR}
202
+ {...iconProps}
203
+ {...tab._icon}
204
+ />}
205
+ {...buttonProps}
206
+ {...thisButtonProps}
207
+ _hover={{
208
+ bg: isCurrentTab? styles.TAB_ACTIVE_HOVER_BG : styles.TAB_HOVER_BG,
209
+ }}
210
+ bg={isCurrentTab ? styles.TAB_ACTIVE_BG : styles.TAB_BG}
211
+ >
212
+ <Text
213
+ color={isCurrentTab ? styles.TAB_ACTIVE_COLOR : styles.TAB_COLOR}
214
+ fontSize={styles.TAB_FONTSIZE}
215
+ numberOfLines={1}
216
+ ellipsizeMode="head"
217
+ {...textProps}
218
+ >{tab.title}</Text>
219
+ </Button>;
220
+ }
221
+ buttons.push(button);
222
+ });
223
+
224
+ if (additionalButtons) {
225
+ _.each(additionalButtons, (additionalButton, ix) => {
226
+ if (!additionalButton._icon) {
227
+ throw new Error('additionalButton._icon required!');
228
+ }
229
+ let button;
230
+ const thisButtonProps = {};
231
+ if (!ix) {
232
+ // First button should have gap before it
233
+ switch(direction) {
234
+ case VERTICAL:
235
+ thisButtonProps.mt = 6;
236
+ break;
237
+ case HORIZONTAL:
238
+ thisButtonProps.ml = 6;
239
+ break;
240
+ default:
241
+ }
242
+ }
243
+ if (isCollapsed) {
244
+ button = <IconButton
245
+ key={'additionalBtn' + ix}
246
+ onPress={additionalButton.onPress}
247
+ {...buttonProps}
248
+ {...thisButtonProps}
249
+ _icon={{
250
+ ...additionalButton._icon,
251
+ color: styles.TAB_ICON_COLOR,
252
+ }}
253
+ _hover={{
254
+ bg: styles.TAB_HOVER_BG,
255
+ }}
256
+ bg={styles.TAB_BG}
257
+ tooltip={additionalButton.text}
258
+ />;
259
+ } else {
260
+ button = <Button
261
+ key={'additionalBtn' + ix}
262
+ onPress={additionalButton.onPress}
263
+ leftIcon={<Icon
264
+ color={styles.TAB_ICON_COLOR}
265
+ {...additionalButton._icon}
266
+ />}
267
+ _hover={{
268
+ bg: styles.TAB_HOVER_BG,
269
+ }}
270
+ bg={styles.TAB_BG}
271
+ {...buttonProps}
272
+ {...thisButtonProps}
273
+ >
274
+ <Text
275
+ color={styles.TAB_COLOR}
276
+ fontSize={styles.TAB_FONTSIZE}
277
+ numberOfLines={1}
278
+ ellipsizeMode="head"
279
+ {...textProps}
280
+ >{additionalButton.text}</Text>
281
+ </Button>;
282
+ }
283
+ buttons.push(button);
284
+ });
285
+ }
286
+
287
+ return buttons;
288
+ },
289
+ renderCurrentTabContent = () => {
290
+ if (content) {
291
+ return content;
292
+ }
293
+
294
+ const currentTabIx = getCurrentTab();
295
+ if (tabs[currentTabIx].content) {
296
+ return tabs[currentTabIx].content;
297
+ }
298
+ return _.map(tabs[currentTabIx].items, (item, ix) => {
299
+ return React.cloneElement(item, { key: ix });
300
+ });
301
+ };
302
+
303
+ useEffect(() => {
304
+ // Restore saved settings
305
+ (async () => {
306
+ let key, val;
307
+ key = id + '-isCollapsed';
308
+ val = await getSaved(key);
309
+ if (!_.isNil(val)) {
310
+ setIsCollapsed(val);
311
+ }
312
+
313
+ if (saveCurrentTab) {
314
+ key = id + '-currentTabIx';
315
+ val = await getSaved(key);
316
+ if (!_.isNil(val)) {
317
+ setCurrentTab(val);
318
+ }
319
+ }
320
+
321
+ if (!isReady) {
322
+ setIsReady(true);
323
+ }
324
+ })();
325
+ }, []);
326
+
327
+ if (!isReady) {
328
+ return null;
329
+ }
330
+
331
+ const
332
+ renderedTabs = renderTabs(),
333
+ renderedCurrentTabContent = renderCurrentTabContent(),
334
+ renderedToggleButton = renderToggleButton();
335
+
336
+
337
+ if (direction === VERTICAL) {
338
+ return <Row flex={1} w="100%" {...propsToPass}>
339
+ <Column
340
+ alignItems="center"
341
+ justifyContent="flex-start"
342
+ py={2}
343
+ pl={isCollapsed ? 1 : 4}
344
+ bg={styles.TAB_BAR_BG}
345
+ w={isCollapsed ? '50px' : tabWidth}
346
+ >
347
+ {renderedTabs}
348
+ <Column flex={1} w="100%" justifyContent="flex-end">
349
+ {renderedToggleButton}
350
+ </Column>
351
+ </Column>
352
+ {renderedCurrentTabContent &&
353
+ <Column
354
+ alignItems="center"
355
+ justifyContent="flex-start"
356
+ flex={1}
357
+ >
358
+ {renderedCurrentTabContent}
359
+ </Column>}
360
+ </Row>;
361
+ }
362
+
363
+ // HORIZONTAL
364
+ return <Column flex={1} w="100%" {...propsToPass}>
365
+ <Row
366
+ alignItems="center"
367
+ justifyContent="flex-start"
368
+ p={2}
369
+ pb={0}
370
+ bg={styles.TAB_BAR_BG}
371
+ h={isCollapsed ? '30px' : tabHeight}
372
+ >
373
+ {renderedTabs}
374
+ <Row flex={1} h="100%" justifyContent="flex-end">
375
+ <Row h="100%">
376
+ {renderedToggleButton}
377
+ </Row>
378
+ </Row>
379
+ </Row>
380
+ {renderedCurrentTabContent &&
381
+ <Column
382
+ alignItems="center"
383
+ justifyContent="flex-start"
384
+ flex={1}
385
+ >
386
+ {renderedCurrentTabContent}
387
+ </Column>}
388
+ </Column>;
389
+ }
@@ -1,39 +0,0 @@
1
- import { useEffect, useState, } from 'react';
2
- import Panel from './Panel.js';
3
- import { InlineGridEditor, } from '../Grid/Grid.js';
4
- import _ from 'lodash';
5
-
6
- export function GridPanel(props) {
7
- const {
8
- disableTitleChange = false,
9
- selectorSelected,
10
- } = props,
11
- originalTitle = props.title,
12
- [isReady, setIsReady] = useState(disableTitleChange),
13
- [title, setTitle] = useState(originalTitle);
14
-
15
- useEffect(() => {
16
- if (!disableTitleChange && originalTitle) {
17
- let newTitle = originalTitle;
18
- if (selectorSelected?.[0]?.displayValue) {
19
- newTitle = originalTitle + ' for ' + selectorSelected[0].displayValue;
20
- }
21
- if (newTitle !== title) {
22
- setTitle(newTitle);
23
- }
24
- }
25
- if (!isReady) {
26
- setIsReady(true);
27
- }
28
- }, [selectorSelected, disableTitleChange, originalTitle]);
29
-
30
- if (!isReady) {
31
- return null;
32
- }
33
-
34
- return <Panel {...props} title={title}>
35
- <InlineGridEditor {...props} />
36
- </Panel>;
37
- }
38
-
39
- export default GridPanel;
@@ -1,39 +0,0 @@
1
- import { useEffect, useState, } from 'react';
2
- import Panel from './Panel.js';
3
- import { SideGridEditor, } from '../Grid/Grid.js';
4
- import _ from 'lodash';
5
-
6
- export function GridPanel(props) {
7
- const {
8
- disableTitleChange = false,
9
- selectorSelected,
10
- } = props,
11
- originalTitle = props.title,
12
- [isReady, setIsReady] = useState(disableTitleChange),
13
- [title, setTitle] = useState(originalTitle);
14
-
15
- useEffect(() => {
16
- if (!disableTitleChange && originalTitle) {
17
- let newTitle = originalTitle;
18
- if (selectorSelected?.[0]?.displayValue) {
19
- newTitle = originalTitle + ' for ' + selectorSelected[0].displayValue;
20
- }
21
- if (newTitle !== title) {
22
- setTitle(newTitle);
23
- }
24
- }
25
- if (!isReady) {
26
- setIsReady(true);
27
- }
28
- }, [selectorSelected, disableTitleChange, originalTitle]);
29
-
30
- if (!isReady) {
31
- return null;
32
- }
33
-
34
- return <Panel {...props} title={title}>
35
- <SideGridEditor {...props} />
36
- </Panel>;
37
- }
38
-
39
- export default GridPanel;
@@ -1,39 +0,0 @@
1
- import { useEffect, useState, } from 'react';
2
- import Panel from './Panel.js';
3
- import { SideTreeEditor, } from '../Tree/Tree.js';
4
- import _ from 'lodash';
5
-
6
- export function TreePanel(props) {
7
- const {
8
- disableTitleChange = false,
9
- selectorSelected,
10
- } = props,
11
- originalTitle = props.title,
12
- [isReady, setIsReady] = useState(disableTitleChange),
13
- [title, setTitle] = useState(originalTitle);
14
-
15
- useEffect(() => {
16
- if (!disableTitleChange && originalTitle) {
17
- let newTitle = originalTitle;
18
- if (selectorSelected?.[0]?.displayValue) {
19
- newTitle = originalTitle + ' for ' + selectorSelected[0].displayValue;
20
- }
21
- if (newTitle !== title) {
22
- setTitle(newTitle);
23
- }
24
- }
25
- if (!isReady) {
26
- setIsReady(true);
27
- }
28
- }, [selectorSelected, disableTitleChange, originalTitle]);
29
-
30
- if (!isReady) {
31
- return null;
32
- }
33
-
34
- return <Panel {...props} title={title}>
35
- <SideTreeEditor {...props} />
36
- </Panel>;
37
- }
38
-
39
- export default TreePanel;
@@ -1,39 +0,0 @@
1
- import { useEffect, useState, } from 'react';
2
- import Panel from './Panel.js';
3
- import { WindowedGridEditor, } from '../Grid/Grid.js';
4
- import _ from 'lodash';
5
-
6
- export function GridPanel(props) {
7
- const {
8
- disableTitleChange = false,
9
- selectorSelected,
10
- } = props,
11
- originalTitle = props.title,
12
- [isReady, setIsReady] = useState(disableTitleChange),
13
- [title, setTitle] = useState(originalTitle);
14
-
15
- useEffect(() => {
16
- if (!disableTitleChange && originalTitle) {
17
- let newTitle = originalTitle;
18
- if (selectorSelected?.[0]?.displayValue) {
19
- newTitle = originalTitle + ' for ' + selectorSelected[0].displayValue;
20
- }
21
- if (newTitle !== title) {
22
- setTitle(newTitle);
23
- }
24
- }
25
- if (!isReady) {
26
- setIsReady(true);
27
- }
28
- }, [selectorSelected, disableTitleChange, originalTitle]);
29
-
30
- if (!isReady) {
31
- return null;
32
- }
33
-
34
- return <Panel {...props} title={title}>
35
- <WindowedGridEditor {...props} />
36
- </Panel>;
37
- }
38
-
39
- export default GridPanel;
@@ -1,39 +0,0 @@
1
- import { useEffect, useState, } from 'react';
2
- import Panel from './Panel.js';
3
- import { WindowedTreeEditor, } from '../Tree/Tree.js';
4
- import _ from 'lodash';
5
-
6
- export function TreePanel(props) {
7
- const {
8
- disableTitleChange = false,
9
- selectorSelected,
10
- } = props,
11
- originalTitle = props.title,
12
- [isReady, setIsReady] = useState(disableTitleChange),
13
- [title, setTitle] = useState(originalTitle);
14
-
15
- useEffect(() => {
16
- if (!disableTitleChange && originalTitle) {
17
- let newTitle = originalTitle;
18
- if (selectorSelected?.[0]?.displayValue) {
19
- newTitle = originalTitle + ' for ' + selectorSelected[0].displayValue;
20
- }
21
- if (newTitle !== title) {
22
- setTitle(newTitle);
23
- }
24
- }
25
- if (!isReady) {
26
- setIsReady(true);
27
- }
28
- }, [selectorSelected, disableTitleChange, originalTitle]);
29
-
30
- if (!isReady) {
31
- return null;
32
- }
33
-
34
- return <Panel {...props} title={title}>
35
- <WindowedTreeEditor {...props} />
36
- </Panel>;
37
- }
38
-
39
- export default TreePanel;