@onehat/ui 0.3.1 → 0.3.3

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.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -12,13 +12,9 @@ import {
12
12
  } from '../../../../Constants/UiModes.js';
13
13
  import UiGlobals from '../../../../UiGlobals.js';
14
14
  import Input from '../Input.js';
15
- import withAlert from '../../../Hoc/withAlert.js';
16
15
  import withData from '../../../Hoc/withData.js';
17
- import withEvents from '../../../Hoc/withEvents.js';
18
- import withPresetButtons from '../../../Hoc/withPresetButtons.js';
19
16
  import withSelection from '../../../Hoc/withSelection.js';
20
17
  import withValue from '../../../Hoc/withValue.js';
21
- import withWindowedEditor from '../../../Hoc/withWindowedEditor.js';
22
18
  import emptyFn from '../../../../Functions/emptyFn.js';
23
19
  import { Grid, WindowedGridEditor } from '../../../Grid/Grid.js';
24
20
  import IconButton from '../../../Buttons/IconButton.js';
@@ -53,9 +49,6 @@ export function ComboComponent(props) {
53
49
  idIx,
54
50
  displayIx,
55
51
 
56
- // withEvents
57
- onEvent,
58
-
59
52
  // withSelection
60
53
  selection,
61
54
  setSelection,
@@ -70,11 +63,11 @@ export function ComboComponent(props) {
70
63
  inputRef = useRef(),
71
64
  triggerRef = useRef(),
72
65
  menuRef = useRef(),
73
- // isTyping = useRef(false),
74
- // typingTimeout = useRef(),
66
+ isManuallyEnteringText = useRef(false),
67
+ savedSearch = useRef(null),
68
+ typingTimeout = useRef(),
75
69
  [isMenuShown, setIsMenuShown] = useState(false),
76
70
  [isRendered, setIsRendered] = useState(false),
77
- [isManuallyEnteringText, setIsManuallyEnteringText] = useState(false), // when typing a value, not using trigger/grid
78
71
  [textValue, setTextValue] = useState(''),
79
72
  [width, setWidth] = useState(0),
80
73
  [height, setHeight] = useState(null),
@@ -126,6 +119,18 @@ export function ComboComponent(props) {
126
119
  }
127
120
  setIsMenuShown(false);
128
121
  },
122
+ getIsManuallyEnteringText = () => {
123
+ return isManuallyEnteringText.current;
124
+ },
125
+ setIsManuallyEnteringText = (bool) => {
126
+ isManuallyEnteringText.current = bool;
127
+ },
128
+ getSavedSearch = () => {
129
+ return savedSearch.current;
130
+ },
131
+ setSavedSearch = (val) => {
132
+ savedSearch.current = val;
133
+ },
129
134
  toggleMenu = () => {
130
135
  setIsMenuShown(!isMenuShown);
131
136
  },
@@ -168,22 +173,20 @@ export function ComboComponent(props) {
168
173
  return;
169
174
  }
170
175
  setTextValue(value);
171
- // searchForMatches(value);
172
- // setIsManuallyEnteringText(true);
173
-
174
- // isTyping.current = true;
175
- // if (typingTimeout.current) {
176
- // clearTimeout(typingTimeout.current);
177
- // }
178
- // typingTimeout.current = setTimeout(() => {
179
- // isTyping.current = false;
180
- // }, 300);
176
+
177
+ setIsManuallyEnteringText(true);
178
+ clearTimeout(typingTimeout.current);
179
+ typingTimeout.current = setTimeout(() => {
180
+ searchForMatches(value);
181
+ }, 300);
181
182
  },
182
183
  onInputBlur = (e) => {
183
184
  const {
184
185
  relatedTarget
185
186
  } = e;
186
187
 
188
+ setIsManuallyEnteringText(false);
189
+
187
190
  // If user focused on the trigger and text is blank, clear the selection and close the menu
188
191
  if ((triggerRef.current === relatedTarget || triggerRef.current.contains(relatedTarget)) && (_.isEmpty(textValue) || _.isNil(textValue))) {
189
192
  setSelection([]); // delete current selection
@@ -211,7 +214,7 @@ export function ComboComponent(props) {
211
214
  if (_.isEmpty(textValue) || _.isNil(textValue)) {
212
215
  setSelection([]); // delete current selection
213
216
 
214
- } else if (isManuallyEnteringText) {
217
+ } else if (getIsManuallyEnteringText()) {
215
218
  if (forceSelection) {
216
219
  setSelection([]); // delete current selection
217
220
  hideMenu();
@@ -257,43 +260,57 @@ export function ComboComponent(props) {
257
260
  hideMenu();
258
261
  }
259
262
  },
260
- searchForMatches = (value) => {
261
-
262
- // Do a search for this value
263
- // TODO: Do fuzzy seach for results
264
- // Would have to do differently for remote repositories
265
- // Narrow results in grid to those that match the filter.
266
- // If filter is cleared, show original results.
263
+ searchForMatches = async (value) => {
267
264
 
268
265
  let found;
269
266
  if (Repository) {
270
267
 
271
- debugger;
272
-
273
268
  // Set filter
274
269
  let filter = {};
275
- if (value !== '') {
270
+ if (Repository.isRemote) {
271
+ let searchField = 'q';
272
+
273
+ // Check to see if displayField is a real field
274
+ const
275
+ schema = Repository.getSchema(),
276
+ displayFieldName = schema.model.displayProperty;
277
+ displayFieldDef = schema.getPropertyDefinition(displayFieldName);
278
+ if (!displayFieldDef.isVirtual) {
279
+ searchField = displayFieldName + ' LIKE';
280
+ }
276
281
 
277
- // TODO: Want to build a search functionality that shows results in combo grid
282
+ if (!_.isEmpty(value)) {
283
+ value += '%';
284
+ }
278
285
 
279
- if (Repository.isRemote) {
280
- // 'q' fuzzy search from server
281
-
282
-
283
- } else {
284
- // Fuzzy search with getBy filter function
285
- filter = (entity) => {
286
- const
287
- displayValue = entity.displayValue,
288
- regex = new RegExp('^' + value);
289
- return displayValue.match(regex);
290
- };
286
+ await Repository.filter(searchField, value);
287
+ if (!this.isAutoLoad) {
288
+ await Repository.reload();
291
289
  }
292
- }
293
- Repository.filter(filter);
294
290
 
295
- // TODO: Auto-select if filter produces only one result
291
+ } else {
292
+ throw Error('Not sure if this works yet!');
293
+
294
+ // Fuzzy search with getBy filter function
295
+ filter = (entity) => {
296
+ const
297
+ displayValue = entity.displayValue,
298
+ regex = new RegExp('^' + value);
299
+ return displayValue.match(regex);
300
+ };
301
+ Repository.filter(filter);
302
+ }
296
303
 
304
+ setSavedSearch(value);
305
+ const numResults = Repository.entities.length;
306
+ if (!numResults) {
307
+ setSelection([]);
308
+ } else if (numResults === 1) {
309
+ const selection = Repository.entities[0];
310
+ setSelection([selection]);
311
+ setSavedSearch(null);
312
+ }
313
+
297
314
  } else {
298
315
  // Search through data
299
316
  found = _.find(data, (item) => {
@@ -333,6 +350,10 @@ export function ComboComponent(props) {
333
350
  }, [isRendered]);
334
351
 
335
352
  useEffect(() => {
353
+ if (getIsManuallyEnteringText() && getSavedSearch()) {
354
+ return
355
+ }
356
+
336
357
  // Adjust text input to match selection
337
358
  let localTextValue = getDisplayFromSelection(selection);
338
359
  if (!_.isEqual(localTextValue, textValue)) {
@@ -498,7 +519,6 @@ export function ComboComponent(props) {
498
519
  {...props}
499
520
  disablePresetButtons={!isEditor}
500
521
  disablePagination={disablePagination}
501
- fireEvent={onEvent}
502
522
  setSelection={(selection) => {
503
523
  // Decorator fn to add local functionality
504
524
  // Close the menu when row is selected on grid
@@ -11,6 +11,7 @@ import {
11
11
  import {
12
12
  FILTER_TYPE_ANCILLARY
13
13
  } from '../../Constants/Filters.js';
14
+ import Inflector from 'inflector-js';
14
15
  import inArray from '../../Functions/inArray.js';
15
16
  import getComponentFromType from '../../Functions/getComponentFromType.js';
16
17
  import IconButton from '../Buttons/IconButton.js';
@@ -65,6 +66,7 @@ export default function withFilters(WrappedComponent) {
65
66
  !_.isEmpty(defaultFilters) ? defaultFilters : // component filters override model filters
66
67
  !_.isEmpty(modelDefaultFilters) ? modelDefaultFilters : [],
67
68
  isUsingCustomFilters = startingFilters === customFilters,
69
+ modelFilterTypes = Repository.getSchema().getFilterTypes(),
68
70
  [isReady, setIsReady] = useState(false),
69
71
  [isFilterSelectorShown, setIsFilterSelectorShown] = useState(false),
70
72
  getFormattedFilter = (filter) => {
@@ -79,17 +81,13 @@ export default function withFilters(WrappedComponent) {
79
81
  title = propertyDef.title;
80
82
  type = propertyDef.filterType;
81
83
  } else {
82
- const modelFilterTypes = Repository.getSchema().getFilterTypes();
83
84
  if (!modelFilterTypes[field]) {
84
85
  throw Error('not a propertyDef, and not an ancillaryFilter!');
85
86
  }
86
-
87
87
  const ancillaryFilter = modelFilterTypes[field];
88
88
  title = ancillaryFilter.title;
89
89
  type = FILTER_TYPE_ANCILLARY;
90
90
  }
91
-
92
-
93
91
  formatted = {
94
92
  field,
95
93
  title,
@@ -245,15 +243,19 @@ export default function withFilters(WrappedComponent) {
245
243
  let {
246
244
  field,
247
245
  type: filterType,
246
+ title,
248
247
  } = filter,
248
+
249
249
  propertyDef = Repository.getSchema().getPropertyDefinition(field);
250
+
251
+ if (!title) {
252
+ title = propertyDef?.title;
253
+ }
250
254
 
251
255
  if (_.isString(filterType)) {
252
256
  if (filterType === FILTER_TYPE_ANCILLARY) {
253
- // Convert field to PluralCamelGrid
254
- debugger;
255
-
256
-
257
+ title = modelFilterTypes[field].title;
258
+ filterType = Inflector.camelize(Inflector.pluralize(field)) + 'Combo'; // Convert field to PluralCamelCombo
257
259
  }
258
260
  Element = getComponentFromType(filterType);
259
261
  if (filterType === 'Input') {
@@ -275,7 +277,7 @@ export default function withFilters(WrappedComponent) {
275
277
  elementProps.minWidth = 100;
276
278
  }
277
279
 
278
- const tooltip = filter.tooltip || filter.title || propertyDef.title;
280
+ const tooltip = filter.tooltip || title;
279
281
  let filterElement = <Element
280
282
  key={'filter-' + field}
281
283
  tooltip={tooltip}
@@ -287,7 +289,7 @@ export default function withFilters(WrappedComponent) {
287
289
  />;
288
290
  if (showLabels && field !== 'q') {
289
291
  filterElement = <Row key={'label-' + ix} alignItems="center">
290
- <Text ml={2} mr={1} fontSize={UiGlobals.styles.FILTER_LABEL_FONTSIZE}>{propertyDef.title}</Text>
292
+ <Text ml={2} mr={1} fontSize={UiGlobals.styles.FILTER_LABEL_FONTSIZE}>{title}</Text>
291
293
  {filterElement}
292
294
  </Row>;
293
295
  }
@@ -411,8 +413,7 @@ export default function withFilters(WrappedComponent) {
411
413
  usedFields = _.filter(_.map(modalFilters, (filter) => {
412
414
  return filter?.field;
413
415
  }), el => !_.isNil(el)),
414
- formStartingValues = {},
415
- modelFilterTypes = Repository.getSchema().getFilterTypes();
416
+ formStartingValues = {};
416
417
 
417
418
  _.each(modalSlots, (field, ix) => {
418
419