@evoke-platform/ui-components 1.10.0 → 1.10.1-dev.1
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/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.js +15 -15
- package/dist/published/components/custom/CriteriaBuilder/PropertyTree.d.ts +4 -2
- package/dist/published/components/custom/CriteriaBuilder/PropertyTree.js +71 -148
- package/dist/published/components/custom/CriteriaBuilder/PropertyTreeItem.d.ts +11 -7
- package/dist/published/components/custom/CriteriaBuilder/PropertyTreeItem.js +43 -41
- package/dist/published/components/custom/CriteriaBuilder/types.d.ts +8 -1
- package/dist/published/components/custom/CriteriaBuilder/utils.d.ts +7 -13
- package/dist/published/components/custom/CriteriaBuilder/utils.js +13 -23
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/RepeatableField.js +3 -0
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/ObjectPropertyInput.js +12 -5
- package/dist/published/stories/CriteriaBuilder.stories.js +30 -0
- package/package.json +1 -1
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { AddRounded, UnfoldMore } from '@mui/icons-material';
|
|
2
|
-
import { Typography } from '@mui/material';
|
|
3
2
|
import { QueryBuilderMaterial } from '@react-querybuilder/material';
|
|
4
3
|
import { isArray, isEmpty, startCase } from 'lodash';
|
|
5
4
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
@@ -7,7 +6,7 @@ import { QueryBuilder, RuleGroupBodyComponents, RuleGroupHeaderComponents, TestI
|
|
|
7
6
|
import 'react-querybuilder/dist/query-builder.css';
|
|
8
7
|
import escape from 'string-escape-regex';
|
|
9
8
|
import { TrashCan } from '../../../icons/custom';
|
|
10
|
-
import { Autocomplete, Button, IconButton } from '../../core';
|
|
9
|
+
import { Autocomplete, Button, IconButton, Typography } from '../../core';
|
|
11
10
|
import { Box } from '../../layout';
|
|
12
11
|
import { OverflowTextField } from '../OverflowTextField';
|
|
13
12
|
import { difference } from '../util';
|
|
@@ -181,7 +180,7 @@ const customSelector = (props) => {
|
|
|
181
180
|
});
|
|
182
181
|
handleOnChange(propertyId);
|
|
183
182
|
};
|
|
184
|
-
return (React.createElement(React.Fragment, null, isTreeViewEnabled ? (React.createElement(PropertyTree, { value: val ?? value, rootObject: object, fetchObject: fetchObject, handleTreePropertySelect: handleTreePropertySelect })) : (React.createElement(Autocomplete, { options: opts, value: val ?? null, getOptionLabel: (option) => {
|
|
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) => {
|
|
185
184
|
if (typeof option === 'string') {
|
|
186
185
|
return opts.find((o) => option === o.name)?.label || option;
|
|
187
186
|
}
|
|
@@ -264,6 +263,18 @@ export const valueEditor = (props) => {
|
|
|
264
263
|
}
|
|
265
264
|
return ValueEditor(props);
|
|
266
265
|
};
|
|
266
|
+
const getAllRuleIds = (rules) => {
|
|
267
|
+
const ids = [];
|
|
268
|
+
rules.forEach((rule) => {
|
|
269
|
+
if ('rules' in rule) {
|
|
270
|
+
ids.push(...getAllRuleIds(rule.rules));
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
ids.push(rule.field);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
return ids;
|
|
277
|
+
};
|
|
267
278
|
const CriteriaBuilder = (props) => {
|
|
268
279
|
const { properties, criteria, setCriteria, originalCriteria, enablePresetValues, presetValues, operators, disabled, disabledCriteria, hideBorder, presetGroupLabel, customValueEditor, treeViewOpts, disableRegexEscapeChars, } = props;
|
|
269
280
|
const [propertyTreeMap, setPropertyTreeMap] = useState();
|
|
@@ -299,18 +310,7 @@ const CriteriaBuilder = (props) => {
|
|
|
299
310
|
// they are then used in the custom query builder components to determine the input type etc
|
|
300
311
|
const updatePropertyTreeMap = async () => {
|
|
301
312
|
const newQuery = parseMongoDB(criteria || originalCriteria || {});
|
|
302
|
-
const ids =
|
|
303
|
-
const traverseRulesForIds = (rules) => {
|
|
304
|
-
rules.forEach((rule) => {
|
|
305
|
-
if ('rules' in rule) {
|
|
306
|
-
traverseRulesForIds(rule.rules);
|
|
307
|
-
}
|
|
308
|
-
else {
|
|
309
|
-
ids.push(rule.field);
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
};
|
|
313
|
-
traverseRulesForIds(newQuery.rules);
|
|
313
|
+
const ids = getAllRuleIds(newQuery.rules);
|
|
314
314
|
let newPropertyTreeMap = {};
|
|
315
315
|
const newPropertyTreeMapPromises = [];
|
|
316
316
|
for (const id of ids) {
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { Property } from '@evoke-platform/context';
|
|
1
2
|
import React from 'react';
|
|
2
3
|
import { EvokeObject } from '../../../types';
|
|
3
4
|
type PropertyTreeProps = {
|
|
4
5
|
fetchObject: (id: string) => Promise<EvokeObject | undefined>;
|
|
5
6
|
rootObject: EvokeObject;
|
|
6
|
-
handleTreePropertySelect: (propertyId: string) => void
|
|
7
|
+
handleTreePropertySelect: (propertyId: string) => Promise<void>;
|
|
7
8
|
value: string | undefined;
|
|
9
|
+
propertyTreeMap: Record<string, Property>;
|
|
8
10
|
};
|
|
9
|
-
declare const PropertyTree: (
|
|
11
|
+
declare const PropertyTree: (props: PropertyTreeProps) => React.JSX.Element;
|
|
10
12
|
export default PropertyTree;
|
|
@@ -1,160 +1,83 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ClickAwayListener } from '@mui/material';
|
|
2
2
|
import React, { useEffect, useState } from 'react';
|
|
3
|
-
import { Autocomplete,
|
|
3
|
+
import { Autocomplete, RichTreeView } from '../../core';
|
|
4
|
+
import { Box } from '../../layout';
|
|
4
5
|
import { OverflowTextField } from '../OverflowTextField';
|
|
5
|
-
import PropertyTreeItem from './PropertyTreeItem';
|
|
6
|
-
import {
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
const [
|
|
11
|
-
const [
|
|
12
|
-
const
|
|
13
|
-
const originalValue = value ?? '';
|
|
14
|
-
const flattenObjectProperties = (properties) => {
|
|
15
|
-
let result = [];
|
|
16
|
-
properties.forEach((property) => {
|
|
17
|
-
const { children, ...rest } = property;
|
|
18
|
-
const fullId = property.id;
|
|
19
|
-
const fullName = property.name;
|
|
20
|
-
const newProperty = {
|
|
21
|
-
...rest,
|
|
22
|
-
id: fullId,
|
|
23
|
-
name: fullName,
|
|
24
|
-
};
|
|
25
|
-
result.push(newProperty);
|
|
26
|
-
// If the property has children, recursively process them
|
|
27
|
-
if (children && children.length > 0) {
|
|
28
|
-
const childProperties = flattenObjectProperties(children);
|
|
29
|
-
result = result.concat(childProperties);
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
6
|
+
import { PropertyTreeItem } from './PropertyTreeItem';
|
|
7
|
+
import { findTreeItemById, truncateNamePath, updateTreeNode } from './utils';
|
|
8
|
+
const NAME_PATH_LIMIT = 35;
|
|
9
|
+
const PropertyTree = (props) => {
|
|
10
|
+
const { fetchObject, handleTreePropertySelect, rootObject, value, propertyTreeMap } = props;
|
|
11
|
+
const [options, setOptions] = useState([]);
|
|
12
|
+
const [expandedItems, setExpandedItems] = useState([]);
|
|
13
|
+
const [openDropdown, setOpenDropdown] = useState(false);
|
|
34
14
|
useEffect(() => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
...newObjectPropertyNamePathMap,
|
|
57
|
-
[originalValue]: fieldNamePath,
|
|
58
|
-
};
|
|
59
|
-
setObjectPropertyNamePathMap(newObjectPropertyNamePathMap);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
fetchDisplayNames();
|
|
66
|
-
}
|
|
67
|
-
}, [originalValue, rootObject, objectProperties, fetchObject]);
|
|
68
|
-
const handleNodeToggle = (event, nodeIds) => {
|
|
69
|
-
const newlyExpandedNodes = nodeIds.filter((id) => !expandedNodes.includes(id));
|
|
70
|
-
newlyExpandedNodes.forEach((nodeId) => {
|
|
71
|
-
handleLoadData(nodeId);
|
|
72
|
-
});
|
|
73
|
-
setExpandedNodes(nodeIds);
|
|
15
|
+
// Transform rootObject properties to TreeItem format
|
|
16
|
+
setOptions(rootObject.properties.map((property) => ({
|
|
17
|
+
id: property.id,
|
|
18
|
+
label: property.name,
|
|
19
|
+
value: property.id,
|
|
20
|
+
type: property.type,
|
|
21
|
+
objectId: property.objectId,
|
|
22
|
+
children: property.children
|
|
23
|
+
? property.children.map((child) => ({
|
|
24
|
+
id: child.id,
|
|
25
|
+
label: child.name,
|
|
26
|
+
value: child.id,
|
|
27
|
+
type: child.type,
|
|
28
|
+
objectId: child.objectId,
|
|
29
|
+
}))
|
|
30
|
+
: undefined,
|
|
31
|
+
})));
|
|
32
|
+
setExpandedItems([]);
|
|
33
|
+
}, [rootObject.properties]);
|
|
34
|
+
const handleExpandedItemsChange = (e, itemIds) => {
|
|
35
|
+
setExpandedItems(itemIds);
|
|
74
36
|
};
|
|
75
|
-
const
|
|
76
|
-
|
|
37
|
+
const handleUpdateNodeChildren = (nodeId, children) => {
|
|
38
|
+
setOptions((prevOptions) => updateTreeNode(prevOptions, nodeId, (node) => ({ ...node, children })));
|
|
77
39
|
};
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const handleTreeItemClick = (propertyId) => {
|
|
98
|
-
const property = findPropertyById(objectProperties, propertyId);
|
|
99
|
-
// this prevents the selection of a parent node
|
|
100
|
-
if (property && !property.children) {
|
|
101
|
-
handleTreePropertySelect(property.id);
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
return (React.createElement(Autocomplete, { "aria-label": "Property Selector", value: value, fullWidth: true, sx: {
|
|
105
|
-
width: '37%',
|
|
106
|
-
}, disableClearable: true, options: propertyOptions.map((property) => {
|
|
107
|
-
return {
|
|
108
|
-
label: objectPropertyNamePathMap[property.id],
|
|
109
|
-
value: property.id,
|
|
110
|
-
};
|
|
111
|
-
}), componentsProps: {
|
|
112
|
-
paper: {
|
|
113
|
-
sx: {
|
|
114
|
-
'& .MuiAutocomplete-option': {
|
|
115
|
-
'&.Mui-focused': {
|
|
116
|
-
'&:has(.MuiTreeItem-group)': {
|
|
117
|
-
backgroundColor: 'transparent', // Remove background color if child of TreeView
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
|
-
}, getOptionLabel: (option) => {
|
|
40
|
+
return (React.createElement(Autocomplete, { "aria-label": "Property Selector", value: value, fullWidth: true, sx: { width: '37%' }, size: "small", disableClearable: true, options: options, open: openDropdown, onBlur: (e) => {
|
|
41
|
+
const targetComponents = e.relatedTarget?.getAttribute('id')?.split('-');
|
|
42
|
+
// If the user clicks on tree component with children, don't close the menu.
|
|
43
|
+
if (targetComponents?.[targetComponents.length - 1]) {
|
|
44
|
+
const treeItem = findTreeItemById(options, targetComponents[targetComponents.length - 1]);
|
|
45
|
+
// Only keep dropdown open if the clicked item has children AND was not selected
|
|
46
|
+
if (!treeItem?.children) {
|
|
47
|
+
setOpenDropdown(false);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
setOpenDropdown(false);
|
|
52
|
+
}
|
|
53
|
+
}, onOpen: () => setOpenDropdown(true), onClose: (e, reason) => {
|
|
54
|
+
// Only close if user clicked the caret close icon
|
|
55
|
+
if (reason === 'toggleInput') {
|
|
56
|
+
setOpenDropdown(false);
|
|
57
|
+
}
|
|
58
|
+
}, onFocus: () => setOpenDropdown(true), getOptionLabel: (option) => {
|
|
124
59
|
// Retrieve the full name path from the map
|
|
125
60
|
const namePath = typeof option === 'string'
|
|
126
|
-
? (
|
|
127
|
-
: (
|
|
61
|
+
? ((propertyTreeMap[option] ?? '')?.name ?? option)
|
|
62
|
+
: (propertyTreeMap[option.value]?.name ?? option.value);
|
|
128
63
|
return truncateNamePath(namePath, NAME_PATH_LIMIT);
|
|
129
64
|
}, renderInput: (params) => {
|
|
130
|
-
const fullDisplayName = value &&
|
|
131
|
-
return (React.createElement(OverflowTextField, { ...params, "aria-label": fullDisplayName, value: fullDisplayName,
|
|
132
|
-
},
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
// top level items
|
|
147
|
-
return (React.createElement(MenuItem, { sx: {
|
|
148
|
-
'&:hover': {
|
|
149
|
-
backgroundColor: 'transparent',
|
|
150
|
-
},
|
|
151
|
-
'.MuiTreeItem-group &': {
|
|
152
|
-
'&:hover': {
|
|
153
|
-
backgroundColor: 'rgba(0, 0, 0, 0.04)',
|
|
154
|
-
},
|
|
155
|
-
},
|
|
156
|
-
}, onClick: () => handleTreeItemClick(property?.id), key: property.id, value: property.id }, property.name));
|
|
157
|
-
}
|
|
65
|
+
const fullDisplayName = value && propertyTreeMap[value]?.name;
|
|
66
|
+
return (React.createElement(OverflowTextField, { ...params, "aria-label": fullDisplayName, value: fullDisplayName, placeholder: "Select a property" }));
|
|
67
|
+
}, ListboxComponent: (props) => {
|
|
68
|
+
return (React.createElement(ClickAwayListener, { onClickAway: (e) => {
|
|
69
|
+
// Close the dropdown when clicking outside but don't close when clicking to the autocomplete input
|
|
70
|
+
if (!e.target.closest('.MuiAutocomplete-root')) {
|
|
71
|
+
setOpenDropdown(false);
|
|
72
|
+
}
|
|
73
|
+
} },
|
|
74
|
+
React.createElement(Box, { ...props },
|
|
75
|
+
React.createElement(RichTreeView, { sx: { width: '100%', paddingLeft: 0 }, role: "menu", "aria-label": "Property Tree", expandedItems: expandedItems, onExpandedItemsChange: handleExpandedItemsChange, items: options, slots: {
|
|
76
|
+
item: (itemProps) => (React.createElement(PropertyTreeItem, { ...itemProps, items: options, expanded: expandedItems, setExpanded: setExpandedItems, fetchObject: fetchObject, updateNodeChildren: handleUpdateNodeChildren, handleTreePropertySelect: async (propertyId) => {
|
|
77
|
+
await handleTreePropertySelect(propertyId);
|
|
78
|
+
setOpenDropdown(false);
|
|
79
|
+
} })),
|
|
80
|
+
} }))));
|
|
158
81
|
} }));
|
|
159
82
|
};
|
|
160
83
|
export default PropertyTree;
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
import { TreeItemProps } from '@mui/x-tree-view';
|
|
1
2
|
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import { Obj } from '../../../types';
|
|
4
|
+
import { TreeItem } from './types';
|
|
5
|
+
type PropertyTreeItemProps = TreeItemProps & {
|
|
6
|
+
items: TreeItem[];
|
|
7
|
+
expanded: string[];
|
|
8
|
+
setExpanded: (expanded: string[]) => void;
|
|
9
|
+
updateNodeChildren: (nodeId: string, children: TreeItem[]) => void;
|
|
5
10
|
fetchObject: (id: string) => Promise<Obj | undefined>;
|
|
6
|
-
|
|
7
|
-
onClick: (property: string) => void;
|
|
11
|
+
handleTreePropertySelect: (propertyId: string) => Promise<void>;
|
|
8
12
|
};
|
|
9
|
-
declare const PropertyTreeItem: (
|
|
10
|
-
export
|
|
13
|
+
export declare const PropertyTreeItem: (props: PropertyTreeItemProps) => React.JSX.Element;
|
|
14
|
+
export {};
|
|
@@ -1,43 +1,45 @@
|
|
|
1
|
-
import { TreeItem } from '@mui/lab';
|
|
2
|
-
import { MenuItem } from '@mui/material';
|
|
3
1
|
import React from 'react';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
2
|
+
import { RichTreeItem } from '../../core';
|
|
3
|
+
import { findTreeItemById } from './utils';
|
|
4
|
+
export const PropertyTreeItem = (props) => {
|
|
5
|
+
const { itemId, label, items, setExpanded, expanded, children, updateNodeChildren, fetchObject, handleTreePropertySelect, } = props;
|
|
6
|
+
const onClick = async (e) => {
|
|
7
|
+
// If the item is already expanded, collapse it.
|
|
8
|
+
if (expanded.includes(itemId)) {
|
|
9
|
+
e.stopPropagation();
|
|
10
|
+
setExpanded(expanded.filter((id) => id !== itemId));
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const item = findTreeItemById(items, itemId);
|
|
14
|
+
if (item?.type === 'object') {
|
|
15
|
+
e.stopPropagation();
|
|
16
|
+
// If the item has an associated "objectId", fetch the properties of the object and expand the item.
|
|
17
|
+
if (item.objectId && item.children?.length === 1 && item.children[0].type === 'loading') {
|
|
18
|
+
const object = item.objectId ? await fetchObject(item.objectId) : undefined;
|
|
19
|
+
if (object) {
|
|
20
|
+
updateNodeChildren(itemId, (object.properties ?? []).map((prop) => ({
|
|
21
|
+
id: `${itemId}.${prop.id}`,
|
|
22
|
+
label: prop.name,
|
|
23
|
+
value: `${itemId}.${prop.id}`,
|
|
24
|
+
type: prop.type,
|
|
25
|
+
objectId: prop.objectId,
|
|
26
|
+
children: prop.type === 'object'
|
|
27
|
+
? [
|
|
28
|
+
{
|
|
29
|
+
id: `${itemId}.${prop.id}-loading`,
|
|
30
|
+
label: 'Loading...',
|
|
31
|
+
value: `${itemId}.${prop.id}-loading`,
|
|
32
|
+
type: 'loading',
|
|
33
|
+
},
|
|
34
|
+
]
|
|
35
|
+
: undefined,
|
|
36
|
+
})));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
setExpanded([...expanded, itemId]);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
await handleTreePropertySelect(itemId);
|
|
43
|
+
};
|
|
44
|
+
return React.createElement(RichTreeItem, { itemId: itemId, label: label, children: children, onClick: onClick });
|
|
42
45
|
};
|
|
43
|
-
export default PropertyTreeItem;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
import { BaseSelectorProps } from 'react-querybuilder';
|
|
3
3
|
import { ExpandedProperty } from '../../../types';
|
|
4
|
-
import { AutocompleteOption } from '../../core';
|
|
4
|
+
import { AutocompleteOption, TreeViewBaseItem } from '../../core';
|
|
5
5
|
export type ObjectProperty = {
|
|
6
6
|
id: string;
|
|
7
7
|
name: string;
|
|
@@ -51,3 +51,10 @@ export type MongoDBQueryValue = null | string | boolean | {
|
|
|
51
51
|
$in?: unknown[];
|
|
52
52
|
$nin?: unknown[];
|
|
53
53
|
};
|
|
54
|
+
export type TreeItem = TreeViewBaseItem<{
|
|
55
|
+
id: string;
|
|
56
|
+
label: string;
|
|
57
|
+
value: string;
|
|
58
|
+
type?: string;
|
|
59
|
+
objectId?: string;
|
|
60
|
+
}>;
|
|
@@ -1,23 +1,16 @@
|
|
|
1
1
|
import { Property } from '@evoke-platform/context';
|
|
2
2
|
import { RuleGroupType } from 'react-querybuilder';
|
|
3
|
-
import {
|
|
3
|
+
import { Obj, ObjectProperty } from '../../../types';
|
|
4
|
+
import { TreeItem } from './types';
|
|
4
5
|
/**
|
|
5
6
|
* Recursively updates a node in a tree structure by applying an updater function to the node with the specified ID.
|
|
6
7
|
*
|
|
7
|
-
* @param {
|
|
8
|
+
* @param {TreeItem[]} tree - The tree structure to update.
|
|
8
9
|
* @param {string} nodeId - The ID of the node to update.
|
|
9
|
-
* @param {(node:
|
|
10
|
-
* @returns {
|
|
10
|
+
* @param {(node: TreeItem) => TreeItem} updater - The function to apply to the node.
|
|
11
|
+
* @returns {TreeItem[]} - The updated tree structure.
|
|
11
12
|
*/
|
|
12
|
-
export declare const updateTreeNode: (tree:
|
|
13
|
-
/**
|
|
14
|
-
* Recursively searches for a property in a tree structure by its ID.
|
|
15
|
-
*
|
|
16
|
-
* @param {ExpandedProperty[]} properties - The tree structure to search.
|
|
17
|
-
* @param {string} id - The ID of the property to find.
|
|
18
|
-
* @returns {ExpandedProperty | null} - The found property or null if not found.
|
|
19
|
-
*/
|
|
20
|
-
export declare const findPropertyById: (properties: ExpandedProperty[], id: string) => ExpandedProperty | null;
|
|
13
|
+
export declare const updateTreeNode: (tree: TreeItem[], nodeId: string, updater: (node: TreeItem) => TreeItem) => TreeItem[];
|
|
21
14
|
type FetchObjectFunction = (id: string) => Promise<Obj | undefined>;
|
|
22
15
|
/**
|
|
23
16
|
* Fetches the display name path for a given property ID within an object hierarchy.
|
|
@@ -74,4 +67,5 @@ export declare const ALL_OPERATORS: {
|
|
|
74
67
|
* @returns {string} The resulting query string.
|
|
75
68
|
*/
|
|
76
69
|
export declare const getReadableQuery: (mongoQuery?: Record<string, unknown>, properties?: Property[]) => string;
|
|
70
|
+
export declare const findTreeItemById: (nodes: TreeItem[], nodeId: string) => TreeItem | null;
|
|
77
71
|
export {};
|
|
@@ -2,10 +2,10 @@ import { isArray, isEmpty, startCase } from 'lodash';
|
|
|
2
2
|
/**
|
|
3
3
|
* Recursively updates a node in a tree structure by applying an updater function to the node with the specified ID.
|
|
4
4
|
*
|
|
5
|
-
* @param {
|
|
5
|
+
* @param {TreeItem[]} tree - The tree structure to update.
|
|
6
6
|
* @param {string} nodeId - The ID of the node to update.
|
|
7
|
-
* @param {(node:
|
|
8
|
-
* @returns {
|
|
7
|
+
* @param {(node: TreeItem) => TreeItem} updater - The function to apply to the node.
|
|
8
|
+
* @returns {TreeItem[]} - The updated tree structure.
|
|
9
9
|
*/
|
|
10
10
|
export const updateTreeNode = (tree, nodeId, updater) => {
|
|
11
11
|
return tree.map((node) => {
|
|
@@ -20,26 +20,6 @@ export const updateTreeNode = (tree, nodeId, updater) => {
|
|
|
20
20
|
}
|
|
21
21
|
});
|
|
22
22
|
};
|
|
23
|
-
/**
|
|
24
|
-
* Recursively searches for a property in a tree structure by its ID.
|
|
25
|
-
*
|
|
26
|
-
* @param {ExpandedProperty[]} properties - The tree structure to search.
|
|
27
|
-
* @param {string} id - The ID of the property to find.
|
|
28
|
-
* @returns {ExpandedProperty | null} - The found property or null if not found.
|
|
29
|
-
*/
|
|
30
|
-
export const findPropertyById = (properties, id) => {
|
|
31
|
-
for (const prop of properties) {
|
|
32
|
-
if (prop.id === id) {
|
|
33
|
-
return prop;
|
|
34
|
-
}
|
|
35
|
-
else if (prop.children) {
|
|
36
|
-
const result = findPropertyById(prop.children, id);
|
|
37
|
-
if (result)
|
|
38
|
-
return result;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return null;
|
|
42
|
-
};
|
|
43
23
|
/**
|
|
44
24
|
* Fetches the display name path for a given property ID within an object hierarchy.
|
|
45
25
|
*
|
|
@@ -359,3 +339,13 @@ export const getReadableQuery = (mongoQuery, properties) => {
|
|
|
359
339
|
const parsedQuery = parseMongoDB(mongoQuery);
|
|
360
340
|
return buildQueryString(parsedQuery);
|
|
361
341
|
};
|
|
342
|
+
export const findTreeItemById = (nodes, nodeId) => {
|
|
343
|
+
for (const node of nodes) {
|
|
344
|
+
if (node.id === nodeId)
|
|
345
|
+
return node;
|
|
346
|
+
const found = node.children && findTreeItemById(node.children, nodeId);
|
|
347
|
+
if (found)
|
|
348
|
+
return found;
|
|
349
|
+
}
|
|
350
|
+
return null;
|
|
351
|
+
};
|
|
@@ -73,6 +73,9 @@ const RepeatableField = (props) => {
|
|
|
73
73
|
.get(getPrefixedUrl(`/forms/${formId || action?.defaultFormId}`))
|
|
74
74
|
.then((evokeForm) => {
|
|
75
75
|
setForm(evokeForm);
|
|
76
|
+
setFetchedOptions({
|
|
77
|
+
[`${fieldDefinition.id}${action?.type === 'delete' ? '-deleteForm' : action?.type === 'create' ? '-createForm' : '-updateForm'}`]: evokeForm,
|
|
78
|
+
});
|
|
76
79
|
})
|
|
77
80
|
.catch((error) => {
|
|
78
81
|
console.error(error);
|
|
@@ -22,7 +22,7 @@ const ObjectPropertyInput = (props) => {
|
|
|
22
22
|
const [openOptions, setOpenOptions] = useState(false);
|
|
23
23
|
const [hasFetched, setHasFetched] = useState(fetchedOptions[`${id}OptionsHaveFetched`] || false);
|
|
24
24
|
const [options, setOptions] = useState(fetchedOptions[`${id}Options`] || []);
|
|
25
|
-
const [layout, setLayout] = useState();
|
|
25
|
+
const [layout, setLayout] = useState(fetchedOptions[`${id}ViewLayout`]);
|
|
26
26
|
const [form, setForm] = useState();
|
|
27
27
|
const [snackbarError, setSnackbarError] = useState({
|
|
28
28
|
showAlert: false,
|
|
@@ -68,10 +68,17 @@ const ObjectPropertyInput = (props) => {
|
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
70
|
if (viewLayout) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
if (!fetchedOptions[`${id}ViewLayout`]) {
|
|
72
|
+
apiServices
|
|
73
|
+
.get(getPrefixedUrl(`/objects/${viewLayout.objectId}/${displayOption === 'dropdown' ? 'dropdown' : 'table'}Layouts/${viewLayout.id}`))
|
|
74
|
+
.then((layout) => {
|
|
75
|
+
setLayout(layout);
|
|
76
|
+
setFetchedOptions({
|
|
77
|
+
[`${id}ViewLayout`]: layout,
|
|
78
|
+
});
|
|
79
|
+
})
|
|
80
|
+
.catch((err) => setLayout(defaultViewLayout));
|
|
81
|
+
}
|
|
75
82
|
}
|
|
76
83
|
else {
|
|
77
84
|
setLayout(defaultViewLayout);
|
|
@@ -255,6 +255,21 @@ CriteriaBuilderRelatedObject.args = {
|
|
|
255
255
|
},
|
|
256
256
|
],
|
|
257
257
|
},
|
|
258
|
+
{
|
|
259
|
+
id: 'dynamic.id',
|
|
260
|
+
name: 'Dynamic ID',
|
|
261
|
+
type: 'string',
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
id: 'dynamic.name',
|
|
265
|
+
name: 'Dynamic Name',
|
|
266
|
+
type: 'string',
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
id: 'dynamic.objectId',
|
|
270
|
+
name: 'Dynamic Object ID',
|
|
271
|
+
type: 'string',
|
|
272
|
+
},
|
|
258
273
|
{
|
|
259
274
|
id: 'name',
|
|
260
275
|
name: 'Name',
|
|
@@ -526,6 +541,21 @@ CriteriaBuilderRelatedObject.args = {
|
|
|
526
541
|
type: 'string',
|
|
527
542
|
required: false,
|
|
528
543
|
},
|
|
544
|
+
{
|
|
545
|
+
id: 'dynamic.id',
|
|
546
|
+
name: 'Dynamic ID',
|
|
547
|
+
type: 'string',
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
id: 'dynamic.name',
|
|
551
|
+
name: 'Dynamic Name',
|
|
552
|
+
type: 'string',
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
id: 'dynamic.objectId',
|
|
556
|
+
name: 'Dynamic Object Id',
|
|
557
|
+
type: 'string',
|
|
558
|
+
},
|
|
529
559
|
],
|
|
530
560
|
},
|
|
531
561
|
},
|