@evoke-platform/ui-components 1.15.0 → 1.16.0

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 (46) hide show
  1. package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.d.ts +8 -4
  2. package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.js +242 -142
  3. package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.test.js +189 -67
  4. package/dist/published/components/custom/CriteriaBuilder/PropertyTree.d.ts +6 -6
  5. package/dist/published/components/custom/CriteriaBuilder/PropertyTree.js +12 -25
  6. package/dist/published/components/custom/CriteriaBuilder/PropertyTreeItem.d.ts +4 -5
  7. package/dist/published/components/custom/CriteriaBuilder/PropertyTreeItem.js +34 -22
  8. package/dist/published/components/custom/CriteriaBuilder/types.d.ts +2 -11
  9. package/dist/published/components/custom/CriteriaBuilder/utils.d.ts +6 -34
  10. package/dist/published/components/custom/CriteriaBuilder/utils.js +18 -89
  11. package/dist/published/components/custom/Form/FormComponents/DocumentComponent/Document.js +1 -1
  12. package/dist/published/components/custom/Form/FormComponents/DocumentComponent/DocumentList.js +6 -3
  13. package/dist/published/components/custom/Form/utils.d.ts +1 -0
  14. package/dist/published/components/custom/FormV2/FormRenderer.d.ts +2 -1
  15. package/dist/published/components/custom/FormV2/FormRenderer.js +2 -8
  16. package/dist/published/components/custom/FormV2/FormRendererContainer.d.ts +4 -0
  17. package/dist/published/components/custom/FormV2/FormRendererContainer.js +229 -126
  18. package/dist/published/components/custom/FormV2/components/DefaultValues.d.ts +1 -1
  19. package/dist/published/components/custom/FormV2/components/DefaultValues.js +60 -89
  20. package/dist/published/components/custom/FormV2/components/FormContext.d.ts +1 -1
  21. package/dist/published/components/custom/FormV2/components/FormContext.js +0 -1
  22. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/ActionDialog.d.ts +1 -0
  23. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/RepeatableField.js +43 -16
  24. package/dist/published/components/custom/FormV2/components/FormFieldTypes/Criteria.d.ts +2 -2
  25. package/dist/published/components/custom/FormV2/components/FormFieldTypes/Criteria.js +3 -2
  26. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.d.ts +3 -2
  27. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.js +44 -11
  28. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.d.ts +4 -3
  29. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.js +41 -29
  30. package/dist/published/components/custom/FormV2/components/FormFieldTypes/FileContent.d.ts +12 -0
  31. package/dist/published/components/custom/FormV2/components/FormFieldTypes/FileContent.js +197 -0
  32. package/dist/published/components/custom/FormV2/components/FormFieldTypes/Image.js +2 -4
  33. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/InstanceLookup.js +14 -0
  34. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.js +6 -2
  35. package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.js +16 -13
  36. package/dist/published/components/custom/FormV2/components/types.d.ts +6 -1
  37. package/dist/published/components/custom/FormV2/components/utils.d.ts +10 -8
  38. package/dist/published/components/custom/FormV2/components/utils.js +168 -82
  39. package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.js +6 -1
  40. package/dist/published/components/custom/index.d.ts +1 -0
  41. package/dist/published/index.d.ts +1 -1
  42. package/dist/published/stories/CriteriaBuilder.stories.js +70 -22
  43. package/dist/published/stories/FormRenderer.stories.d.ts +6 -3
  44. package/dist/published/stories/FormRendererContainer.stories.d.ts +20 -0
  45. package/dist/published/theme/hooks.d.ts +3 -3
  46. package/package.json +3 -1
@@ -1,10 +1,14 @@
1
+ import { Property } from '@evoke-platform/context';
1
2
  import React from 'react';
2
3
  import 'react-querybuilder/dist/query-builder.css';
3
- import { EvokeObject } from '../../../types';
4
- import { ObjectProperty, Operator, PresetValue, TreeViewObject } from './types';
4
+ import { Operator, PresetValue, TreeViewObject } from './types';
5
5
  import { ValueEditorProps } from './ValueEditor';
6
6
  export type CriteriaInputProps = {
7
- properties: ObjectProperty[];
7
+ /**
8
+ * The ability to input free text for property selection.
9
+ */
10
+ propertyFreeSolo?: boolean;
11
+ properties: Property[];
8
12
  setCriteria: (criteria?: Record<string, unknown> | undefined) => void;
9
13
  criteria?: Record<string, unknown>;
10
14
  originalCriteria?: Record<string, unknown>;
@@ -23,7 +27,7 @@ export type CriteriaInputProps = {
23
27
  hideBorder?: boolean;
24
28
  presetGroupLabel?: string;
25
29
  treeViewOpts?: {
26
- fetchObject?: (objectId: string) => Promise<EvokeObject | undefined>;
30
+ fetchObject?: (objectId: string) => Promise<TreeViewObject | undefined>;
27
31
  object: TreeViewObject;
28
32
  };
29
33
  /**
@@ -11,7 +11,7 @@ import { Box } from '../../layout';
11
11
  import { OverflowTextField } from '../OverflowTextField';
12
12
  import { difference, parseMongoDB } from '../util';
13
13
  import PropertyTree from './PropertyTree';
14
- import { ALL_OPERATORS, traversePropertyPath } from './utils';
14
+ import { ALL_OPERATORS } from './utils';
15
15
  import ValueEditor from './ValueEditor';
16
16
  const styles = {
17
17
  buttons: {
@@ -88,99 +88,114 @@ const customButton = (props) => {
88
88
  },
89
89
  } }, buttonLabel))));
90
90
  };
91
- const customSelector = (props) => {
92
- const { options, value, handleOnChange, title, context, level } = props;
91
+ const customOperatorSelector = (props) => {
92
+ const { options, value, handleOnChange, context, level } = props;
93
93
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
94
94
  const rule = props.rule;
95
- let width = '90px';
96
95
  let val = value;
97
96
  let opts = options;
97
+ const placeholder = 'Select Operator';
98
+ const width = '25%';
98
99
  let inputType = props.fieldData?.inputType;
99
100
  // for tree view / related object properties, the properties are stored in the propertyTreeMap upon selection
100
101
  if (!!context.treeViewOpts && !!context.propertyTreeMap?.[rule.field]) {
101
102
  inputType = context.propertyTreeMap[rule.field].type;
102
103
  }
103
- const isTreeViewEnabled = context.treeViewOpts && title === 'Fields';
104
- const fetchObject = context.treeViewOpts?.fetchObject;
105
- const object = context.treeViewOpts?.object;
106
104
  let readOnly = context.disabled;
107
105
  if (!readOnly && context.disabledCriteria) {
108
106
  readOnly =
109
107
  Object.entries(context.disabledCriteria.criteria).some(([key, value]) => key === rule.field && value === rule.value && rule.operator === '=') && level === context.disabledCriteria.level;
110
- const keys = Object.keys(context.disabledCriteria.criteria);
111
- if (title === 'Fields') {
112
- opts = opts.filter((o) => readOnly || !keys.includes(o.name));
113
- }
114
108
  }
115
- let placeholder = '';
116
- switch (title) {
117
- case 'Operators':
118
- width = '25%';
119
- placeholder = 'Select Operator';
120
- if (inputType === 'array') {
121
- opts = options
122
- .filter((option) => ['null', 'notNull', 'in'].includes(option.name))
123
- .map((option) => ({ name: option.name, label: option.label }));
124
- val = val === '=' ? '' : options.find((option) => option.name === val).name;
125
- }
126
- else if (inputType === 'document' || inputType === 'criteria') {
127
- opts = options
128
- .filter((option) => ['null', 'notNull'].includes(option.name))
129
- .map((option) => ({ name: option.name, label: option.label }));
130
- val = val === '=' ? '' : options.find((option) => option.name === val).name;
131
- }
132
- else if (inputType === 'date' || inputType === 'date-time' || inputType === 'time') {
133
- opts = options
134
- .filter((option) => ['=', '!=', '<', '>', '<=', '>=', 'null', 'notNull'].includes(option.name))
135
- .map((option) => ({
136
- ...option,
137
- label: option.name === '>'
109
+ if (inputType === 'array') {
110
+ opts = options
111
+ .filter((option) => ['null', 'notNull', 'in'].includes(option.name))
112
+ .map((option) => ({ name: option.name, label: option.label }));
113
+ val = val === '=' ? '' : options.find((option) => option.name === val).name;
114
+ }
115
+ else if (inputType === 'document' || inputType === 'criteria') {
116
+ opts = options
117
+ .filter((option) => ['null', 'notNull'].includes(option.name))
118
+ .map((option) => ({ name: option.name, label: option.label }));
119
+ val = val === '=' ? '' : options.find((option) => option.name === val).name;
120
+ }
121
+ else if (inputType === 'date' || inputType === 'date-time' || inputType === 'time') {
122
+ opts = options
123
+ .filter((option) => ['=', '!=', '<', '>', '<=', '>=', 'null', 'notNull'].includes(option.name))
124
+ .map((option) => ({
125
+ ...option,
126
+ label: option.name === '>'
127
+ ? inputType === 'time'
128
+ ? 'Later than'
129
+ : 'After'
130
+ : option.name === '>='
131
+ ? inputType === 'time'
132
+ ? 'Later than or at'
133
+ : 'After or on'
134
+ : option.name === '<'
138
135
  ? inputType === 'time'
139
- ? 'Later than'
140
- : 'After'
141
- : option.name === '>='
136
+ ? 'Earlier than'
137
+ : 'Before'
138
+ : option.name === '<='
142
139
  ? inputType === 'time'
143
- ? 'Later than or at'
144
- : 'After or on'
145
- : option.name === '<'
146
- ? inputType === 'time'
147
- ? 'Earlier than'
148
- : 'Before'
149
- : option.name === '<='
150
- ? inputType === 'time'
151
- ? 'Earlier than or at'
152
- : 'Before or on'
153
- : option.label,
154
- }));
155
- }
156
- else if (inputType === 'integer' || inputType === 'number') {
157
- opts = options.filter((option) => ['=', '!=', '<', '<=', '>', '>=', 'null', 'notNull', 'in', 'notIn'].includes(option.name));
158
- // checks if it is a single-select property
159
- }
160
- else if (inputType === 'string' && isArray(props.fieldData?.values)) {
161
- opts = options.filter((option) => ['in', 'notIn', '=', '!=', 'notNull', 'null'].includes(option.name));
140
+ ? 'Earlier than or at'
141
+ : 'Before or on'
142
+ : option.label,
143
+ }));
144
+ }
145
+ else if (inputType === 'integer' || inputType === 'number') {
146
+ opts = options.filter((option) => ['=', '!=', '<', '<=', '>', '>=', 'null', 'notNull', 'in', 'notIn'].includes(option.name));
147
+ // checks if it is a single-select property
148
+ }
149
+ else if (inputType === 'string' && isArray(props.fieldData?.values)) {
150
+ opts = options.filter((option) => ['in', 'notIn', '=', '!=', 'notNull', 'null'].includes(option.name));
151
+ }
152
+ else if (inputType === 'string') {
153
+ opts = options.filter((option) => !['>', '<', '<=', '>='].includes(option.name));
154
+ }
155
+ else if (inputType === 'image' || inputType === 'boolean') {
156
+ opts = options.filter((option) => ['=', '!=', 'null', 'notNull'].includes(option.name));
157
+ }
158
+ return (React.createElement(Autocomplete, { options: opts, value: val ?? null, getOptionLabel: (option) => {
159
+ if (typeof option === 'string') {
160
+ return opts.find((o) => option === o.name)?.label || option;
162
161
  }
163
- else if (inputType === 'string') {
164
- opts = options.filter((option) => !['>', '<', '<=', '>='].includes(option.name));
162
+ return option.label;
163
+ }, getOptionKey: (option) => option.name, isOptionEqualToValue: (option, value) => {
164
+ if (typeof option === 'string') {
165
+ return option === value;
165
166
  }
166
- else if (inputType === 'image' || inputType === 'boolean') {
167
- opts = options.filter((option) => ['=', '!=', 'null', 'notNull'].includes(option.name));
167
+ else {
168
+ return option.name === value;
168
169
  }
169
- break;
170
- case 'Fields':
171
- placeholder = 'Select Property';
172
- width = '37%';
173
- val = options.find((option) => option.name === val)?.name;
174
- break;
170
+ },
171
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
172
+ onChange: (event, newValue) => {
173
+ handleOnChange(typeof newValue === 'string' ? newValue : newValue?.value.name);
174
+ }, renderInput: (params) => (React.createElement(OverflowTextField, { value: opts.find((o) => value === o.name)?.label || val || '', ...params, placeholder: placeholder, size: "small", inputProps: {
175
+ ...params.inputProps,
176
+ 'aria-label': placeholder,
177
+ } })), sx: { width: width, background: readOnly ? '#f4f6f8' : '#fff' }, disableClearable: true, readOnly: readOnly }));
178
+ };
179
+ const customFieldSelector = (props) => {
180
+ const { options, value, handleOnChange, context, level } = props;
181
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
182
+ const rule = props.rule;
183
+ const val = options.find((option) => option.name === value)?.name || value;
184
+ let opts = options;
185
+ const placeholder = 'Select Property';
186
+ const width = '37%';
187
+ const isTreeViewEnabled = context.treeViewOpts;
188
+ const fetchObject = context.treeViewOpts?.fetchObject;
189
+ const object = context.treeViewOpts?.object;
190
+ const setObject = context.treeViewOpts?.setObject;
191
+ let readOnly = context.disabled;
192
+ if (!readOnly && context.disabledCriteria) {
193
+ readOnly =
194
+ Object.entries(context.disabledCriteria.criteria).some(([key, value]) => key === rule.field && value === rule.value && rule.operator === '=') && level === context.disabledCriteria.level;
195
+ const keys = Object.keys(context.disabledCriteria.criteria);
196
+ opts = opts.filter((o) => readOnly || !keys.includes(o.name));
175
197
  }
176
- const handleTreePropertySelect = async (propertyId) => {
177
- const propertyInfo = await traversePropertyPath(propertyId, object, fetchObject);
178
- context.setPropertyTreeMap((prev) => {
179
- return { ...prev, [propertyId]: propertyInfo };
180
- });
181
- handleOnChange(propertyId);
182
- };
183
- return (React.createElement(React.Fragment, null, isTreeViewEnabled ? (React.createElement(PropertyTree, { value: val ?? value, rootObject: object, fetchObject: fetchObject, propertyTreeMap: context.propertyTreeMap ?? {}, handleTreePropertySelect: handleTreePropertySelect })) : (React.createElement(Autocomplete, { options: opts, value: val ?? null, getOptionLabel: (option) => {
198
+ return (React.createElement(React.Fragment, null, isTreeViewEnabled ? (React.createElement(PropertyTree, { value: val ?? value, rootObject: object, setRootObject: setObject, fetchObject: fetchObject, propertyTreeMap: context.propertyTreeMap ?? {}, handleTreePropertySelect: handleOnChange })) : (React.createElement(Autocomplete, { freeSolo: context.propertyFreeSolo, options: opts, value: val || null, getOptionLabel: (option) => {
184
199
  if (typeof option === 'string') {
185
200
  return opts.find((o) => option === o.name)?.label || option;
186
201
  }
@@ -195,8 +210,10 @@ const customSelector = (props) => {
195
210
  },
196
211
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
197
212
  onChange: (event, newValue) => {
198
- handleOnChange(newValue?.value.name);
199
- }, renderInput: (params) => (React.createElement(OverflowTextField, { value: opts.find((o) => value === o.name)?.label || '', ...params, placeholder: placeholder, size: "small", inputProps: {
213
+ handleOnChange(typeof newValue === 'string' ? newValue : newValue?.value.name);
214
+ }, onInputChange: context.propertyFreeSolo
215
+ ? (event, newInputValue) => handleOnChange(newInputValue)
216
+ : undefined, renderInput: (params) => (React.createElement(OverflowTextField, { value: val || '', ...params, placeholder: placeholder, size: "small", inputProps: {
200
217
  ...params.inputProps,
201
218
  'aria-label': placeholder,
202
219
  } })), sx: { width: width, background: readOnly ? '#f4f6f8' : '#fff' }, disableClearable: true, readOnly: readOnly }))));
@@ -275,77 +292,159 @@ const getAllRuleIds = (rules) => {
275
292
  });
276
293
  return ids;
277
294
  };
278
- const CriteriaBuilder = (props) => {
279
- const { properties, criteria, setCriteria, originalCriteria, enablePresetValues, presetValues, operators, disabled, disabledCriteria, hideBorder, presetGroupLabel, customValueEditor, treeViewOpts, disableRegexEscapeChars, } = props;
280
- const [propertyTreeMap, setPropertyTreeMap] = useState();
281
- const processRules = (rules) => {
282
- return rules.map((rule) => {
283
- if ('rules' in rule) {
284
- return {
285
- ...rule,
286
- rules: processRules(rule.rules),
287
- };
288
- }
289
- else {
290
- const propertyType = properties.find((property) => property.id === rule.field)?.type;
291
- let adjustedValue = rule.value;
292
- if ((rule.operator === 'null' || rule.operator === 'notNull') && rule.value) {
293
- adjustedValue = null;
294
- }
295
- return {
296
- ...rule,
297
- operator: propertyType === 'array' && rule.operator === '=' ? 'in' : rule.operator,
298
- value: adjustedValue,
299
- };
295
+ const processRules = (rules, properties) => {
296
+ return rules.map((rule) => {
297
+ if ('rules' in rule) {
298
+ return {
299
+ ...rule,
300
+ rules: processRules(rule.rules, properties),
301
+ };
302
+ }
303
+ else {
304
+ const propertyType = properties.find((property) => property.id === rule.field)?.type;
305
+ let adjustedValue = rule.value;
306
+ if ((rule.operator === 'null' || rule.operator === 'notNull') && rule.value) {
307
+ adjustedValue = null;
300
308
  }
301
- });
309
+ return {
310
+ ...rule,
311
+ operator: propertyType === 'array' && rule.operator === '=' ? 'in' : rule.operator,
312
+ value: adjustedValue,
313
+ };
314
+ }
315
+ });
316
+ };
317
+ const insertChangesOnly = (prev, newObject) => {
318
+ const mergedProperties = newObject.properties.map((newProp) => {
319
+ const prevProp = prev.properties.find((prop) => prop.id === newProp.id);
320
+ if (prevProp) {
321
+ return {
322
+ ...newProp,
323
+ children: newProp.children &&
324
+ newProp.children[0].type === 'loading' &&
325
+ prevProp.children
326
+ ? prevProp.children
327
+ : newProp.children && prevProp.children
328
+ ? insertChangesOnly({ id: prevProp.id, name: prevProp.name, properties: prevProp.children }, { id: newProp.id, name: newProp.name, properties: newProp.children }).properties
329
+ : newProp.children,
330
+ };
331
+ }
332
+ else {
333
+ return newProp;
334
+ }
335
+ });
336
+ return {
337
+ ...newObject,
338
+ properties: mergedProperties,
302
339
  };
340
+ };
341
+ const CriteriaBuilder = (props) => {
342
+ const { propertyFreeSolo, properties, criteria, setCriteria, originalCriteria, enablePresetValues, presetValues, operators, disabled, disabledCriteria, hideBorder, presetGroupLabel, customValueEditor, treeViewOpts, disableRegexEscapeChars, } = props;
343
+ const [propertyTreeMap, setPropertyTreeMap] = useState({});
344
+ const [treeViewObject, setTreeViewObject] = useState();
303
345
  useEffect(() => {
304
- if ((criteria || originalCriteria) &&
305
- !isEmpty(treeViewOpts) &&
306
- treeViewOpts.object &&
307
- treeViewOpts.fetchObject) {
308
- const { object, fetchObject } = treeViewOpts;
309
- // this retrieves the properties from a treeview for each property in the query
310
- // they are then used in the custom query builder components to determine the input type etc
311
- const updatePropertyTreeMap = async () => {
312
- const newQuery = parseMongoDB(criteria || originalCriteria || {});
346
+ let obj;
347
+ if (treeViewOpts?.object) {
348
+ obj = {
349
+ ...treeViewOpts.object,
350
+ properties: treeViewOpts.object.properties
351
+ .filter((prop) => prop.type !== 'collection')
352
+ .map((prop) => ({
353
+ ...prop,
354
+ children: prop.children
355
+ ?.filter((child) => child.type !== 'collection')
356
+ .map((child) => ({
357
+ ...child,
358
+ id: `${prop.id}.${child.id}`,
359
+ })),
360
+ })),
361
+ };
362
+ }
363
+ if ((criteria || originalCriteria) && obj && treeViewOpts?.fetchObject) {
364
+ const updateTreeViewObject = async (criteria, treeViewObject, fetchObject) => {
365
+ const newQuery = parseMongoDB(criteria);
313
366
  const ids = getAllRuleIds(newQuery.rules);
314
- let newPropertyTreeMap = {};
315
- const newPropertyTreeMapPromises = [];
316
- for (const id of ids) {
317
- if (!propertyTreeMap?.[id]) {
318
- newPropertyTreeMapPromises.push(traversePropertyPath(id, object, fetchObject)
319
- .then((property) => {
320
- if (property) {
321
- return {
322
- [id]: property,
323
- };
367
+ const newTreeViewObject = { ...treeViewObject };
368
+ const traversePath = async (path, properties, fetchObject) => {
369
+ const prop = properties?.find((prop) => path.startsWith(`${prop.id}.`) || path === prop.id);
370
+ if (prop?.type === 'object' && prop.objectId && prop.children) {
371
+ if (prop.children.length === 1 &&
372
+ prop.children[0]?.type === 'loading') {
373
+ try {
374
+ const fetchedObject = await fetchObject(prop.objectId);
375
+ prop.children = fetchedObject?.properties
376
+ ?.filter((item) => item.type !== 'collection')
377
+ .map((item) => {
378
+ return {
379
+ ...item,
380
+ id: `${prop.id}.${item.id}`,
381
+ children: item.children?.map((child) => ({
382
+ ...child,
383
+ id: `${prop.id}.${item.id}.${child.id}`,
384
+ })),
385
+ };
386
+ });
387
+ fetchedObject && (await traversePath(path, fetchedObject.properties, fetchObject));
324
388
  }
325
- return {};
326
- })
327
- .catch((err) => {
328
- console.error(err);
329
- return {};
330
- }));
389
+ catch (error) {
390
+ prop.children = [
391
+ {
392
+ id: `${prop.id}-failed`,
393
+ name: 'Loading Failed',
394
+ type: 'loadingFailed',
395
+ },
396
+ ];
397
+ console.error('Error fetching object for criteria builder:', error);
398
+ }
399
+ }
400
+ else {
401
+ await traversePath(path, prop.children, fetchObject);
402
+ }
403
+ }
404
+ };
405
+ for (const id of ids) {
406
+ await traversePath(id, newTreeViewObject.properties, fetchObject);
407
+ }
408
+ setTreeViewObject((prevTreeViewObject) => prevTreeViewObject ? insertChangesOnly(prevTreeViewObject, newTreeViewObject) : newTreeViewObject);
409
+ };
410
+ updateTreeViewObject(criteria || originalCriteria || {}, obj, treeViewOpts.fetchObject);
411
+ }
412
+ }, [treeViewOpts, criteria, originalCriteria]);
413
+ useEffect(() => {
414
+ if (!treeViewObject)
415
+ return;
416
+ const getNamePath = (path, object) => {
417
+ const helper = (path, object) => {
418
+ let namePath = '';
419
+ const prop = (object.properties ?? []).find((prop) => path.startsWith(`${prop.id}.`) || path === prop.id);
420
+ if (prop) {
421
+ if (prop.children) {
422
+ const result = helper(path, { id: prop.id, name: prop.name, properties: prop.children });
423
+ namePath = result ? prop.name + ' / ' + result : '';
424
+ }
425
+ else {
426
+ namePath = prop.name;
331
427
  }
332
428
  }
333
- newPropertyTreeMap = (await Promise.all(newPropertyTreeMapPromises)).reduce((acc, currentProperty) => ({ ...acc, ...currentProperty }), {});
334
- setPropertyTreeMap((prevPropertyTreeMap) => ({
335
- ...prevPropertyTreeMap,
336
- ...newPropertyTreeMap,
337
- }));
429
+ return namePath;
338
430
  };
339
- updatePropertyTreeMap().catch((err) => console.error(err));
431
+ return helper(path, object) || path;
432
+ };
433
+ const newQuery = parseMongoDB(criteria || originalCriteria || {});
434
+ const ids = getAllRuleIds(newQuery.rules);
435
+ const result = {};
436
+ for (const id of ids) {
437
+ result[id] = getNamePath(id, treeViewObject);
340
438
  }
341
- }, [criteria, originalCriteria, treeViewOpts]);
439
+ setPropertyTreeMap(result);
440
+ }, [criteria, originalCriteria, treeViewObject]);
342
441
  const initializeQuery = () => {
343
442
  const criteriaToParse = criteria || originalCriteria;
344
443
  const updatedQuery = criteriaToParse ? parseMongoDB(criteriaToParse || {}) : undefined;
345
444
  return updatedQuery
346
445
  ? {
347
446
  ...updatedQuery,
348
- rules: processRules(updatedQuery.rules),
447
+ rules: processRules(updatedQuery.rules, properties),
349
448
  }
350
449
  : { combinator: 'and', rules: [] };
351
450
  };
@@ -371,7 +470,7 @@ const CriteriaBuilder = (props) => {
371
470
  const handleQueryChange = (q) => {
372
471
  const processedQuery = {
373
472
  ...q,
374
- rules: processRules(q.rules),
473
+ rules: processRules(q.rules, properties),
375
474
  };
376
475
  setQuery(processedQuery);
377
476
  const newCriteria = JSON.parse(formatQuery(processedQuery, {
@@ -390,7 +489,10 @@ const CriteriaBuilder = (props) => {
390
489
  return defaultRuleProcessorMongoDB(newRule, options);
391
490
  },
392
491
  }));
393
- if (!isEmpty(difference(newCriteria, { $and: [{ $expr: true }] }))) {
492
+ if (isEmpty(difference(newCriteria, criteria))) {
493
+ return;
494
+ }
495
+ else if (!isEmpty(difference(newCriteria, { $and: [{ $expr: true }] }))) {
394
496
  setCriteria(newCriteria);
395
497
  }
396
498
  else {
@@ -510,8 +612,8 @@ const CriteriaBuilder = (props) => {
510
612
  },
511
613
  }, showCombinatorsBetweenRules: true, listsAsArrays: true, disabled: disabled, addRuleToNewGroups: true, controlElements: {
512
614
  combinatorSelector: customCombinator,
513
- fieldSelector: customSelector,
514
- operatorSelector: customSelector,
615
+ fieldSelector: customFieldSelector,
616
+ operatorSelector: customOperatorSelector,
515
617
  addGroupAction: customButton,
516
618
  addRuleAction: customButton,
517
619
  ruleGroup: CustomRuleGroup,
@@ -525,17 +627,15 @@ const CriteriaBuilder = (props) => {
525
627
  presetGroupLabel,
526
628
  disabled,
527
629
  disabledCriteria,
528
- treeViewOpts: treeViewOpts
630
+ treeViewOpts: treeViewObject && treeViewOpts
529
631
  ? {
530
632
  ...treeViewOpts,
531
- object: {
532
- ...treeViewOpts?.object,
533
- properties: treeViewOpts?.object.properties.filter(({ type }) => type !== 'collection'),
534
- },
633
+ object: treeViewObject,
634
+ setObject: setTreeViewObject,
535
635
  }
536
636
  : undefined,
537
637
  propertyTreeMap,
538
- setPropertyTreeMap,
638
+ propertyFreeSolo,
539
639
  }, controlClassnames: {
540
640
  ruleGroup: 'container',
541
641
  }, operators: operators