@onehat/ui 0.4.7 → 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.7",
3
+ "version": "0.4.8",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -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,6 +245,7 @@ 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 = `
@@ -272,6 +273,7 @@ function TagComponent(props) {
272
273
  let valueBoxesClassName = `
273
274
  Tag-valueBoxes-container
274
275
  w-full
276
+ min-h-[40px]
275
277
  flex-wrap
276
278
  max-h-[200px]
277
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)) {
@@ -552,9 +554,9 @@ function Form(props) {
552
554
  }
553
555
  delete itemPropsToPass.w;
554
556
  }
555
- if (!style.flex && !style.width) {
556
- style.flex = 1;
557
- }
557
+ // if (!style.flex && !style.width) {
558
+ // style.flex = 1;
559
+ // }
558
560
  itemPropsToPass.className += ' Column';
559
561
  }
560
562
  if (type === 'Row') {
@@ -565,7 +567,7 @@ function Form(props) {
565
567
  return buildFromItem(item, ix, {...defaults, ...itemDefaults});
566
568
  });
567
569
 
568
- let elementClassName = 'Form-Element';
570
+ let elementClassName = 'Form-ElementFromItem';
569
571
  const defaultsClassName = defaults.className;
570
572
  if (defaultsClassName) {
571
573
  elementClassName += ' ' + defaultsClassName;
@@ -645,12 +647,12 @@ function Form(props) {
645
647
  style.width = '50px';
646
648
  }
647
649
  if (containerWidth > styles.FORM_STACK_ROW_THRESHOLD) {
648
- element = <HStack className="Form-HStack2 w-full py-1">
650
+ element = <HStack className="Form-HStack1 w-full py-1">
649
651
  <Label style={style}>{label}</Label>
650
652
  {element}
651
653
  </HStack>;
652
654
  } else {
653
- element = <VStack className="Form-VStack1 w-full py-1 mt-3">
655
+ element = <VStack className="Form-VStack2 w-full py-1 mt-3">
654
656
  <Label style={style}>{label}</Label>
655
657
  {element}
656
658
  </VStack>;
@@ -710,6 +712,7 @@ function Form(props) {
710
712
 
711
713
  if (useSelectorId) { // This causes the whole form to use selectorId
712
714
  editorTypeProps.selectorId = selectorId;
715
+ editorTypeProps.selectorSelectedField = selectorSelectedField;
713
716
  }
714
717
  if (itemPropsToPass.selectorId || editorTypeProps.selectorId) { // editorTypeProps.selectorId causes just this one field to use selectorId
715
718
  if (_.isNil(itemPropsToPass.selectorSelected)) {
@@ -779,14 +782,14 @@ function Form(props) {
779
782
  if (item.additionalEditButtons) {
780
783
  const buttons = buildAdditionalButtons(item.additionalEditButtons, self, { fieldState, formSetValue, formGetValues, formState });
781
784
  if (containerWidth > styles.FORM_STACK_ROW_THRESHOLD) {
782
- element = <HStack className="Form-additionalEditButtons flex-1 flex-wrap">
785
+ element = <HStack className="Form-HStack5 flex-1 flex-wrap">
783
786
  {element}
784
787
  {buttons}
785
788
  </HStack>;
786
789
  } else {
787
- element = <VStack className="Form-additionalEditButtons flex-1 w-full">
790
+ element = <VStack className="Form-VStack6 flex-1 w-full">
788
791
  {element}
789
- <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">
790
793
  {buttons}
791
794
  </HStack>
792
795
  </VStack>;
@@ -820,18 +823,18 @@ function Form(props) {
820
823
  style.width = '50px';
821
824
  }
822
825
  if (containerWidth > styles.FORM_STACK_ROW_THRESHOLD) {
823
- element = <HStack className="HStack3 flex-1">
826
+ element = <HStack className="Form-HStack8 flex-1">
824
827
  <Label style={style}>{requiredIndicator}{label}</Label>
825
828
  {element}
826
829
  </HStack>;
827
830
  } else {
828
- element = <VStack className="VStack3 flex-1 mt-3">
831
+ element = <VStack className="Form-VStack9 flex-1 mt-3">
829
832
  <Label style={style}>{requiredIndicator}{label}</Label>
830
833
  {element}
831
834
  </VStack>;
832
835
  }
833
836
  } else if (disableLabels && requiredIndicator) {
834
- element = <HStack className="HStack3 flex-1">
837
+ element = <HStack className="Form-HStack10 flex-1">
835
838
  {requiredIndicator}
836
839
  {element}
837
840
  </HStack>;
@@ -851,7 +854,7 @@ function Form(props) {
851
854
  return <HStack
852
855
  key={ix}
853
856
  className={`
854
- HStack4
857
+ Form-HStack11
855
858
  flex-none
856
859
  pb-2
857
860
  h-[50px]
@@ -873,6 +876,7 @@ function Form(props) {
873
876
  title = null,
874
877
  description = null,
875
878
  selectorId,
879
+ selectorSelectedField,
876
880
  ...itemPropsToPass
877
881
  } = item;
878
882
  if (isMultiple && type !== 'Attachments') {
@@ -887,6 +891,7 @@ function Form(props) {
887
891
  element = <Element
888
892
  {...testProps('ancillary-' + type)}
889
893
  selectorId={selectorId}
894
+ selectorSelectedField={selectorSelectedField}
890
895
  selectorSelected={selectorSelected || record}
891
896
  uniqueRepository={true}
892
897
  parent={self}
@@ -898,6 +903,7 @@ function Form(props) {
898
903
  }
899
904
  title = <Text
900
905
  className={`
906
+ Form-Ancillary-Title
901
907
  font-bold
902
908
  ${styles.FORM_ANCILLARY_TITLE_FONTSIZE}
903
909
  `}
@@ -906,6 +912,7 @@ function Form(props) {
906
912
  if (description) {
907
913
  description = <Text
908
914
  className={`
915
+ Form-Ancillary-Description
909
916
  italic
910
917
  ${styles.FORM_ANCILLARY_DESCRIPTION_FONTSIZE}
911
918
  `}
@@ -914,6 +921,7 @@ function Form(props) {
914
921
  components.push(<VStack
915
922
  key={'ancillary-' + ix}
916
923
  className={`
924
+ Form-VStack12
917
925
  mx-1
918
926
  my-3
919
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,7 @@ 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,
38
39
  style,
39
40
 
40
41
  ...propsToPass
@@ -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),