@onehat/ui 0.4.71 → 0.4.72

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 (44) hide show
  1. package/package.json +1 -1
  2. package/src/Components/Buttons/Button.js +13 -6
  3. package/src/Components/Container/ScreenContainer.js +1 -0
  4. package/src/Components/Form/Field/Combo/MeterTypesCombo.js +4 -2
  5. package/src/Components/Form/Field/Input.js +5 -4
  6. package/src/Components/Grid/Grid.js +16 -7
  7. package/src/Components/Grid/GridHeaderRow.js +1 -1
  8. package/src/Components/Grid/GridRow.js +35 -34
  9. package/src/Components/Grid/RowDragHandle.js +25 -10
  10. package/src/Components/Grid/RowHandle.js +55 -0
  11. package/src/{Hooks → Components/Grid}/useAsyncRenderers.js +1 -1
  12. package/src/Components/Hoc/Secondary/withSecondaryData.js +2 -1
  13. package/src/Components/Hoc/Secondary/withSecondaryEditor.js +3 -2
  14. package/src/Components/Hoc/Secondary/withSecondarySelection.js +3 -2
  15. package/src/Components/Hoc/Secondary/withSecondaryValue.js +2 -1
  16. package/src/Components/Hoc/withAlert.js +3 -1
  17. package/src/Components/Hoc/withCollapsible.js +9 -4
  18. package/src/Components/Hoc/withComponent.js +6 -0
  19. package/src/Components/Hoc/withContextMenu.js +6 -0
  20. package/src/Components/Hoc/withData.js +3 -2
  21. package/src/Components/Hoc/withDnd.js +16 -8
  22. package/src/Components/Hoc/withDraggable.js +21 -5
  23. package/src/Components/Hoc/withEditor.js +2 -1
  24. package/src/Components/Hoc/withEvents.js +11 -1
  25. package/src/Components/Hoc/withFilters.js +7 -2
  26. package/src/Components/Hoc/withModal.js +3 -2
  27. package/src/Components/Hoc/withPdfButtons.js +3 -2
  28. package/src/Components/Hoc/withPermissions.js +3 -2
  29. package/src/Components/Hoc/withPresetButtons.js +3 -2
  30. package/src/Components/Hoc/withSelection.js +2 -8
  31. package/src/Components/Hoc/withToast.js +4 -2
  32. package/src/Components/Hoc/withTooltip.js +10 -1
  33. package/src/Components/Hoc/withValue.js +3 -9
  34. package/src/Components/Messages/GlobalModals.js +47 -0
  35. package/src/Components/Tree/Tree.js +22 -6
  36. package/src/Components/Tree/TreeNode.js +11 -11
  37. package/src/Components/Tree/TreeNodeDragHandle.js +8 -4
  38. package/src/Components/Viewer/MeterTypeText.js +21 -1
  39. package/src/Constants/MeterTypes.js +2 -0
  40. package/src/Functions/addIconProps.js +46 -0
  41. package/src/Functions/testProps.js +1 -1
  42. package/src/Hooks/useWhyDidYouUpdate.js +33 -0
  43. package/src/PlatformImports/Web/Attachments.js +1 -1
  44. package/src/Components/Hoc/withBlank.js +0 -10
@@ -18,8 +18,8 @@ import {
18
18
 
19
19
  export function withDragSource(WrappedComponent) {
20
20
  return forwardRef((props, ref) => {
21
-
22
- if (!props.isDragSource) {
21
+
22
+ if (!props.isDragSource || props.alreadyHasDragSource) {
23
23
  return <WrappedComponent {...props} ref={ref} />;
24
24
  }
25
25
 
@@ -37,6 +37,7 @@ export function withDragSource(WrappedComponent) {
37
37
  dragOptions = null,
38
38
  dropEffect = null,
39
39
  // onDrag,
40
+ onDragStart = null,
40
41
  onDragEnd = null,
41
42
  canDrag = null,
42
43
  isDragging = null,
@@ -63,10 +64,15 @@ export function withDragSource(WrappedComponent) {
63
64
 
64
65
  return {
65
66
  type: dragSourceType, // Required. This must be either a string or a symbol. Only the drop targets registered for the same type will react to this item.
66
- item: {
67
- ...dragSourceItem,
68
- getDragProxy,
69
- }, // Required (object or function).
67
+ item: () => { // Required (object or function). If a function, it runs only once at the start of a drag.
68
+ if (dragSourceItem.onDragStart) {
69
+ dragSourceItem.onDragStart();
70
+ }
71
+ return {
72
+ ...dragSourceItem,
73
+ getDragProxy,
74
+ };
75
+ },
70
76
  // When an object, it is a plain JavaScript object describing the data being dragged. This is the only information available to the drop targets about the drag source so it's important to pick the minimal data they need to know. You may be tempted to put a complex reference here, but you should try very hard to avoid doing this because it couples the drag sources and drop targets. It's a good idea to use something like { id }.
71
77
  // When a function, it is fired at the beginning of the drag operation and returns an object representing the drag operation (see first bullet). If null is returned, the drag operation is cancelled.
72
78
  previewOptions: dragPreviewOptions, // Optional. A plain JavaScript object describing drag preview options.
@@ -119,6 +125,7 @@ export function withDragSource(WrappedComponent) {
119
125
 
120
126
  return <WrappedComponent
121
127
  {...props}
128
+ alreadyHasDragSource={true}
122
129
  ref={ref}
123
130
  canDrag={stateCanDrag}
124
131
  isDragging={stateIsDragging}
@@ -133,7 +140,7 @@ export function withDragSource(WrappedComponent) {
133
140
  export function withDropTarget(WrappedComponent) {
134
141
  return forwardRef((props, ref) => {
135
142
 
136
- if (!props.isDropTarget) {
143
+ if (!props.isDropTarget || props.alreadyHasDropTarget) {
137
144
  return <WrappedComponent {...props} ref={ref} />;
138
145
  }
139
146
 
@@ -197,8 +204,9 @@ export function withDropTarget(WrappedComponent) {
197
204
  dropTargetRef(localTargetRef); // register DOM node with react-dnd
198
205
 
199
206
  return <WrappedComponent
200
- canDrop={stateCanDrop}
207
+ alreadyHasDropTarget={true}
201
208
  ref={ref}
209
+ canDrop={stateCanDrop}
202
210
  isOver={isOver}
203
211
  dropTargetRef={localTargetRef}
204
212
  draggedItem={draggedItem} // Pass the dragged item
@@ -23,7 +23,7 @@ import getComponentFromType from '../../Functions/getComponentFromType.js';
23
23
  export default function withDraggable(WrappedComponent) {
24
24
  return forwardRef((props, ref) => {
25
25
 
26
- if (!props.isDraggable) {
26
+ if (!props.isDraggable || props.alreadyHasDraggable) {
27
27
  return <WrappedComponent {...props} ref={ref} />;
28
28
  }
29
29
 
@@ -237,7 +237,11 @@ export default function withDraggable(WrappedComponent) {
237
237
  {...draggableProps}
238
238
  >
239
239
  <div ref={nodeRef} className="nsResize">
240
- <WrappedComponent {...propsToPass} ref={ref} />
240
+ <WrappedComponent
241
+ {...propsToPass}
242
+ alreadyHasDraggable={true}
243
+ ref={ref}
244
+ />
241
245
  </div>
242
246
  </Draggable>;
243
247
  } else if (mode === HORIZONTAL) {
@@ -252,7 +256,11 @@ export default function withDraggable(WrappedComponent) {
252
256
  {...draggableProps}
253
257
  >
254
258
  <div ref={nodeRef} className="ewResize" style={{ height: '100%', }}>
255
- <WrappedComponent {...propsToPass} ref={ref} />
259
+ <WrappedComponent
260
+ {...propsToPass}
261
+ alreadyHasDraggable={true}
262
+ ref={ref}
263
+ />
256
264
  </div>
257
265
  </Draggable>;
258
266
  }
@@ -268,14 +276,22 @@ export default function withDraggable(WrappedComponent) {
268
276
  nodeRef={nodeRef}
269
277
  {...draggableProps}
270
278
  >
271
- <WrappedComponent {...propsToPass} ref={nodeRef} />
279
+ <WrappedComponent
280
+ {...propsToPass}
281
+ alreadyHasDraggable={true}
282
+ ref={nodeRef}
283
+ />
272
284
  </Draggable>;
273
285
  } else if (CURRENT_MODE === UI_MODE_NATIVE) {
274
286
 
275
287
  // NOT YET IMPLEMENTED
276
288
  // Really need to replace most of this, as much of it is web-centric.
277
289
 
278
- return <WrappedComponent {...propsToPass} ref={ref} />; // TEMP
290
+ return <WrappedComponent
291
+ {...propsToPass}
292
+ alreadyHasDraggable={true}
293
+ ref={ref}
294
+ />;
279
295
 
280
296
  }
281
297
  });
@@ -21,7 +21,7 @@ import _ from 'lodash';
21
21
  export default function withEditor(WrappedComponent, isTree = false) {
22
22
  return forwardRef((props, ref) => {
23
23
 
24
- if (props.disableWithEditor) {
24
+ if (props.disableWithEditor || props.alreadyHasWithEditor) {
25
25
  return <WrappedComponent {...props} ref={ref} isTree={isTree} />;
26
26
  }
27
27
 
@@ -702,6 +702,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
702
702
  {...props}
703
703
  ref={ref}
704
704
  disableWithEditor={false}
705
+ alreadyHasWithEditor={true}
705
706
  currentRecord={currentRecord}
706
707
  setCurrentRecord={setCurrentRecord}
707
708
  isEditorShown={isEditorShown}
@@ -2,9 +2,19 @@ import { forwardRef } from 'react';
2
2
 
3
3
  export default function withEvents(WrappedComponent) {
4
4
  return forwardRef((props, ref) => {
5
+
6
+ if (props.alreadyHasWithEvents) {
7
+ return <WrappedComponent {...props} ref={ref} />;
8
+ }
9
+
5
10
  const {
6
11
  onEvent,
7
12
  } = props;
8
- return <WrappedComponent fireEvent={onEvent} {...props} ref={ref} />;
13
+ return <WrappedComponent
14
+ {...props}
15
+ alreadyHasWithEvents={true}
16
+ ref={ref}
17
+ fireEvent={onEvent}
18
+ />;
9
19
  });
10
20
  }
@@ -42,7 +42,7 @@ const isWindows = Platform.OS === 'windows';
42
42
  export default function withFilters(WrappedComponent) {
43
43
  return forwardRef((props, ref) => {
44
44
 
45
- if (!props.useFilters) {
45
+ if (!props.useFilters || props.alreadyHasWithFilters) {
46
46
  return <WrappedComponent {...props} ref={ref} />;
47
47
  }
48
48
 
@@ -695,7 +695,12 @@ export default function withFilters(WrappedComponent) {
695
695
  </HStack>}
696
696
  </Toolbar>;
697
697
 
698
- return <WrappedComponent {...props} topToolbar={toolbar} ref={ref} />;
698
+ return <WrappedComponent
699
+ {...props}
700
+ ref={ref}
701
+ alreadyHasWithFilters={true}
702
+ topToolbar={toolbar}
703
+ />;
699
704
 
700
705
  });
701
706
  }
@@ -19,7 +19,7 @@ import _ from 'lodash';
19
19
  export default function withModal(WrappedComponent) {
20
20
  return forwardRef((props, ref) => {
21
21
 
22
- if (props.disableWithModal || props.showModal) {
22
+ if (props.disableWithModal || props.alreadyHasWithModal) {
23
23
  return <WrappedComponent {...props} ref={ref} />;
24
24
  }
25
25
 
@@ -180,12 +180,13 @@ export default function withModal(WrappedComponent) {
180
180
  <WrappedComponent
181
181
  {...props}
182
182
  disableWithModal={false}
183
+ alreadyHasWithModal={true}
184
+ ref={ref}
183
185
  showModal={showModal}
184
186
  hideModal={onCancel || hideModal}
185
187
  updateModalBody={updateModalBody}
186
188
  isModalShown={isModalShown}
187
189
  whichModal={whichModal}
188
- ref={ref}
189
190
  />
190
191
  {isModalShown &&
191
192
  <Modal
@@ -22,7 +22,7 @@ export default function withPdfButtons(WrappedComponent) {
22
22
  if (props.canUser && !props.canUser(VIEW)) { // permissions
23
23
  showButtons = false;
24
24
  }
25
- if (!showButtons) {
25
+ if (!showButtons || props.alreadyHasWithPdfButtons) {
26
26
  // bypass everything.
27
27
  // If we don't do this, we get an infinite recursion with Form
28
28
  // because this HOC wraps Form and uses Form itself.
@@ -384,9 +384,10 @@ export default function withPdfButtons(WrappedComponent) {
384
384
 
385
385
  return <WrappedComponent
386
386
  {...props}
387
+ ref={ref}
388
+ alreadyHasWithPdfButtons={true}
387
389
  additionalEditButtons={additionalEditButtons}
388
390
  additionalViewButtons={additionalViewButtons}
389
- ref={ref}
390
391
  />;
391
392
  }));
392
393
  }
@@ -97,7 +97,7 @@ export function canUser(permission, modelToCheck = null) {
97
97
  export default function withPermissions(WrappedComponent, forceUsePermissions = false) {
98
98
  return forwardRef((props, ref) => {
99
99
 
100
- if (!props.usePermissions && !forceUsePermissions) {
100
+ if ((!props.usePermissions && !forceUsePermissions) || props.alreadyHasWithPermissions) {
101
101
  return <WrappedComponent {...props} ref={ref} />;
102
102
  }
103
103
 
@@ -126,9 +126,10 @@ export default function withPermissions(WrappedComponent, forceUsePermissions =
126
126
 
127
127
  return <WrappedComponent
128
128
  {...props}
129
+ alreadyHasWithPermissions={true}
130
+ ref={ref}
129
131
  canUser={canUserDecorator}
130
132
  showPermissionsError={showPermissionsError}
131
- ref={ref}
132
133
  />;
133
134
  });
134
135
  }
@@ -41,7 +41,7 @@ const presetButtons = [
41
41
  export default function withPresetButtons(WrappedComponent, isGrid = false) {
42
42
  return forwardRef((props, ref) => {
43
43
 
44
- if (props.disablePresetButtons) {
44
+ if (props.disablePresetButtons || props.alreadyHasWithPresetButtons) {
45
45
  // bypass everything
46
46
  return <WrappedComponent {...props} ref={ref} />;
47
47
  }
@@ -433,8 +433,9 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
433
433
 
434
434
  return <WrappedComponent
435
435
  {...propsToPass}
436
- ref={ref}
437
436
  disablePresetButtons={false}
437
+ alreadyHasWithPresetButtons={true}
438
+ ref={ref}
438
439
  contextMenuItems={[
439
440
  ...localContextMenuItems,
440
441
  ...contextMenuItems,
@@ -12,14 +12,7 @@ import _ from 'lodash';
12
12
  export default function withSelection(WrappedComponent) {
13
13
  return forwardRef((props, ref) => {
14
14
 
15
- if (props.disableWithSelection) {
16
- return <WrappedComponent {...props} ref={ref} />;
17
- }
18
-
19
- if (props.setSelection) {
20
- // bypass everything, since we're already using withSelection() in hierarchy.
21
- // For example, Combo has withSelection(), and intenally it uses Grid which also has withSelection(),
22
- // but we only need it defined once for the whole thing.
15
+ if (props.disableWithSelection || props.alreadyHasWithSelection) {
23
16
  return <WrappedComponent {...props} ref={ref} />;
24
17
  }
25
18
 
@@ -427,6 +420,7 @@ export default function withSelection(WrappedComponent) {
427
420
  {...props}
428
421
  ref={ref}
429
422
  disableWithSelection={false}
423
+ alreadyHasWithSelection={true}
430
424
  selection={getSelection()}
431
425
  getSelection={getSelection}
432
426
  setSelection={setSelection}
@@ -13,7 +13,7 @@ import _ from 'lodash';
13
13
  export default function withToast(WrappedComponent) {
14
14
  return forwardRef((props, ref) => {
15
15
 
16
- if (props.disableWithToast || props.showToast) {
16
+ if (props.disableWithToast || props.alreadyHasWithToast) {
17
17
  return <WrappedComponent {...props} ref={ref} />;
18
18
  }
19
19
 
@@ -80,8 +80,10 @@ export default function withToast(WrappedComponent) {
80
80
 
81
81
  return <WrappedComponent
82
82
  {...props}
83
- showToast={showToast}
83
+ alreadyHasWithToast={true}
84
+ disableWithToast={false}
84
85
  ref={ref}
86
+ showToast={showToast}
85
87
  />;
86
88
  });
87
89
  }
@@ -7,6 +7,11 @@ import _ from 'lodash';
7
7
 
8
8
  export default function withTooltip(WrappedComponent) {
9
9
  return forwardRef((props, ref) => {
10
+
11
+ if (props.alreadyHasWithTooltip) {
12
+ return <WrappedComponent {...props} ref={ref} />;
13
+ }
14
+
10
15
  const {
11
16
  tooltip,
12
17
  tooltipPlacement = 'bottom',
@@ -16,7 +21,11 @@ export default function withTooltip(WrappedComponent) {
16
21
  ...propsToPass
17
22
  } = props;
18
23
 
19
- let component = <WrappedComponent {...propsToPass} ref={ref} />;
24
+ let component = <WrappedComponent
25
+ {...propsToPass}
26
+ alreadyHasWithTooltip={true}
27
+ ref={ref}
28
+ />;
20
29
 
21
30
  if (tooltip || !_.isEmpty(_tooltip)) {
22
31
  component = <Tooltip
@@ -9,14 +9,7 @@ import _ from 'lodash';
9
9
  export default function withValue(WrappedComponent) {
10
10
  return forwardRef((props, ref) => {
11
11
 
12
- if (props.disableWithValue) {
13
- return <WrappedComponent {...props} ref={ref} />;
14
- }
15
-
16
- if (props.setValue) {
17
- // bypass everything, since we're already using withValue() in hierarchy.
18
- // For example, Combo has withValue(), and intenally it uses Input which also has withValue(),
19
- // but we only need it defined once for the whole thing.
12
+ if (props.disableWithValue || props.alreadyHasWithValue) {
20
13
  return <WrappedComponent {...props} ref={ref} />;
21
14
  }
22
15
 
@@ -143,8 +136,9 @@ export default function withValue(WrappedComponent) {
143
136
 
144
137
  return <WrappedComponent
145
138
  {...props}
146
- ref={ref}
147
139
  disableWithValue={false}
140
+ alreadyHasWithValue={true}
141
+ ref={ref}
148
142
  value={convertedValue}
149
143
  setValue={setValueRef.current}
150
144
  onChangeSelection={onChangeSelection}
@@ -0,0 +1,47 @@
1
+
2
+ import { useSelector, useDispatch, } from 'react-redux';
3
+ import {
4
+ selectIsWaitModalShown,
5
+ selectAlertMessage,
6
+ selectDebugMessage,
7
+ selectInfoMessage,
8
+ selectWaitMessage,
9
+ setAlertMessage,
10
+ setDebugMessage,
11
+ setInfoMessage,
12
+ } from '@src/models/Slices/DebugSlice';
13
+ import WaitMessage from './WaitMessage';
14
+ import ErrorMessage from './ErrorMessage';
15
+
16
+
17
+
18
+ export default function GlobalModals() {
19
+ const
20
+ dispatch = useDispatch(),
21
+ isWaitModalShown = useSelector(selectIsWaitModalShown),
22
+ alertMessage = useSelector(selectAlertMessage),
23
+ debugMessage = useSelector(selectDebugMessage),
24
+ infoMessage = useSelector(selectInfoMessage),
25
+ waitMessage = useSelector(selectWaitMessage);
26
+
27
+ return <>
28
+ {isWaitModalShown && <WaitMessage text={waitMessage} />}
29
+ {!isWaitModalShown && alertMessage &&
30
+ <ErrorMessage
31
+ text={alertMessage}
32
+ onOk={() => dispatch(setAlertMessage(null))}
33
+ />}
34
+ {!isWaitModalShown && debugMessage &&
35
+ <ErrorMessage
36
+ text={debugMessage}
37
+ color="green"
38
+ onOk={() => dispatch(setDebugMessage(null))}
39
+ />}
40
+ {!isWaitModalShown && infoMessage &&
41
+ <ErrorMessage
42
+ text={infoMessage}
43
+ color="#000"
44
+ onOk={() => dispatch(setInfoMessage(null))}
45
+ />}
46
+ </>;
47
+ }
@@ -1088,17 +1088,26 @@ function TreeComponent(props) {
1088
1088
  id: item.id,
1089
1089
  item,
1090
1090
  getSelection,
1091
+ isInSelection,
1091
1092
  type: nodeDragSourceType,
1093
+ onDragStart: () => {
1094
+ if (!isInSelection(item)) { // get updated isSelected (will be stale if using one in closure)
1095
+ // reset the selection to just this one node if it's not already selected
1096
+ setSelection([item]);
1097
+ }
1098
+ },
1092
1099
  };
1093
1100
 
1094
1101
  // Prevent root nodes from being dragged, and use custom logic if provided
1095
1102
  nodeDragProps.canDrag = (monitor) => {
1096
1103
  const currentSelection = getSelection();
1097
1104
 
1098
- // Check if any selected node is a root node (can't drag root nodes)
1099
- const hasRootNode = currentSelection.some(node => node.isRoot);
1100
- if (hasRootNode) {
1101
- return false;
1105
+ if (isInSelection(item)) {
1106
+ // make sure root node is not selected (can't drag root nodes)
1107
+ const hasRootNode = currentSelection.some(node => node.isRoot);
1108
+ if (hasRootNode) {
1109
+ return false;
1110
+ }
1102
1111
  }
1103
1112
 
1104
1113
  // Use custom drag validation if provided
@@ -1118,7 +1127,7 @@ function TreeComponent(props) {
1118
1127
  // Add drag preview rendering
1119
1128
  nodeDragProps.getDragProxy = getCustomDragProxy ?
1120
1129
  (dragItem) => getCustomDragProxy(item, getSelection()) :
1121
- null; // Let GlobalDragProxy handle the default case
1130
+ null; // let GlobalDragProxy handle the default case
1122
1131
 
1123
1132
  const dropTargetAccept = 'internal';
1124
1133
  nodeDragProps.isDropTarget = true;
@@ -1178,15 +1187,22 @@ function TreeComponent(props) {
1178
1187
  nodeDragProps.isDragSource = !item.isRoot; // Root nodes cannot be dragged
1179
1188
  nodeDragProps.dragSourceType = nodeDragSourceType;
1180
1189
  if (getNodeDragSourceItem) {
1181
- nodeDragProps.dragSourceItem = getNodeDragSourceItem(item, getSelection, nodeDragSourceType);
1190
+ nodeDragProps.dragSourceItem = getNodeDragSourceItem(item, getSelection, isInSelection, nodeDragSourceType);
1182
1191
  } else {
1183
1192
  nodeDragProps.dragSourceItem = {
1184
1193
  id: item.id,
1185
1194
  item,
1186
1195
  getSelection,
1196
+ isInSelection,
1187
1197
  type: nodeDragSourceType,
1188
1198
  };
1189
1199
  }
1200
+ nodeDragProps.dragSourceItem.onDragStart = () => {
1201
+ if (!isInSelection(item)) { // get updated isSelected (will be stale if using one in closure)
1202
+ // reset the selection to just this one node if it's not already selected
1203
+ setSelection([item]);
1204
+ }
1205
+ };
1190
1206
  if (canNodeMoveExternally) {
1191
1207
  nodeDragProps.canDrag = canNodeMoveExternally;
1192
1208
  }
@@ -16,7 +16,7 @@ import UiGlobals from '../../UiGlobals.js';
16
16
  import withDraggable from '../Hoc/withDraggable.js';
17
17
  import IconButton from '../Buttons/IconButton.js';
18
18
  import { withDragSource, withDropTarget } from '../Hoc/withDnd.js';
19
- import TreeNodeDragHandle from './TreeNodeDragHandle.js';
19
+ import RowHandle from '../Grid/RowHandle.js';
20
20
  import testProps from '../../Functions/testProps.js';
21
21
  import ChevronRight from '../Icons/ChevronRight.js';
22
22
  import ChevronDown from '../Icons/ChevronDown.js';
@@ -40,11 +40,13 @@ export default function TreeNode(props) {
40
40
  nodeProps = {},
41
41
  onToggle,
42
42
  bg,
43
+ isDraggable,
43
44
  isDragSource,
44
45
  isHovered,
45
46
  isHighlighted,
46
47
  isOver,
47
48
  isSelected,
49
+ showSelectHandle,
48
50
  canDrop,
49
51
  draggedItem,
50
52
  validateDrop, // same as canDrop (for visual feedback)
@@ -144,19 +146,21 @@ export default function TreeNode(props) {
144
146
  backgroundColor: bg,
145
147
  }}
146
148
  ref={(element) => {
147
- // Attach both drag and drop refs to the same element
148
- if (dragSourceRef && typeof dragSourceRef === 'function') {
149
- dragSourceRef(element);
150
- }
151
149
  if (dropTargetRef && dropTargetRef.current !== undefined) {
152
150
  // dropTargetRef is a ref object, not a callback
153
151
  dropTargetRef.current = element;
154
152
  }
155
153
  }}
156
154
  >
157
- {isPhantom && <Box t={0} l={0} className="absolute bg-[#f00] h-[2px] w-[2px]" />}
155
+ {isPhantom && <Box className="absolute t-0 l-0 bg-[#f00] h-[2px] w-[2px]" />}
158
156
 
159
- {isDragSource && <TreeNodeDragHandle />}
157
+ {(isDragSource || showSelectHandle) &&
158
+ <RowHandle
159
+ ref={dragSourceRef}
160
+ isDragSource={isDragSource}
161
+ isDraggable={isDraggable}
162
+ showSelectHandle={showSelectHandle}
163
+ />}
160
164
 
161
165
  {hasChildren && <IconButton
162
166
  {...testProps('expandBtn')}
@@ -180,12 +184,8 @@ export default function TreeNode(props) {
180
184
  'flex',
181
185
  'flex-1',
182
186
  'text-ellipsis',
183
- 'select-none',
184
187
  styles.TREE_NODE_CLASSNAME,
185
188
  )}
186
- style={{
187
- userSelect: 'none',
188
- }}
189
189
  >{text}</TextNative>}
190
190
 
191
191
  {content}
@@ -1,3 +1,4 @@
1
+ import { forwardRef } from 'react';
1
2
  import {
2
3
  Icon,
3
4
  VStack,
@@ -6,11 +7,11 @@ import clsx from 'clsx';
6
7
  import styles from '../../Styles/StyleSheets.js';
7
8
  import GripVertical from '../Icons/GripVertical.js';
8
9
 
9
- function TreeNodeDragHandle(props) {
10
+ const TreeNodeDragHandle = forwardRef(function(props, ref) {
10
11
  let className = clsx(
11
12
  'TreeNodeDragHandle',
12
13
  'h-full',
13
- 'w-[14px]',
14
+ 'w-[17px]',
14
15
  'px-[2px]',
15
16
  'border-l-2',
16
17
  'items-center',
@@ -21,14 +22,17 @@ function TreeNodeDragHandle(props) {
21
22
  className += ' ' + props.className;
22
23
  }
23
24
  return <VStack
25
+ {...props}
26
+ ref={ref}
24
27
  style={styles.ewResize}
25
28
  className={className}
26
29
  >
27
30
  <Icon
28
31
  as={GripVertical}
29
32
  size="xs"
30
- className="handle w-full h-full text-[#ccc]" />
33
+ className="handle w-full h-full text-[#ccc]"
34
+ />
31
35
  </VStack>;
32
- }
36
+ });
33
37
 
34
38
  export default TreeNodeDragHandle;
@@ -2,6 +2,13 @@ import {
2
2
  TextNative,
3
3
  } from '@project-components/Gluestack';
4
4
  import clsx from 'clsx';
5
+ import {
6
+ METER_TYPES__HOURS,
7
+ METER_TYPES__MILES,
8
+ METER_TYPES__HOURS_TEXT,
9
+ METER_TYPES__MILES_TEXT,
10
+ } from '../../Constants/MeterTypes';
11
+
5
12
  import UiGlobals from '../../UiGlobals';
6
13
 
7
14
  export default function MeterTypeText(props) {
@@ -17,8 +24,21 @@ export default function MeterTypeText(props) {
17
24
  if (props.className) {
18
25
  className += ' ' + props.className;
19
26
  }
27
+ let meterType = '';
28
+ switch(props.value) {
29
+ case METER_TYPES__HOURS:
30
+ meterType = METER_TYPES__HOURS_TEXT;
31
+ break;
32
+ case METER_TYPES__MILES:
33
+ meterType = METER_TYPES__MILES_TEXT;
34
+ break;
35
+ default:
36
+ meterType = 'unknown';
37
+ break;
38
+ }
39
+
20
40
  return <TextNative
21
41
  {...props}
22
42
  className={className}
23
- >{props.value ? 'Time (hrs)' : 'Distance (mi/km)'}</TextNative>;
43
+ >{meterType}</TextNative>;
24
44
  };
@@ -1,2 +1,4 @@
1
1
  export const METER_TYPES__HOURS = 1;
2
2
  export const METER_TYPES__MILES = 2;
3
+ export const METER_TYPES__HOURS_TEXT = 'Time (hrs)';
4
+ export const METER_TYPES__MILES_TEXT = 'Distance (mi/km)';
@@ -0,0 +1,46 @@
1
+
2
+ import clsx from 'clsx';
3
+ import {
4
+ UI_MODE_WEB,
5
+ UI_MODE_NATIVE,
6
+ CURRENT_MODE,
7
+ } from '../Constants/UiModes.js';
8
+ import _ from 'lodash';
9
+
10
+ export default function addIconProps(iconProps = {}) {
11
+
12
+ iconProps = _.cloneDeep(iconProps); // avoid mutating the original props, as they may be submitted to multiple components
13
+
14
+ iconProps.className = clsx(
15
+ 'Icon',
16
+ iconProps.className,
17
+ );
18
+
19
+ if (CURRENT_MODE === UI_MODE_WEB) {
20
+ return iconProps;
21
+ }
22
+
23
+ // native only
24
+
25
+ // marginx
26
+ iconProps.style = {
27
+ marginHorizontal: 8,
28
+ ...iconProps.style,
29
+ };
30
+
31
+ // On native, react-native-svg ignores className and will only size the icon based on
32
+ // explicit width / height props (or size if the wrapper supports it).
33
+ // If no size set, it falls back to the full intrinsic viewBox size, so we need to ensure we set a default size.
34
+ // If you want to override the size, pass width and height props to the icon.
35
+ if (iconProps.width || iconProps.height) {
36
+ return iconProps;
37
+ }
38
+ const nativeDefaults = {
39
+ width: 24,
40
+ height: 24,
41
+ };
42
+ return {
43
+ ...nativeDefaults,
44
+ ...iconProps,
45
+ };
46
+ }