@onehat/ui 0.4.101 → 0.4.103

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 (51) hide show
  1. package/package.json +1 -1
  2. package/src/Components/Accordion/Accordion.js +65 -6
  3. package/src/Components/Container/Container.js +10 -4
  4. package/src/Components/Form/Field/Combo/Combo.js +10 -4
  5. package/src/Components/Form/Field/Tag/Tag.js +6 -0
  6. package/src/Components/Form/Form.js +8 -3
  7. package/src/Components/Grid/Grid.js +232 -154
  8. package/src/Components/Grid/GridRow.js +7 -3
  9. package/src/Components/Hoc/withPresetButtons.js +18 -6
  10. package/src/Components/Icons/ArrowsLeftRight.js +10 -0
  11. package/src/Components/Icons/Bar.js +10 -0
  12. package/src/Components/Icons/Box.js +11 -0
  13. package/src/Components/Icons/BoxOpen.js +11 -0
  14. package/src/Components/Icons/Bucket.js +10 -0
  15. package/src/Components/Icons/Bump.js +21 -0
  16. package/src/Components/Icons/Calculator.js +12 -0
  17. package/src/Components/Icons/Dots.js +20 -0
  18. package/src/Components/Icons/Fleets.js +26 -0
  19. package/src/Components/Icons/Lock.js +11 -0
  20. package/src/Components/Icons/Microchip.js +12 -0
  21. package/src/Components/Icons/Num1.js +10 -0
  22. package/src/Components/Icons/Num2.js +10 -0
  23. package/src/Components/Icons/Num3.js +10 -0
  24. package/src/Components/Icons/Num4.js +10 -0
  25. package/src/Components/Icons/OilCan.js +11 -0
  26. package/src/Components/Icons/Operations.js +10 -0
  27. package/src/Components/Icons/OverduePms.js +10 -0
  28. package/src/Components/Icons/SackDollar.js +11 -0
  29. package/src/Components/Icons/ShortBar.js +15 -0
  30. package/src/Components/Icons/Tower.js +10 -0
  31. package/src/Components/Icons/UpcomingPms.js +10 -0
  32. package/src/Components/Layout/ScreenHeader.js +35 -3
  33. package/src/Components/Layout/SetupButton.js +31 -0
  34. package/src/Components/Layout/UserIndicator.js +35 -0
  35. package/src/Components/Pms/Editor/BumpPmsEditor.js +9 -0
  36. package/src/Components/Pms/Editor/MetersEditor.js +173 -0
  37. package/src/Components/Pms/Editor/PmEventsEditor.js +291 -0
  38. package/src/Components/Pms/Grid/UpcomingPmsGrid.js +569 -0
  39. package/src/Components/Pms/Layout/TreeSpecific/MakeTreeSelection.js +11 -0
  40. package/src/Components/Pms/Layout/TreeSpecific/TreeSpecific.js +30 -0
  41. package/src/Components/Pms/Modals/BulkAssignTechnician.js +104 -0
  42. package/src/Components/Pms/Screens/PmsManager.js +136 -0
  43. package/src/Components/Pms/Window/BumpPmsEditorWindow.js +25 -0
  44. package/src/Components/Screens/Manager.js +3 -0
  45. package/src/Components/Screens/ReportsManager.js +51 -26
  46. package/src/Components/Tree/Tree.js +15 -6
  47. package/src/Components/Viewer/PmCalcDebugViewer.js +164 -146
  48. package/src/Components/Viewer/TextWithLinks.js +9 -1
  49. package/src/Components/Viewer/Viewer.js +38 -30
  50. package/src/Functions/flatten.js +39 -0
  51. package/src/Functions/verifyCanCrudPmEvents.js +33 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onehat/ui",
3
- "version": "0.4.101",
3
+ "version": "0.4.103",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -1,14 +1,21 @@
1
1
  import { useState, useEffect, useRef, } from 'react';
2
2
  import {
3
+ Box,
3
4
  HStack,
4
- ScrollView,
5
+ Icon,
5
6
  Pressable,
7
+ ScrollView,
8
+ Text,
9
+ TextNative,
6
10
  VStack,
7
11
  VStackNative,
8
12
  } from '@project-components/Gluestack';
9
13
  import clsx from 'clsx';
14
+ import Plus from '../Icons/Plus.js';
15
+ import Minus from '../Icons/Minus.js';
10
16
  import inArray from '../../Functions/inArray.js';
11
17
  import emptyFn from '../../Functions/emptyFn.js';
18
+ import UiGlobals from '../../UiGlobals.js';
12
19
  import _ from 'lodash';
13
20
 
14
21
  // The Accordion has two modes.
@@ -25,11 +32,61 @@ import _ from 'lodash';
25
32
 
26
33
  export default function Accordion(props) {
27
34
  const {
35
+ styles = UiGlobals.styles,
28
36
  sections = [],
29
37
  activeSections = [],
30
38
  setActiveSections = emptyFn,
31
- renderHeader = emptyFn,
32
- renderContent = emptyFn,
39
+ renderHeader = (section, ix, isActive) => {
40
+ return <HStack
41
+ className={clsx(
42
+ 'Header',
43
+ 'bg-grey-300',
44
+ 'items-center',
45
+ 'justify-start',
46
+ 'py-1',
47
+ 'px-2',
48
+ 'border-b-grey-400',
49
+ 'border-b-1',
50
+ styles.PANEL_HEADER_BG,
51
+ )}
52
+ >
53
+ {/* <Text className="text-white flex-1">{section.header}</Text> */}
54
+
55
+ <TextNative
56
+ numberOfLines={1}
57
+ ellipsizeMode="head"
58
+ className={clsx(
59
+ 'Header-TextNative1',
60
+ 'flex-1',
61
+ 'font-bold',
62
+ styles.PANEL_HEADER_TEXT_CLASSNAME,
63
+ )}>{section.header}</TextNative>
64
+ <Icon
65
+ as={isActive ? Minus : Plus}
66
+ className={clsx(
67
+ 'text-black',
68
+ )}
69
+ />
70
+ </HStack>;
71
+ },
72
+ unmountInactiveContent = true,
73
+ renderContent = (section, ix, isActive, ref) => {
74
+ if (unmountInactiveContent) {
75
+ if (!isActive) {
76
+ return null;
77
+ }
78
+ return section.content;
79
+ }
80
+
81
+ // This keeps all content rendered, just hidden (zero height) if it's inActive
82
+ let className = 'w-full overflow-hidden';
83
+ if (!isActive) {
84
+ className += ' h-[0px]';
85
+ }
86
+ return <Box className={className}>
87
+ {section.content}
88
+ </Box>;
89
+ },
33
90
  onAnimationEnd = emptyFn,
34
91
  onLayout,
35
92
  onlyOne = true,
@@ -67,7 +124,9 @@ export default function Accordion(props) {
67
124
 
68
125
  // TODO: Animate height. Possible help here: https://stackoverflow.com/a/57333550 and https://stackoverflow.com/a/64797961
69
126
  if (isActive) {
70
- rowProps.flex = 1;
127
+ if (onlyOne) {
128
+ rowProps.flex = 1;
129
+ }
71
130
  } else {
72
131
  rowProps.h = 0;
73
132
  rowProps.overflow = 'hidden'; // otherwise some elements mistakenly show
@@ -94,7 +153,7 @@ export default function Accordion(props) {
94
153
  >
95
154
  {header}
96
155
  </Pressable>
97
- <HStack {...rowProps} className="bg-[#f00]">
156
+ <HStack {...rowProps}>
98
157
  {content}
99
158
  </HStack>
100
159
  </VStack>;
@@ -129,7 +188,7 @@ export default function Accordion(props) {
129
188
  keyboardShouldPersistTaps="always"
130
189
  className="Accordion-ScrollView flex-1 w-full"
131
190
  contentContainerStyle={{
132
- height: '100%',
191
+ height: onlyOne ? '100%' : undefined,
133
192
  }}
134
193
  >
135
194
  <VStackNative
@@ -125,16 +125,20 @@ function Container(props) {
125
125
  localSouthIsCollapsedRef = useRef(southInitialIsCollapsed),
126
126
  localEastIsCollapsedRef = useRef(eastInitialIsCollapsed),
127
127
  localWestIsCollapsedRef = useRef(westInitialIsCollapsed),
128
+ isSplitterDraggingRef = useRef(false),
128
129
  onLayout = async (e) => {
129
- console.log('Container onLayout', e.nativeEvent.layout.width);
130
+ if (isSplitterDraggingRef.current) {
131
+ return;
132
+ }
130
133
  if (id) {
131
134
  // save prevScreenSize if changed
132
135
  const
133
- height = parseFloat(e.nativeEvent.layout.height),
134
- width = parseFloat(e.nativeEvent.layout.width),
136
+ height = windowSize?.height ?? parseFloat(e.nativeEvent.layout.height),
137
+ width = windowSize?.width ?? parseFloat(e.nativeEvent.layout.width),
135
138
  key = id + '-prevScreenSize',
136
139
  prevScreenSize = await getSaved(key);
137
- if (!prevScreenSize || prevScreenSize.width !== width || prevScreenSize.height !== height) {
140
+ const hasChanged = !prevScreenSize || Math.abs((prevScreenSize.width ?? 0) - width) > 1 || Math.abs((prevScreenSize.height ?? 0) - height) > 1;
141
+ if (hasChanged) {
138
142
  await setSaved(key, {
139
143
  height,
140
144
  width,
@@ -300,10 +304,12 @@ function Container(props) {
300
304
  }
301
305
  },
302
306
  onSplitterDragStart = () => {
307
+ isSplitterDraggingRef.current = true;
303
308
  setIsComponentsDisabled(true);
304
309
  },
305
310
  onSplitterDragStop = (delta, which) => {
306
311
  setIsComponentsDisabled(false);
312
+ isSplitterDraggingRef.current = false;
307
313
  switch(which) {
308
314
  case 'north':
309
315
  onNorthResize(delta);
@@ -100,6 +100,8 @@ export const ComboComponent = forwardRef((props, ref) => {
100
100
  onGridSave, // to hook into when menu saves (ComboEditor only)
101
101
  onGridDelete, // to hook into when menu deletes (ComboEditor only)
102
102
  onSubmit, // when Combo is used in a Tag, call this when the user submits the Combo value (i.e. presses Enter or clicks a row)
103
+ displayProperty, // override for Repository.schema.model.displayProperty
104
+ valueProperty, // override for Repository.schema.model.idProperty
103
105
  newEntityDisplayProperty,
104
106
  testID,
105
107
 
@@ -242,7 +244,7 @@ export const ComboComponent = forwardRef((props, ref) => {
242
244
  displayValue = _.each(value, (id) => {
243
245
  const entity = Repository.getById(id);
244
246
  if (entity) {
245
- displayValue.push(entity.displayValue);
247
+ displayValue.push((displayProperty ? entity?.[displayProperty] : entity?.displayValue) || '');
246
248
  }
247
249
  });
248
250
  }
@@ -272,7 +274,7 @@ export const ComboComponent = forwardRef((props, ref) => {
272
274
  }
273
275
  }
274
276
  }
275
- displayValue = entity?.displayValue || '';
277
+ displayValue = (displayProperty ? entity?.[displayProperty] : entity?.displayValue) || '';
276
278
  }
277
279
  } else {
278
280
  const item = _.find(data, (datum) => datum[idIx] === value);
@@ -318,7 +320,11 @@ export const ComboComponent = forwardRef((props, ref) => {
318
320
 
319
321
  let id = null;
320
322
  if (gridSelection.length) {
321
- id = Repository ? gridSelection[0].id : gridSelection[0][idIx];
323
+ if (Repository) {
324
+ id = valueProperty ? gridSelection[0][valueProperty] : gridSelection[0].id;
325
+ } else {
326
+ id = gridSelection[0][idIx];
327
+ }
322
328
  }
323
329
  if (id !== value) {
324
330
  setValue(id);
@@ -863,7 +869,7 @@ export const ComboComponent = forwardRef((props, ref) => {
863
869
  return;
864
870
  }
865
871
 
866
- setValue(selection[0] ? selection[0].id : null);
872
+ setValue(selection[0] ? (valueProperty ? selection[0][valueProperty] : selection[0].id) : null);
867
873
 
868
874
  } else {
869
875
 
@@ -545,10 +545,16 @@ function TagComponent(props) {
545
545
 
546
546
  function withAdditionalProps(WrappedComponent) {
547
547
  return (props) => {
548
+ const tooltipTriggerClassName = clsx(
549
+ 'w-full',
550
+ 'flex-1',
551
+ props.tooltipTriggerClassName,
552
+ );
548
553
  return <WrappedComponent
549
554
  isValueAlwaysArray={true}
550
555
  isValueAsStringifiedJson={true}
551
556
  {...props}
557
+ tooltipTriggerClassName={tooltipTriggerClassName}
552
558
  />;
553
559
  };
554
560
  }
@@ -1213,6 +1213,10 @@ function Form(props) {
1213
1213
  style.maxHeight = maxHeight;
1214
1214
  }
1215
1215
 
1216
+ const
1217
+ // allow horizontal layouts only if there are top-level columns, and screen is wide enough
1218
+ hasTopLevelColumns = _.some(items, (item) => item?.type === 'Column'),
1219
+ shouldUseHorizontalFormLayout = !isItemsCustomLayout && hasTopLevelColumns && containerWidth >= styles.FORM_ONE_COLUMN_THRESHOLD;
1216
1220
  let modeHeader = null,
1217
1221
  formButtons = null,
1218
1222
  scrollButtons = null,
@@ -1246,8 +1250,8 @@ function Form(props) {
1246
1250
  formComponents = buildFromItems();
1247
1251
  const formAncillaryComponents = buildAncillary();
1248
1252
  editor = <>
1249
- {containerWidth >= styles.FORM_ONE_COLUMN_THRESHOLD && !isItemsCustomLayout ? <HStack className="Form-formComponents-HStack p-4 gap-4 justify-center">{formComponents}</HStack> : null}
1250
- {containerWidth < styles.FORM_ONE_COLUMN_THRESHOLD || isItemsCustomLayout ? <VStack className="Form-formComponents-VStack p-4">{formComponents}</VStack> : null}
1253
+ {shouldUseHorizontalFormLayout ? <HStack className="Form-formComponents-HStack w-full min-w-0 p-4 gap-4 justify-start items-stretch">{formComponents}</HStack> : null}
1254
+ {!shouldUseHorizontalFormLayout ? <VStack className="Form-formComponents-VStack p-4">{formComponents}</VStack> : null}
1251
1255
  {formAncillaryComponents.length ? <VStack className="Form-AncillaryComponents m-2 pt-4 px-2">{formAncillaryComponents}</VStack> : null}
1252
1256
  </>;
1253
1257
 
@@ -1532,7 +1536,8 @@ function Form(props) {
1532
1536
  onScroll={onScroll}
1533
1537
  scrollEventThrottle={16 /* ms */}
1534
1538
  contentContainerStyle={{
1535
- // height: '100%',
1539
+ width: '100%',
1540
+ minWidth: '100%',
1536
1541
  }}
1537
1542
  >
1538
1543
  {scrollToTopAnchor}