@onehat/ui 0.4.6 → 0.4.8

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.4.6",
3
+ "version": "0.4.8",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -325,13 +325,13 @@ function Container(props) {
325
325
  componentProps = {};
326
326
  }
327
327
 
328
- return <VStack className="w-full flex-1">
328
+ return <VStack className="Container-all w-full flex-1">
329
329
  {northComponent}
330
330
  {(!isNorthCollapsed && !localIsNorthCollapsed) && northSplitter}
331
- <HStack className="w-full" style={{ flex: 100 }}>
331
+ <HStack className="Container-mid w-full" style={{ flex: 100 }}>
332
332
  {westComponent}
333
333
  {(!isWestCollapsed && !localIsWestCollapsed) && westSplitter}
334
- <VStack className="h-full overflow-auto" style={{ flex: 100 }}>
334
+ <VStack className="Container-center h-full overflow-auto" style={{ flex: 100 }}>
335
335
  {centerComponent}
336
336
  </VStack>
337
337
  {(!isEastCollapsed && !localIsEastCollapsed) && eastSplitter}
@@ -681,6 +681,7 @@ export function ComboComponent(props) {
681
681
  'disablePrint',
682
682
  'selectorId',
683
683
  'selectorSelected',
684
+ 'selectorSelectedField',
684
685
  'usePermissions',
685
686
  ]);
686
687
  if (!Repository) {
@@ -245,10 +245,12 @@ function TagComponent(props) {
245
245
  if (propsToPass.selectorId) {
246
246
  _combo.selectorId = propsToPass.selectorId;
247
247
  _combo.selectorSelected = propsToPass.selectorSelected;
248
+ _combo.selectorSelectedField = propsToPass.selectorSelectedField;
248
249
  }
249
250
 
250
251
  let className = `
251
252
  Tag
253
+ w-full
252
254
  p-0
253
255
  `;
254
256
  if (props.className) {
@@ -271,6 +273,7 @@ function TagComponent(props) {
271
273
  let valueBoxesClassName = `
272
274
  Tag-valueBoxes-container
273
275
  w-full
276
+ min-h-[40px]
274
277
  flex-wrap
275
278
  max-h-[200px]
276
279
  overflow-auto
@@ -125,6 +125,7 @@ function Form(props) {
125
125
  // parent container
126
126
  selectorId,
127
127
  selectorSelected,
128
+ selectorSelectedField,
128
129
 
129
130
  // withAlert
130
131
  alert,
@@ -297,7 +298,7 @@ function Form(props) {
297
298
  }
298
299
  }
299
300
 
300
- let elementClassName = '';
301
+ let elementClassName = 'Form-ElementFromColumnsConfig';
301
302
  const
302
303
  boxFlex = configPropsToPass.flex,
303
304
  boxW = configPropsToPass.w;
@@ -365,6 +366,7 @@ function Form(props) {
365
366
 
366
367
  if (useSelectorId) { // This causes the whole form to use selectorId
367
368
  editorTypeProps.selectorId = selectorId;
369
+ editorTypeProps.selectorSelectedField = selectorSelectedField;
368
370
  }
369
371
  if (configPropsToPass.selectorId || editorTypeProps.selectorId) { // editorTypeProps.selectorId causes just this one field to use selectorId
370
372
  if (_.isNil(configPropsToPass.selectorSelected)) {
@@ -376,7 +378,7 @@ function Form(props) {
376
378
  dynamicProps = getDynamicProps({ fieldState, formSetValue, formGetValues, formState });
377
379
  }
378
380
 
379
- let elementClassName = 'flex-1';
381
+ let elementClassName = 'Form-Element flex-1';
380
382
  if (type.match(/Tag/)) {
381
383
  elementClassName += ' overflow-auto';
382
384
  }
@@ -433,6 +435,7 @@ function Form(props) {
433
435
  return <HStack
434
436
  key={ix}
435
437
  className={`
438
+ Form-HStack1
436
439
  flex-${flex}
437
440
  ${error ? "bg-[#fdd]" : "bg-white"}
438
441
  ${columnClassName}
@@ -551,9 +554,9 @@ function Form(props) {
551
554
  }
552
555
  delete itemPropsToPass.w;
553
556
  }
554
- if (!style.flex && !style.width) {
555
- style.flex = 1;
556
- }
557
+ // if (!style.flex && !style.width) {
558
+ // style.flex = 1;
559
+ // }
557
560
  itemPropsToPass.className += ' Column';
558
561
  }
559
562
  if (type === 'Row') {
@@ -564,7 +567,7 @@ function Form(props) {
564
567
  return buildFromItem(item, ix, {...defaults, ...itemDefaults});
565
568
  });
566
569
 
567
- let elementClassName = '';
570
+ let elementClassName = 'Form-ElementFromItem';
568
571
  const defaultsClassName = defaults.className;
569
572
  if (defaultsClassName) {
570
573
  elementClassName += ' ' + defaultsClassName;
@@ -644,18 +647,18 @@ function Form(props) {
644
647
  style.width = '50px';
645
648
  }
646
649
  if (containerWidth > styles.FORM_STACK_ROW_THRESHOLD) {
647
- element = <HStack className="HStackA py-1">
650
+ element = <HStack className="Form-HStack1 w-full py-1">
648
651
  <Label style={style}>{label}</Label>
649
652
  {element}
650
653
  </HStack>;
651
654
  } else {
652
- element = <VStack className="HStackA w-full py-1 mt-3">
655
+ element = <VStack className="Form-VStack2 w-full py-1 mt-3">
653
656
  <Label style={style}>{label}</Label>
654
657
  {element}
655
658
  </VStack>;
656
659
  }
657
660
  }
658
- return <HStack key={ix} className="HStackB px-2 pb-1">{element}</HStack>;
661
+ return <HStack key={ix} className="Form-HStack3 w-full px-2 pb-1">{element}</HStack>;
659
662
  }
660
663
 
661
664
 
@@ -709,6 +712,7 @@ function Form(props) {
709
712
 
710
713
  if (useSelectorId) { // This causes the whole form to use selectorId
711
714
  editorTypeProps.selectorId = selectorId;
715
+ editorTypeProps.selectorSelectedField = selectorSelectedField;
712
716
  }
713
717
  if (itemPropsToPass.selectorId || editorTypeProps.selectorId) { // editorTypeProps.selectorId causes just this one field to use selectorId
714
718
  if (_.isNil(itemPropsToPass.selectorSelected)) {
@@ -720,7 +724,7 @@ function Form(props) {
720
724
  dynamicProps = getDynamicProps({ fieldState, formSetValue, formGetValues, formState });
721
725
  }
722
726
 
723
- let elementClassName = 'field-' + name + ' flex-1 Form-Element';
727
+ let elementClassName = 'Form-Element field-' + name + ' flex-1';
724
728
  const defaultsClassName = defaults.className;
725
729
  if (defaultsClassName) {
726
730
  elementClassName += ' ' + defaultsClassName;
@@ -770,7 +774,7 @@ function Form(props) {
770
774
  if (message) {
771
775
  message = <Text className="text-[#f00]">{message}</Text>;
772
776
  }
773
- element = <VStack className="Form-messageContainer pt-1 flex-1">
777
+ element = <VStack className="Form-VStack4 pt-1 flex-1">
774
778
  {element}
775
779
  {message}
776
780
  </VStack>;
@@ -778,14 +782,14 @@ function Form(props) {
778
782
  if (item.additionalEditButtons) {
779
783
  const buttons = buildAdditionalButtons(item.additionalEditButtons, self, { fieldState, formSetValue, formGetValues, formState });
780
784
  if (containerWidth > styles.FORM_STACK_ROW_THRESHOLD) {
781
- element = <HStack className="Form-additionalEditButtons flex-1 flex-wrap">
785
+ element = <HStack className="Form-HStack5 flex-1 flex-wrap">
782
786
  {element}
783
787
  {buttons}
784
788
  </HStack>;
785
789
  } else {
786
- element = <VStack className="Form-additionalEditButtons flex-1 w-full">
790
+ element = <VStack className="Form-VStack6 flex-1 w-full">
787
791
  {element}
788
- <HStack className="Form-additionalEditButtons-VStack flex-1 w-full mt-1 flex-wrap">
792
+ <HStack className="Form-HStack7-VStack flex-1 w-full mt-1 flex-wrap">
789
793
  {buttons}
790
794
  </HStack>
791
795
  </VStack>;
@@ -819,18 +823,18 @@ function Form(props) {
819
823
  style.width = '50px';
820
824
  }
821
825
  if (containerWidth > styles.FORM_STACK_ROW_THRESHOLD) {
822
- element = <HStack className="HStack3 flex-1">
826
+ element = <HStack className="Form-HStack8 flex-1">
823
827
  <Label style={style}>{requiredIndicator}{label}</Label>
824
828
  {element}
825
829
  </HStack>;
826
830
  } else {
827
- element = <VStack className="VStack3 flex-1 mt-3">
831
+ element = <VStack className="Form-VStack9 flex-1 mt-3">
828
832
  <Label style={style}>{requiredIndicator}{label}</Label>
829
833
  {element}
830
834
  </VStack>;
831
835
  }
832
836
  } else if (disableLabels && requiredIndicator) {
833
- element = <HStack className="HStack3 flex-1">
837
+ element = <HStack className="Form-HStack10 flex-1">
834
838
  {requiredIndicator}
835
839
  {element}
836
840
  </HStack>;
@@ -850,7 +854,7 @@ function Form(props) {
850
854
  return <HStack
851
855
  key={ix}
852
856
  className={`
853
- HStack4
857
+ Form-HStack11
854
858
  flex-none
855
859
  pb-2
856
860
  h-[50px]
@@ -872,6 +876,7 @@ function Form(props) {
872
876
  title = null,
873
877
  description = null,
874
878
  selectorId,
879
+ selectorSelectedField,
875
880
  ...itemPropsToPass
876
881
  } = item;
877
882
  if (isMultiple && type !== 'Attachments') {
@@ -886,6 +891,7 @@ function Form(props) {
886
891
  element = <Element
887
892
  {...testProps('ancillary-' + type)}
888
893
  selectorId={selectorId}
894
+ selectorSelectedField={selectorSelectedField}
889
895
  selectorSelected={selectorSelected || record}
890
896
  uniqueRepository={true}
891
897
  parent={self}
@@ -897,6 +903,7 @@ function Form(props) {
897
903
  }
898
904
  title = <Text
899
905
  className={`
906
+ Form-Ancillary-Title
900
907
  font-bold
901
908
  ${styles.FORM_ANCILLARY_TITLE_FONTSIZE}
902
909
  `}
@@ -905,6 +912,7 @@ function Form(props) {
905
912
  if (description) {
906
913
  description = <Text
907
914
  className={`
915
+ Form-Ancillary-Description
908
916
  italic
909
917
  ${styles.FORM_ANCILLARY_DESCRIPTION_FONTSIZE}
910
918
  `}
@@ -913,6 +921,7 @@ function Form(props) {
913
921
  components.push(<VStack
914
922
  key={'ancillary-' + ix}
915
923
  className={`
924
+ Form-VStack12
916
925
  mx-1
917
926
  my-3
918
927
  `}
@@ -128,7 +128,6 @@ function GridComponent(props) {
128
128
  hideNavColumn = true,
129
129
  noneFoundText,
130
130
  autoAdjustPageSizeToHeight = true,
131
- disableSelectorSelected = false,
132
131
  showRowExpander = false,
133
132
  getExpandedRowContent,
134
133
  showHeaders = true,
@@ -154,6 +153,22 @@ function GridComponent(props) {
154
153
  alternateRowBackgrounds = true,
155
154
  alternatingInterval = 2,
156
155
  defaultRowHeight = 48,
156
+
157
+ // The selectorSelected mechanism allows us to filter results of the primary model, (e.g. WorkOrders)
158
+ // by the selection on the secondary model (e.g. Equipment). It's used on Grids, Trees, Forms, etc.
159
+ // The 'selectorId' is the name of the primary model's filter (e.g. 'WorkOrders.equipment_id').
160
+ // which gets submitted to the server as a condition (e.g. 'conditions[WorkOrders.equipment_id]').
161
+ // The 'selectorSelected' is the Entity on the secondary model which is selected (e.g. Equipment).
162
+ // The 'selectorSelectedField' is the field on the secondary model to use as the value for the filter
163
+ // (e.g. 'fleet_id'). If not given, it defaults to 'id'.
164
+ // It can be disabled altogether for a specific grid ('disableSelectorSelected'), and configured
165
+ // so that no selection means no results ('noSelectorMeansNoResults').
166
+
167
+ selectorId,
168
+ selectorSelected,
169
+ selectorSelectedField = 'id',
170
+ noSelectorMeansNoResults = false,
171
+ disableSelectorSelected = false,
157
172
 
158
173
  // withComponent
159
174
  self,
@@ -201,16 +216,11 @@ function GridComponent(props) {
201
216
  deselectAll,
202
217
  selectRangeTo,
203
218
  isInSelection,
204
- noSelectorMeansNoResults = false,
205
219
  selectNext,
206
220
  selectPrev,
207
221
  addNextToSelection,
208
222
  addPrevToSelection,
209
223
 
210
- // DataMgt
211
- selectorId,
212
- selectorSelected,
213
-
214
224
  // withInlineEditor
215
225
  inlineEditor = null,
216
226
  isInlineEditorShown = false,
@@ -231,6 +241,7 @@ function GridComponent(props) {
231
241
  expandedRowsRef = useRef({}),
232
242
  cachedDragElements = useRef(),
233
243
  dragSelectionRef = useRef([]),
244
+ previousSelectorId = useRef(),
234
245
  [isInited, setIsInited] = useState(false),
235
246
  [isReady, setIsReady] = useState(false),
236
247
  [isLoading, setIsLoading] = useState(false),
@@ -760,11 +771,23 @@ function GridComponent(props) {
760
771
  if (disableSelectorSelected || !selectorId) {
761
772
  return
762
773
  }
763
- let id = selectorSelected?.id;
764
- if (_.isEmpty(selectorSelected)) {
765
- id = noSelectorMeansNoResults ? 'NO_MATCHES' : null;
774
+
775
+ if (previousSelectorId.current && selectorId !== previousSelectorId.current) {
776
+ Repository.pauseEvents();
777
+ Repository.clearFilters(previousSelectorId.current);
778
+ Repository.resumeEvents();
779
+ }
780
+ previousSelectorId.current = selectorId;
781
+
782
+ let value = null;
783
+ if (selectorSelected) {
784
+ value = selectorSelected[selectorSelectedField];
785
+ }
786
+ if (noSelectorMeansNoResults && _.isEmpty(selectorSelected)) {
787
+ value = 'NO_MATCHES';
766
788
  }
767
- Repository.filter(selectorId, id, false); // so it doesn't clear existing filters
789
+
790
+ Repository.filter(selectorId, value, false); // false so it doesn't clear existing filters
768
791
  },
769
792
  onGridKeyDown = (e) => {
770
793
  if (isInlineEditorShown) {
@@ -1035,7 +1058,7 @@ function GridComponent(props) {
1035
1058
 
1036
1059
  applySelectorSelected();
1037
1060
 
1038
- }, [selectorSelected]);
1061
+ }, [selectorId, selectorSelected]);
1039
1062
 
1040
1063
  if (canUser && !canUser('view')) {
1041
1064
  return <Unauthorized />;
@@ -59,6 +59,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
59
59
  // parent container
60
60
  secondarySelectorId,
61
61
  secondarySelectorSelected,
62
+ secondarySelectorSelectedField = 'id',
62
63
 
63
64
  // withSecondaryData
64
65
  SecondaryRepository,
@@ -149,7 +150,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
149
150
  }
150
151
 
151
152
  if (secondarySelectorId && !_.isEmpty(secondarySelectorSelected)) {
152
- addValues[secondarySelectorId] = secondarySelectorSelected.id;
153
+ addValues[secondarySelectorId] = secondarySelectorSelected[secondarySelectorSelectedField];
153
154
  }
154
155
 
155
156
  if (getNewEntityDisplayValue()) {
@@ -35,6 +35,7 @@ export default function withSecondarySideEditor(WrappedComponent, isTree = false
35
35
  // pull these out, as we don't want them going to the Editor
36
36
  secondarySelectorId,
37
37
  secondarySelectorSelected,
38
+ secondarySelectorSelectedField,
38
39
 
39
40
  ...propsToPass
40
41
  } = props;
@@ -39,6 +39,7 @@ export default function withSecondaryWindowedEditor(WrappedComponent, isTree = f
39
39
  // pull these out, as we don't want them going to the SecondaryEditor
40
40
  secondarySelectorId,
41
41
  secondarySelectorSelected,
42
+ secondarySelectorSelectedField,
42
43
  h,
43
44
 
44
45
  ...propsToPass
@@ -58,6 +58,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
58
58
  // parent container
59
59
  selectorId,
60
60
  selectorSelected,
61
+ selectorSelectedField = 'id',
61
62
 
62
63
  // withData
63
64
  Repository,
@@ -147,7 +148,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
147
148
  }
148
149
 
149
150
  if (selectorId && !_.isEmpty(selectorSelected)) {
150
- addValues[selectorId] = selectorSelected.id;
151
+ addValues[selectorId] = selectorSelected[selectorSelectedField];
151
152
  }
152
153
 
153
154
  if (getNewEntityDisplayValue()) {
@@ -35,6 +35,7 @@ export default function withInlineEditor(WrappedComponent, skipWrappers = false)
35
35
  // pull these out, as we don't want them going to the Editor
36
36
  selectorId,
37
37
  selectorSelected,
38
+ selectorSelectedField,
38
39
  h,
39
40
 
40
41
  ...propsToPass
@@ -35,6 +35,8 @@ export default function withSideEditor(WrappedComponent, isTree = false) {
35
35
  // pull these out, as we don't want them going to the Editor
36
36
  selectorId,
37
37
  selectorSelected,
38
+ selectorSelectedField,
39
+ style,
38
40
 
39
41
  ...propsToPass
40
42
  } = props;
@@ -56,6 +56,7 @@ export default function withWindowedEditor(WrappedComponent, isTree = false) {
56
56
  // pull these out, as we don't want them going to the Editor
57
57
  selectorId,
58
58
  selectorSelected,
59
+ selectorSelectedField,
59
60
  h,
60
61
 
61
62
  ...propsToPass
@@ -114,6 +114,10 @@ function TreeComponent(props) {
114
114
  initialSelection,
115
115
  canRecordBeEdited,
116
116
  onTreeLoad,
117
+
118
+ selectorId,
119
+ selectorSelected,
120
+ selectorSelectedField = 'id',
117
121
 
118
122
  // withComponent
119
123
  self,
@@ -160,10 +164,6 @@ function TreeComponent(props) {
160
164
  isInSelection,
161
165
  noSelectorMeansNoResults = false,
162
166
 
163
- // DataMgt
164
- selectorId,
165
- selectorSelected,
166
-
167
167
  } = props,
168
168
  styles = UiGlobals.styles,
169
169
  forceUpdate = useForceUpdate(),
@@ -1275,7 +1275,7 @@ function TreeComponent(props) {
1275
1275
  return () => {};
1276
1276
  }
1277
1277
  if (!disableSelectorSelected && selectorId) {
1278
- let id = selectorSelected?.id;
1278
+ let id = selectorSelected?.[selectorSelectedField] ?? null;
1279
1279
  if (_.isEmpty(selectorSelected)) {
1280
1280
  id = noSelectorMeansNoResults ? 'NO_MATCHES' : null;
1281
1281
  }
@@ -62,6 +62,7 @@ function Viewer(props) {
62
62
  // parent container
63
63
  selectorId,
64
64
  selectorSelected,
65
+ selectorSelectedField,
65
66
 
66
67
  } = props,
67
68
  scrollViewRef = useRef(),
@@ -150,9 +151,9 @@ function Viewer(props) {
150
151
  }
151
152
  delete itemPropsToPass.w;
152
153
  }
153
- if (!style.flex && !style.width) {
154
- style.flex = 1;
155
- }
154
+ // if (!style.flex && !style.width) {
155
+ // style.flex = 1;
156
+ // }
156
157
  itemPropsToPass.className += ' Column';
157
158
  // if (containerWidth < styles.FORM_ONE_COLUMN_THRESHOLD) {
158
159
  // // everything is in one column
@@ -310,6 +311,7 @@ function Viewer(props) {
310
311
  {...testProps('ancillary-' + type)}
311
312
  selectorId={selectorId}
312
313
  selectorSelected={selectorSelected || record}
314
+ selectorSelectedField={selectorSelectedField}
313
315
  canEditorViewOnly={true}
314
316
  canCrud={false}
315
317
  uniqueRepository={true}
@@ -89,6 +89,7 @@ function AttachmentsElement(props) {
89
89
 
90
90
  // parentContainer
91
91
  selectorSelected,
92
+ selectorSelectedField = 'id',
92
93
 
93
94
  // withData
94
95
  Repository,
@@ -100,7 +101,7 @@ function AttachmentsElement(props) {
100
101
  } = props,
101
102
  styles = UiGlobals.styles,
102
103
  model = _.isArray(selectorSelected) && selectorSelected[0] ? selectorSelected[0].repository?.name : selectorSelected?.repository?.name,
103
- modelidCalc = _.isArray(selectorSelected) ? _.map(selectorSelected, (entity) => entity.id) : selectorSelected?.id,
104
+ modelidCalc = _.isArray(selectorSelected) ? _.map(selectorSelected, (entity) => entity[selectorSelectedField]) : selectorSelected?.[selectorSelectedField],
104
105
  modelid = useRef(modelidCalc),
105
106
  [isReady, setIsReady] = useState(false),
106
107
  [isUploading, setIsUploading] = useState(false),