@onehat/ui 0.4.81 → 0.4.83

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.
Files changed (31) hide show
  1. package/package.json +7 -6
  2. package/src/Components/Container/Container.js +4 -4
  3. package/src/Components/Form/Field/Combo/Combo.js +88 -16
  4. package/src/Components/Form/Field/Combo/MeterTypesCombo.js +1 -0
  5. package/src/Components/Form/Field/Date.js +1 -1
  6. package/src/Components/Form/Field/Json.js +2 -1
  7. package/src/Components/Form/Field/Select/PageSizeSelect.js +6 -1
  8. package/src/Components/Form/Field/Select/Select.js +14 -38
  9. package/src/Components/Form/Field/Tag/Tag.js +234 -14
  10. package/src/Components/Form/Field/Tag/ValueBox.js +20 -1
  11. package/src/Components/Form/Form.js +26 -13
  12. package/src/Components/Grid/Grid.js +316 -106
  13. package/src/Components/Grid/GridHeaderRow.js +42 -22
  14. package/src/Components/Grid/GridRow.js +16 -6
  15. package/src/Components/Grid/RowHandle.js +16 -4
  16. package/src/Components/Hoc/Secondary/withSecondaryEditor.js +137 -43
  17. package/src/Components/Hoc/Secondary/withSecondarySideEditor.js +1 -1
  18. package/src/Components/Hoc/withData.js +7 -0
  19. package/src/Components/Hoc/withEditor.js +19 -4
  20. package/src/Components/Hoc/withPresetButtons.js +1 -1
  21. package/src/Components/Hoc/withSideEditor.js +1 -1
  22. package/src/Components/Icons/Join.js +10 -0
  23. package/src/Components/Layout/AsyncOperation.js +61 -14
  24. package/src/Components/Layout/CenterBox.js +1 -1
  25. package/src/Components/Screens/Manager.js +1 -1
  26. package/src/Components/Toolbar/Pagination.js +108 -106
  27. package/src/Components/Toolbar/PaginationToolbar.js +3 -1
  28. package/src/Components/Toolbar/Toolbar.js +10 -6
  29. package/src/Components/Tree/TreeNode.js +39 -9
  30. package/src/Components/Viewer/Viewer.js +7 -2
  31. package/src/Constants/Progress.js +2 -1
@@ -86,10 +86,10 @@ export default function withEditor(WrappedComponent, isTree = false) {
86
86
  newEntityDisplayValueRef = useRef(),
87
87
  editorModeRef = useRef(initialEditorMode),
88
88
  isIgnoreNextSelectionChangeRef = useRef(false),
89
+ isEditorShownRef = useRef(false),
89
90
  [currentRecord, setCurrentRecord] = useState(null),
90
91
  [isAdding, setIsAdding] = useState(false),
91
92
  [isSaving, setIsSaving] = useState(false),
92
- [isEditorShown, setIsEditorShownRaw] = useState(false),
93
93
  [isEditorViewOnly, setIsEditorViewOnly] = useState(canEditorViewOnly), // current state of whether editor is in view-only mode
94
94
  [lastSelection, setLastSelection] = useState(),
95
95
  setIsIgnoreNextSelectionChange = (bool) => {
@@ -99,11 +99,15 @@ export default function withEditor(WrappedComponent, isTree = false) {
99
99
  return isIgnoreNextSelectionChangeRef.current;
100
100
  },
101
101
  setIsEditorShown = (bool) => {
102
- setIsEditorShownRaw(bool);
102
+ isEditorShownRef.current = bool;
103
+ forceUpdate();
103
104
  if (!bool && onEditorClose) {
104
105
  onEditorClose();
105
106
  }
106
107
  },
108
+ getIsEditorShown = () => {
109
+ return isEditorShownRef.current;
110
+ },
107
111
  setIsWaitModalShown = (bool) => {
108
112
  const
109
113
  dispatch = UiGlobals.redux?.dispatch,
@@ -685,8 +689,18 @@ export default function withEditor(WrappedComponent, isTree = false) {
685
689
  };
686
690
 
687
691
  useEffect(() => {
688
- setEditorMode(calculateEditorMode());
689
692
 
693
+ if (editorType === EDITOR_TYPE__SIDE) {
694
+ if (selection?.length) { // || isAdding
695
+ // there is a selection, so show the editor
696
+ setIsEditorShown(true);
697
+ } else {
698
+ // no selection, so close the editor
699
+ setIsEditorShown(false);
700
+ }
701
+ }
702
+
703
+ setEditorMode(calculateEditorMode());
690
704
  setLastSelection(selection);
691
705
 
692
706
  // Push isIgnoreNextSelectionChange until after a microtask to ensure all
@@ -722,7 +736,8 @@ export default function withEditor(WrappedComponent, isTree = false) {
722
736
  alreadyHasWithEditor={true}
723
737
  currentRecord={currentRecord}
724
738
  setCurrentRecord={setCurrentRecord}
725
- isEditorShown={isEditorShown}
739
+ isEditorShown={getIsEditorShown()}
740
+ getIsEditorShown={getIsEditorShown}
726
741
  isEditorViewOnly={isEditorViewOnly}
727
742
  isAdding={isAdding}
728
743
  isSaving={isSaving}
@@ -300,7 +300,7 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
300
300
  isEmptySelection() ||
301
301
  isMultiSelection() ||
302
302
  isProtectedValue() ||
303
- (canRecordBeEdited && !canRecordBeEdited(selection))
303
+ (canRecordBeDeleted && !canRecordBeDeleted(selection))
304
304
  ) {
305
305
  isDisabled = true;
306
306
  }
@@ -66,7 +66,7 @@ export default function withSideEditor(WrappedComponent, isTree = false) {
66
66
  isSideEditor={true}
67
67
  {...props}
68
68
  />}
69
- east={<Editor
69
+ east={props.isEditorShown && <Editor
70
70
  {...propsToPass}
71
71
  editorType={EDITOR_TYPE__SIDE}
72
72
  {..._editor}
@@ -0,0 +1,10 @@
1
+ import { createIcon } from "../Gluestack/icon";
2
+ import { Path, Svg } from 'react-native-svg';
3
+
4
+ const SvgComponent = createIcon({
5
+ Root: Svg,
6
+ viewBox: '0 0 77.88 66.87',
7
+ path: <Path d="M65.2 0c-7 0-12.68 5.68-12.68 12.68 0 2.66.82 5.13 2.23 7.17L40.94 41.67c-.65-.1-1.32-.17-2-.17s-1.35.07-2 .17L23.13 19.85c1.4-2.04 2.23-4.51 2.23-7.17C25.37 5.68 19.69 0 12.68 0S0 5.68 0 12.68s5.68 12.68 12.68 12.68c.68 0 1.35-.07 2-.17l13.81 21.82c-1.4 2.04-2.23 4.51-2.23 7.17 0 7 5.68 12.68 12.68 12.68s12.68-5.68 12.68-12.68c0-2.66-.82-5.13-2.23-7.17L63.2 25.19c.65.1 1.32.17 2 .17 7 0 12.68-5.68 12.68-12.68S72.21 0 65.2 0z"/>,
8
+ });
9
+
10
+ export default SvgComponent
@@ -8,13 +8,14 @@ import {
8
8
  } from '@project-components/Gluestack';
9
9
  import clsx from 'clsx';
10
10
  import * as Progress from 'react-native-progress';
11
- import useForceUpdate from '@onehat/ui/src/Hooks/useForceUpdate';
11
+ import useForceUpdate from '../../Hooks/useForceUpdate';
12
12
  import {
13
13
  PROGRESS__NONE_FOUND,
14
14
  PROGRESS__IN_PROCESS,
15
15
  PROGRESS__COMPLETED,
16
16
  PROGRESS__FAILED,
17
17
  PROGRESS__STUCK,
18
+ PROGRESS__UNSTUCK,
18
19
  } from '../../Constants/Progress.js';
19
20
  import {
20
21
  MOMENT_DATE_FORMAT_2,
@@ -27,6 +28,7 @@ import withAlert from '../Hoc/withAlert.js';
27
28
  import Loading from '../Messages/Loading.js';
28
29
  import ChevronLeft from '../Icons/ChevronLeft.js';
29
30
  import ChevronRight from '../Icons/ChevronRight.js';
31
+ import RotateLeft from '../Icons/RotateLeft.js';
30
32
  import Play from '../Icons/Play.js';
31
33
  import EllipsisHorizontal from '../Icons/EllipsisHorizontal.js';
32
34
  import Stop from '../Icons/Stop.js';
@@ -63,6 +65,7 @@ function AsyncOperation(props) {
63
65
  getProgressUpdates = false,
64
66
  parseProgress, // optional fn, accepts 'response' as arg and returns an object like this: { status, errors, started, lastUpdated, timeElapsed, count, current, total, percentage }
65
67
  updateInterval = 10000, // ms
68
+ progressColor = '#666',
66
69
 
67
70
  // withComponent
68
71
  self,
@@ -101,6 +104,13 @@ function AsyncOperation(props) {
101
104
  setFormValues = (values) => {
102
105
  formValuesRef.current = values;
103
106
  },
107
+ isStuckRef = useRef(false),
108
+ getIsStuck = () => {
109
+ return isStuckRef.current;
110
+ },
111
+ setIsStuck = (bool) => {
112
+ isStuckRef.current = bool;
113
+ },
104
114
  getFooter = () => {
105
115
  switch(getMode()) {
106
116
  case INIT:
@@ -126,19 +136,28 @@ function AsyncOperation(props) {
126
136
  // />
127
137
  // </Toolbar>;
128
138
  case RESULTS:
129
- return <Toolbar>
130
- <Button
139
+ let button;
140
+ if (getIsStuck()) {
141
+ button = <Button
142
+ text="Unstick"
143
+ icon={RotateLeft}
144
+ onPress={() => unstick()}
145
+ />;
146
+ } else {
147
+ button = <Button
131
148
  text="Reset"
132
149
  icon={ChevronLeft}
133
150
  onPress={() => resetToInitialState()}
134
- />
151
+ />;
152
+ }
153
+ return <Toolbar>
154
+ {button}
135
155
  </Toolbar>;
136
156
  }
137
157
  },
138
158
  [footer, setFooter] = useState(getFooter()),
139
159
  [results, setResults] = useState(null),
140
160
  [progress, setProgress] = useState(null),
141
- [isStuck, setIsStuck] = useState(false),
142
161
  [isReady, setIsReady] = useState(false),
143
162
  showResults = (results) => {
144
163
  setMode(RESULTS);
@@ -193,7 +212,6 @@ function AsyncOperation(props) {
193
212
  <Text>{message}</Text>;
194
213
  }
195
214
  showResults(results);
196
-
197
215
  },
198
216
  getProgress = (immediately = false) => {
199
217
  if (!getProgressUpdates) {
@@ -266,17 +284,17 @@ function AsyncOperation(props) {
266
284
 
267
285
  const className = 'text-lg';
268
286
  renderItems.push(<Text className={className + ' ' + color} key="status">Status: {statusMessage}</Text>);
269
- if (!_.isNil(percentage)) {
270
- renderItems.push(<HStack className="mb-2" key="progress">
287
+ if (!_.isNil(percentage) && status !== PROGRESS__COMPLETED) {
288
+ renderItems.push(<VStack key="progress">
271
289
  <Progress.Bar
272
290
  animated={true}
273
291
  progress={percentage / 100}
274
292
  width={175}
275
- height={20}
276
- color="#666"
293
+ height={15}
294
+ color={progressColor}
277
295
  />
278
- <Text className={className + ' pl-1'}>{percentage}%</Text>
279
- </HStack>);
296
+ <Text className={className}>{percentage}%</Text>
297
+ </VStack>);
280
298
  }
281
299
  if (started) {
282
300
  const startedMoment = moment(started);
@@ -296,10 +314,10 @@ function AsyncOperation(props) {
296
314
  if (!_.isNil(count) && count !== 0) {
297
315
  renderItems.push(<Text className={className} key="count">Count: {count}</Text>);
298
316
  }
299
- if (!_.isNil(current) && !_.isNil(total) && current !== 0 && total !== 0) {
317
+ if (!_.isNil(current) && !_.isNil(total)) {
300
318
  renderItems.push(<Text className={className} key="currentTotal">Current/Total: {current} / {total}</Text>);
301
319
  }
302
- if (!_.isNil(message)) {
320
+ if (!_.isNil(message) && !_.isEmpty(message)) {
303
321
  renderItems.push(<Text className={className} key="message">{message}</Text>);
304
322
  }
305
323
  if (!_.isNil(errors)) {
@@ -331,6 +349,35 @@ function AsyncOperation(props) {
331
349
  fetchProgress(true); // isInitial
332
350
  }
333
351
  },
352
+ unstick = async () => {
353
+ stopGettingProgress();
354
+ setMode(PROCESSING);
355
+ setFooter(getFooter());
356
+
357
+ const
358
+ method = Repository.methods.edit,
359
+ uri = Repository.getModel() + '/unstickProcess',
360
+ data = {
361
+ process
362
+ };
363
+ const
364
+ result = await Repository._send(method, uri, data);
365
+
366
+ const response = Repository._processServerResponse(result);
367
+ if (!response?.success) {
368
+ alert(response.message || 'Error unsticking process on server.');
369
+ resetToInitialState();
370
+ return;
371
+ }
372
+
373
+ if (response.root?.status !== PROGRESS__UNSTUCK) {
374
+ alert('Process could not be unstuck.');
375
+ return;
376
+ }
377
+
378
+ alert('Process unstuck.');
379
+ resetToInitialState();
380
+ },
334
381
  resetToInitialState = () => {
335
382
  setMode(START);
336
383
  setFooter(getFooter());
@@ -13,7 +13,7 @@ export default function CenterBox(props) {
13
13
  'p-2',
14
14
  );
15
15
  if (props.className) {
16
- className += props.className;
16
+ className += ' ' + props.className;
17
17
  }
18
18
  return <Box
19
19
  {...props}
@@ -29,7 +29,7 @@ function ManagerScreen(props) {
29
29
  [isRendered, setIsRendered] = useState(false),
30
30
  [isModeSet, setIsModeSet] = useState(false),
31
31
  [allowSideBySide, setAllowSideBySide] = useState(false),
32
- [mode, setModeRaw] = useState(SCREEN_MODES__FULL),
32
+ [mode, setModeRaw] = useState(SCREEN_MODES__SIDE),
33
33
  actualMode = (!allowSideBySide || mode === SCREEN_MODES__FULL) ? SCREEN_MODES__FULL : SCREEN_MODES__SIDE,
34
34
  setMode = (newMode) => {
35
35
  if (!allowSideBySide && newMode === SCREEN_MODES__SIDE) {
@@ -56,8 +56,8 @@ export default function Pagination(props) {
56
56
  let items = [],
57
57
  isDisabled = false;
58
58
  if (showMoreOnly) {
59
- isDisabled = (pageEnd === total);
60
- if (showPagination) {
59
+ if (totalPages > 1 && showPagination) {
60
+ isDisabled = (pageEnd === total);
61
61
  items.push(<Button
62
62
  {...testProps('showMoreBtn')}
63
63
  key="showMoreBtn"
@@ -88,120 +88,122 @@ export default function Pagination(props) {
88
88
  self={self}
89
89
  />);
90
90
  }
91
- isDisabled = page === 1;
92
- if (showPagination) {
93
- items.push(<IconButton
94
- {...testProps('firstPageBtn')}
95
- key="firstPageBtn"
96
- reference="firstPageBtn"
97
- className="Pagination-firstPageBtn"
98
- parent={self}
99
- isDisabled={isDisabled}
100
- icon={AnglesLeft}
101
- _icon={iconProps}
102
- onPress={() => Repository.setPage(1)}
103
- tooltip="First Page"
104
- />);
105
- items.push(<IconButton
106
- {...testProps('prevPageBtn')}
107
- key="prevPageBtn"
108
- reference="prevPageBtn"
109
- className="Pagination-prevPageBtn"
110
- parent={self}
111
- isDisabled={isDisabled}
112
- icon={AngleLeft}
113
- _icon={iconProps}
114
- onPress={() => Repository.prevPage()}
115
- tooltip="Previous Page"
116
- />);
91
+ if (totalPages > 1) {
92
+ isDisabled = page === 1;
93
+ if (showPagination) {
94
+ items.push(<IconButton
95
+ {...testProps('firstPageBtn')}
96
+ key="firstPageBtn"
97
+ reference="firstPageBtn"
98
+ className="Pagination-firstPageBtn"
99
+ parent={self}
100
+ isDisabled={isDisabled}
101
+ icon={AnglesLeft}
102
+ _icon={iconProps}
103
+ onPress={() => Repository.setPage(1)}
104
+ tooltip="First Page"
105
+ />);
106
+ items.push(<IconButton
107
+ {...testProps('prevPageBtn')}
108
+ key="prevPageBtn"
109
+ reference="prevPageBtn"
110
+ className="Pagination-prevPageBtn"
111
+ parent={self}
112
+ isDisabled={isDisabled}
113
+ icon={AngleLeft}
114
+ _icon={iconProps}
115
+ onPress={() => Repository.prevPage()}
116
+ tooltip="Previous Page"
117
+ />);
117
118
 
118
- isDisabled = page === totalPages || totalPages <= 1;
119
- items.push(<IconButton
120
- {...testProps('nextPageBtn')}
121
- key="nextPageBtn"
122
- reference="nextPageBtn"
123
- className="Pagination-nextPageBtn"
124
- parent={self}
125
- isDisabled={isDisabled}
126
- icon={AngleRight}
127
- _icon={iconProps}
128
- onPress={() => Repository.nextPage()}
129
- tooltip="Next Page"
130
- />);
131
- items.push(<IconButton
132
- {...testProps('lastPageBtn')}
133
- key="lastPageBtn"
134
- reference="lastPageBtn"
135
- className="Pagination-lastPageBtn"
136
- parent={self}
137
- isDisabled={isDisabled}
138
- icon={AnglesRight}
139
- _icon={iconProps}
140
- onPress={() => Repository.setPage(totalPages)}
141
- tooltip="Last Page"
142
- />);
143
- if (!minimize) {
144
- items.push(<Text
145
- key="page"
146
- className="Pagination-page mx-1"
147
- >Page</Text>);
148
- items.push(<Input
149
- {...testProps('pageInput')}
150
- key="pageInput"
151
- reference="pageInput"
119
+ isDisabled = page === totalPages || totalPages <= 1;
120
+ items.push(<IconButton
121
+ {...testProps('nextPageBtn')}
122
+ key="nextPageBtn"
123
+ reference="nextPageBtn"
124
+ className="Pagination-nextPageBtn"
152
125
  parent={self}
153
- keyboardType="numeric"
154
- value={page?.toString()}
155
- onChangeValue={(value) => Repository.setPage(value)}
156
- maxValue={totalPages}
157
- isDisabled={totalPages === 1}
158
- className={clsx(
159
- 'Pagination-pageInput',
160
- 'min-w-[40px]',
161
- 'w-[40px]',
162
- 'text-center',
163
- 'bg-grey-100',
164
- )}
165
- textAlignIsCenter={true}
166
- tooltip="Set Page"
167
- tooltipClassName="w-[40px]"
126
+ isDisabled={isDisabled}
127
+ icon={AngleRight}
128
+ _icon={iconProps}
129
+ onPress={() => Repository.nextPage()}
130
+ tooltip="Next Page"
131
+ />);
132
+ items.push(<IconButton
133
+ {...testProps('lastPageBtn')}
134
+ key="lastPageBtn"
135
+ reference="lastPageBtn"
136
+ className="Pagination-lastPageBtn"
137
+ parent={self}
138
+ isDisabled={isDisabled}
139
+ icon={AnglesRight}
140
+ _icon={iconProps}
141
+ onPress={() => Repository.setPage(totalPages)}
142
+ tooltip="Last Page"
143
+ />);
144
+ if (!minimize) {
145
+ items.push(<Text
146
+ key="page"
147
+ className="Pagination-page mx-1"
148
+ >Page</Text>);
149
+ items.push(<Input
150
+ {...testProps('pageInput')}
151
+ key="pageInput"
152
+ reference="pageInput"
153
+ parent={self}
154
+ keyboardType="numeric"
155
+ value={page?.toString()}
156
+ onChangeValue={(value) => Repository.setPage(value)}
157
+ maxValue={totalPages}
158
+ isDisabled={totalPages === 1}
159
+ className={clsx(
160
+ 'Pagination-pageInput',
161
+ 'min-w-[40px]',
162
+ 'w-[40px]',
163
+ 'text-center',
164
+ 'bg-grey-100',
165
+ )}
166
+ textAlignIsCenter={true}
167
+ tooltip="Set Page"
168
+ tooltipClassName="w-[40px]"
169
+ />);
170
+ items.push(<Text
171
+ key="totalPages"
172
+ className={clsx(
173
+ 'Pagination-totalPages',
174
+ 'whitespace-nowrap',
175
+ 'inline-flex',
176
+ 'mx-1',
177
+ )}
178
+ >{`of ${totalPages}`}</Text>);
179
+ }
180
+ }
181
+
182
+ if (showPagination && !minimize && !disablePageSize) {
183
+ items.push(<PageSizeSelect
184
+ {...testProps('pageSize')}
185
+ key="pageSize"
186
+ reference="pageSize"
187
+ parent={self}
188
+ pageSize={pageSize}
189
+ Repository={Repository}
168
190
  />);
191
+ }
192
+ if (showPagination && !minimize) {
193
+ let pageSpan = `${pageStart} – ${pageEnd}`;
194
+ if (pageStart === pageEnd) {
195
+ pageSpan = pageStart;
196
+ }
169
197
  items.push(<Text
170
- key="totalPages"
198
+ key="pageDisplay"
171
199
  className={clsx(
172
- 'Pagination-totalPages',
200
+ 'Pagination-pageDisplay',
173
201
  'whitespace-nowrap',
174
202
  'inline-flex',
175
203
  'mx-1',
176
204
  )}
177
- >{`of ${totalPages}`}</Text>);
178
- }
179
- }
180
-
181
- if (showPagination && !minimize && !disablePageSize) {
182
- items.push(<PageSizeSelect
183
- {...testProps('pageSize')}
184
- key="pageSize"
185
- reference="pageSize"
186
- parent={self}
187
- pageSize={pageSize}
188
- Repository={Repository}
189
- />);
190
- }
191
- if (showPagination && !minimize) {
192
- let pageSpan = `${pageStart} – ${pageEnd}`;
193
- if (pageStart === pageEnd) {
194
- pageSpan = pageStart;
205
+ >{`Displaying ${pageSpan} of ${total}`}</Text>);
195
206
  }
196
- items.push(<Text
197
- key="pageDisplay"
198
- className={clsx(
199
- 'Pagination-pageDisplay',
200
- 'whitespace-nowrap',
201
- 'inline-flex',
202
- 'mx-1',
203
- )}
204
- >{`Displaying ${pageSpan} of ${total}`}</Text>);
205
207
  }
206
208
  }
207
209
  return <HStack
@@ -7,7 +7,7 @@ import Pagination from './Pagination.js'
7
7
  import Toolbar from './Toolbar.js'
8
8
  import _ from 'lodash';
9
9
 
10
- export default forwardRef(function PaginationToolbar(props, ref) {
10
+ const PaginationToolbar = forwardRef((props, ref) => {
11
11
  const {
12
12
  toolbarItems = [],
13
13
  disablePageSize = false,
@@ -65,3 +65,5 @@ export default forwardRef(function PaginationToolbar(props, ref) {
65
65
  />
66
66
  </Toolbar>;
67
67
  });
68
+
69
+ export default PaginationToolbar;
@@ -10,9 +10,12 @@ import {
10
10
  } from '../../Constants/UiModes.js';
11
11
  import UiGlobals from '../../UiGlobals.js';
12
12
 
13
- export default forwardRef(function Toolbar(props, ref) {
14
-
15
- const styles = UiGlobals.styles;
13
+ const Toolbar = forwardRef((props, ref) => {
14
+ const {
15
+ children,
16
+ ...propsToPass
17
+ } = props,
18
+ styles = UiGlobals.styles;
16
19
 
17
20
  let className = clsx(
18
21
  'Toolbar',
@@ -34,11 +37,10 @@ export default forwardRef(function Toolbar(props, ref) {
34
37
  }
35
38
  let toolbar = <HStackNative
36
39
  ref={ref}
40
+ {...propsToPass}
37
41
  className={className}
38
- style={props.style || {}}
39
- onLayout={props.onLayout}
40
42
  >
41
- {props.children}
43
+ {children}
42
44
  </HStackNative>;
43
45
 
44
46
  if (CURRENT_MODE === UI_MODE_NATIVE) {
@@ -53,3 +55,5 @@ export default forwardRef(function Toolbar(props, ref) {
53
55
 
54
56
  return toolbar;
55
57
  });
58
+
59
+ export default Toolbar;
@@ -1,6 +1,7 @@
1
1
  import { useMemo, useEffect, } from 'react';
2
2
  import {
3
3
  Box,
4
+ BoxNative,
4
5
  HStackNative,
5
6
  Icon,
6
7
  Spinner,
@@ -128,7 +129,7 @@ export default function TreeNode(props) {
128
129
  'items-center',
129
130
  'flex-1',
130
131
  'grow-1',
131
- 'select-none',
132
+ !showNodeHandle && 'select-none',
132
133
  'cursor-pointer',
133
134
  );
134
135
 
@@ -169,16 +170,44 @@ export default function TreeNode(props) {
169
170
  canDrag={nodeCanDrag}
170
171
  />}
171
172
 
172
- {hasChildren && <IconButton
173
- {...testProps('expandBtn')}
174
- icon={isExpanded ? ChevronDown : ChevronRight}
175
- onPress={(e) => onToggle(datum, e)}
176
- className="ml-2"
177
- />}
178
-
173
+ {hasChildren ?
174
+ <IconButton
175
+ {...testProps('expandBtn')}
176
+ icon={isExpanded ? ChevronDown : ChevronRight}
177
+ _icon={{
178
+ className: clsx(
179
+ 'text-gray-400',
180
+ ),
181
+ }}
182
+ onPress={(e) => onToggle(datum, e)}
183
+ className={clsx(
184
+ 'TreeNode-expandBtn',
185
+ 'ml-2',
186
+ styles.TREE_NODE_EXPAND_BTN_CLASSNAME,
187
+ )}
188
+ /> :
189
+ <BoxNative
190
+ {...testProps('spacer')}
191
+ className={clsx(
192
+ 'TreeNode-spacer',
193
+ 'ml-2',
194
+ styles.TREE_NODE_SPACER_CLASSNAME,
195
+ )}
196
+ />}
197
+
179
198
  {isLoading && <Spinner className="px-2" />}
180
199
 
181
- {!isLoading && icon && <Icon as={icon} className="ml-2 mr-1" />}
200
+ {!isLoading && icon &&
201
+ <Icon
202
+ as={icon}
203
+ className={clsx(
204
+ 'TreeNode-icon',
205
+ 'ml-2',
206
+ 'mr-1',
207
+ styles.TREE_NODE_ICON_CLASSNAME,
208
+ nodeProps?._icon?.className ?? null,
209
+ )}
210
+ />}
182
211
 
183
212
  {text && <TextNative
184
213
  numberOfLines={1}
@@ -192,6 +221,7 @@ export default function TreeNode(props) {
192
221
  'flex-1',
193
222
  'text-ellipsis',
194
223
  styles.TREE_NODE_CLASSNAME,
224
+ nodeProps?._text?.className ?? null,
195
225
  )}
196
226
  >{text}</TextNative>}
197
227
 
@@ -159,6 +159,10 @@ function Viewer(props) {
159
159
  }
160
160
  if (type?.match(/(Tag|TagEditor)$/)) {
161
161
  viewerTypeProps.isViewOnly = true;
162
+ viewerTypeProps.SourceRepository = Repository;
163
+ }
164
+ if (type?.match(/(Grid|GridEditor)$/)) {
165
+ viewerTypeProps.canEditorViewOnly = true;
162
166
  }
163
167
  const Element = getComponentFromType(type);
164
168
 
@@ -253,9 +257,9 @@ function Viewer(props) {
253
257
  label = propertyDef.title;
254
258
  }
255
259
 
256
- let value = record?.properties[name]?.displayValue || null;
260
+ let value = record?.properties?.[name]?.displayValue || record?.[name] || null;
257
261
  const
258
- schema = record?.repository.getSchema(),
262
+ schema = record?.repository?.getSchema(),
259
263
  propertyDefinition = schema?.getPropertyDefinition(name);
260
264
  if (propertyDefinition?.isFk) {
261
265
  // value above is the id, get the actual display value
@@ -479,6 +483,7 @@ function Viewer(props) {
479
483
  'Viewer-VStackNative',
480
484
  'h-full',
481
485
  'bg-white',
486
+ '[transform:translateZ(0)]', // so embedded FAB will be relative to this container, not to viewport
482
487
  );
483
488
  if (props.className) {
484
489
  className += ' ' + props.className;
@@ -2,4 +2,5 @@ export const PROGRESS__NONE_FOUND = 'NONE_FOUND';
2
2
  export const PROGRESS__IN_PROCESS = 'IN_PROCESS';
3
3
  export const PROGRESS__COMPLETED = 'COMPLETED';
4
4
  export const PROGRESS__FAILED = 'FAILED';
5
- export const PROGRESS__STUCK = 'STUCK';
5
+ export const PROGRESS__STUCK = 'STUCK';
6
+ export const PROGRESS__UNSTUCK = 'UNSTUCK';