@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
|
@@ -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
|
-
|
|
74
|
-
|
|
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
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
282
|
+
if (!_.isEmpty(value)) {
|
|
283
|
+
value += '%';
|
|
284
|
+
}
|
|
278
285
|
|
|
279
|
-
|
|
280
|
-
|
|
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
|
-
|
|
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
|
-
|
|
254
|
-
|
|
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 ||
|
|
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}>{
|
|
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
|
|