@nocobase/flow-engine 2.0.55 → 2.0.57
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/lib/components/subModel/LazyDropdown.js +17 -6
- package/lib/components/subModel/utils.js +7 -1
- package/lib/data-source/index.js +1 -1
- package/package.json +4 -4
- package/src/components/subModel/LazyDropdown.tsx +18 -7
- package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +44 -0
- package/src/components/subModel/__tests__/utils.test.ts +24 -0
- package/src/components/subModel/utils.ts +6 -0
- package/src/data-source/index.ts +1 -1
|
@@ -253,6 +253,21 @@ const SearchInputWithAutoFocus = /* @__PURE__ */ __name((props) => {
|
|
|
253
253
|
return /* @__PURE__ */ import_react.default.createElement(import_antd.Input, { ref: inputRef, ...rest });
|
|
254
254
|
}, "SearchInputWithAutoFocus");
|
|
255
255
|
const getKeyPath = /* @__PURE__ */ __name((path, key) => [...path, key].join("/"), "getKeyPath");
|
|
256
|
+
const getLabelSearchText = /* @__PURE__ */ __name((label) => {
|
|
257
|
+
if (label === null || label === void 0 || typeof label === "boolean") {
|
|
258
|
+
return "";
|
|
259
|
+
}
|
|
260
|
+
if (typeof label === "string" || typeof label === "number") {
|
|
261
|
+
return String(label);
|
|
262
|
+
}
|
|
263
|
+
if (Array.isArray(label)) {
|
|
264
|
+
return label.map(getLabelSearchText).join(" ");
|
|
265
|
+
}
|
|
266
|
+
if (import_react.default.isValidElement(label)) {
|
|
267
|
+
return getLabelSearchText(label.props.children);
|
|
268
|
+
}
|
|
269
|
+
return "";
|
|
270
|
+
}, "getLabelSearchText");
|
|
256
271
|
const createSearchItem = /* @__PURE__ */ __name((item, searchKey, currentSearchValue, menuVisible, t, updateSearchValue) => ({
|
|
257
272
|
key: `${searchKey}-search`,
|
|
258
273
|
type: "group",
|
|
@@ -349,13 +364,9 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
349
364
|
const currentSearchValue = searchValues[searchKey] || "";
|
|
350
365
|
const filteredChildren = currentSearchValue ? (/* @__PURE__ */ __name(function deepFilter(items2) {
|
|
351
366
|
const searchText = currentSearchValue.toLowerCase();
|
|
352
|
-
const tryString = /* @__PURE__ */ __name((v) => {
|
|
353
|
-
if (!v) return "";
|
|
354
|
-
return typeof v === "string" ? v : String(v);
|
|
355
|
-
}, "tryString");
|
|
356
367
|
return items2.map((child) => {
|
|
357
|
-
const labelStr =
|
|
358
|
-
const selfMatch = labelStr.includes(searchText)
|
|
368
|
+
const labelStr = getLabelSearchText(child.label).toLowerCase();
|
|
369
|
+
const selfMatch = labelStr.includes(searchText);
|
|
359
370
|
if (child.type === "group" && Array.isArray(child.children)) {
|
|
360
371
|
const nested = deepFilter(child.children);
|
|
361
372
|
if (selfMatch || nested.length > 0) {
|
|
@@ -172,7 +172,7 @@ function buildSubModelItems(subModelBaseClass, exclude = []) {
|
|
|
172
172
|
__name(buildSubModelItems, "buildSubModelItems");
|
|
173
173
|
function buildSubModelGroups(subModelBaseClasses = []) {
|
|
174
174
|
return async (ctx) => {
|
|
175
|
-
var _a, _b, _c;
|
|
175
|
+
var _a, _b, _c, _d, _e;
|
|
176
176
|
const items = [];
|
|
177
177
|
const exclude = [];
|
|
178
178
|
for (const subModelBaseClass of subModelBaseClasses) {
|
|
@@ -203,11 +203,15 @@ function buildSubModelGroups(subModelBaseClasses = []) {
|
|
|
203
203
|
const baseKey = typeof subModelBaseClass === "string" ? subModelBaseClass : BaseClass.name;
|
|
204
204
|
const menuType = ((_b = BaseClass == null ? void 0 : BaseClass.meta) == null ? void 0 : _b.menuType) || "group";
|
|
205
205
|
const groupSort = ((_c = BaseClass == null ? void 0 : BaseClass.meta) == null ? void 0 : _c.sort) ?? 1e3;
|
|
206
|
+
const searchable = !!((_d = BaseClass == null ? void 0 : BaseClass.meta) == null ? void 0 : _d.searchable);
|
|
207
|
+
const searchPlaceholder = (_e = BaseClass == null ? void 0 : BaseClass.meta) == null ? void 0 : _e.searchPlaceholder;
|
|
206
208
|
if (menuType === "submenu") {
|
|
207
209
|
items.push({
|
|
208
210
|
key: baseKey,
|
|
209
211
|
label: groupLabel,
|
|
210
212
|
sort: groupSort,
|
|
213
|
+
searchable,
|
|
214
|
+
searchPlaceholder,
|
|
211
215
|
children
|
|
212
216
|
});
|
|
213
217
|
} else {
|
|
@@ -216,6 +220,8 @@ function buildSubModelGroups(subModelBaseClasses = []) {
|
|
|
216
220
|
type: "group",
|
|
217
221
|
label: groupLabel,
|
|
218
222
|
sort: groupSort,
|
|
223
|
+
searchable,
|
|
224
|
+
searchPlaceholder,
|
|
219
225
|
children
|
|
220
226
|
});
|
|
221
227
|
}
|
package/lib/data-source/index.js
CHANGED
|
@@ -759,7 +759,7 @@ const _CollectionField = class _CollectionField {
|
|
|
759
759
|
{
|
|
760
760
|
...import_lodash.default.omit(((_a = this.options.uiSchema) == null ? void 0 : _a["x-component-props"]) || {}, "fieldNames"),
|
|
761
761
|
options: this.enum.length ? this.enum : void 0,
|
|
762
|
-
mode: this.
|
|
762
|
+
mode: this.interface === "multipleSelect" ? "multiple" : void 0,
|
|
763
763
|
multiple: target ? ["belongsToMany", "hasMany", "belongsToArray"].includes(type) : void 0,
|
|
764
764
|
maxCount: target && !["belongsToMany", "hasMany", "belongsToArray"].includes(type) ? 1 : void 0,
|
|
765
765
|
target,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/flow-engine",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.57",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A standalone flow engine for NocoBase, managing workflows, models, and actions.",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"@formily/antd-v5": "1.x",
|
|
10
10
|
"@formily/reactive": "2.x",
|
|
11
|
-
"@nocobase/sdk": "2.0.
|
|
12
|
-
"@nocobase/shared": "2.0.
|
|
11
|
+
"@nocobase/sdk": "2.0.57",
|
|
12
|
+
"@nocobase/shared": "2.0.57",
|
|
13
13
|
"ahooks": "^3.7.2",
|
|
14
14
|
"axios": "^1.7.0",
|
|
15
15
|
"dayjs": "^1.11.9",
|
|
@@ -37,5 +37,5 @@
|
|
|
37
37
|
],
|
|
38
38
|
"author": "NocoBase Team",
|
|
39
39
|
"license": "Apache-2.0",
|
|
40
|
-
"gitHead": "
|
|
40
|
+
"gitHead": "629bf05e63bca0cbd60fffb87057d45bda5d9222"
|
|
41
41
|
}
|
|
@@ -358,6 +358,22 @@ const SearchInputWithAutoFocus: FC<InputProps & { visible: boolean }> = (props)
|
|
|
358
358
|
|
|
359
359
|
const getKeyPath = (path: string[], key: string) => [...path, key].join('/');
|
|
360
360
|
|
|
361
|
+
const getLabelSearchText = (label: React.ReactNode): string => {
|
|
362
|
+
if (label === null || label === undefined || typeof label === 'boolean') {
|
|
363
|
+
return '';
|
|
364
|
+
}
|
|
365
|
+
if (typeof label === 'string' || typeof label === 'number') {
|
|
366
|
+
return String(label);
|
|
367
|
+
}
|
|
368
|
+
if (Array.isArray(label)) {
|
|
369
|
+
return label.map(getLabelSearchText).join(' ');
|
|
370
|
+
}
|
|
371
|
+
if (React.isValidElement(label)) {
|
|
372
|
+
return getLabelSearchText(label.props.children);
|
|
373
|
+
}
|
|
374
|
+
return '';
|
|
375
|
+
};
|
|
376
|
+
|
|
361
377
|
const createSearchItem = (
|
|
362
378
|
item: Item,
|
|
363
379
|
searchKey: string,
|
|
@@ -498,15 +514,10 @@ const LazyDropdown: React.FC<Omit<DropdownProps, 'menu'> & { menu: LazyDropdownM
|
|
|
498
514
|
const filteredChildren = currentSearchValue
|
|
499
515
|
? (function deepFilter(items: Item[]): Item[] {
|
|
500
516
|
const searchText = currentSearchValue.toLowerCase();
|
|
501
|
-
const tryString = (v: any) => {
|
|
502
|
-
if (!v) return '';
|
|
503
|
-
return typeof v === 'string' ? v : String(v);
|
|
504
|
-
};
|
|
505
517
|
return items
|
|
506
518
|
.map((child) => {
|
|
507
|
-
const labelStr =
|
|
508
|
-
const selfMatch =
|
|
509
|
-
labelStr.includes(searchText) || (child.key && String(child.key).toLowerCase().includes(searchText));
|
|
519
|
+
const labelStr = getLabelSearchText(child.label).toLowerCase();
|
|
520
|
+
const selfMatch = labelStr.includes(searchText);
|
|
510
521
|
if (child.type === 'group' && Array.isArray(child.children)) {
|
|
511
522
|
const nested = deepFilter(child.children);
|
|
512
523
|
if (selfMatch || nested.length > 0) {
|
|
@@ -188,6 +188,50 @@ describe('transformItems - searchable flags', () => {
|
|
|
188
188
|
expect(submenu.searchPlaceholder).toBe('Search blocks');
|
|
189
189
|
expect(Array.isArray(submenu.children)).toBe(true);
|
|
190
190
|
});
|
|
191
|
+
|
|
192
|
+
it('filters searchable field menus by display label instead of item key', async () => {
|
|
193
|
+
const engine = new FlowEngine();
|
|
194
|
+
engine.flowSettings.forceEnable();
|
|
195
|
+
const parent = engine.createModel<FlowModel>({ use: FlowModel });
|
|
196
|
+
const user = userEvent.setup();
|
|
197
|
+
|
|
198
|
+
const items = [
|
|
199
|
+
{
|
|
200
|
+
key: 'fields',
|
|
201
|
+
label: '',
|
|
202
|
+
type: 'group' as const,
|
|
203
|
+
searchable: true,
|
|
204
|
+
searchPlaceholder: 'Search fields',
|
|
205
|
+
children: [
|
|
206
|
+
{ key: 'field_name', label: 'Field display name' },
|
|
207
|
+
{ key: 'other_field', label: 'Other field' },
|
|
208
|
+
],
|
|
209
|
+
},
|
|
210
|
+
];
|
|
211
|
+
|
|
212
|
+
render(
|
|
213
|
+
<FlowEngineProvider engine={engine}>
|
|
214
|
+
<ConfigProvider>
|
|
215
|
+
<App>
|
|
216
|
+
<AddSubModelButton model={parent} items={items as any} subModelKey="items">
|
|
217
|
+
Open
|
|
218
|
+
</AddSubModelButton>
|
|
219
|
+
</App>
|
|
220
|
+
</ConfigProvider>
|
|
221
|
+
</FlowEngineProvider>,
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
await user.click(screen.getByText('Open'));
|
|
225
|
+
const searchInput = await screen.findByPlaceholderText('Search fields');
|
|
226
|
+
expect(screen.getByText('Field display name')).toBeInTheDocument();
|
|
227
|
+
|
|
228
|
+
await user.type(searchInput, 'field_name');
|
|
229
|
+
await waitFor(() => expect(screen.queryByText('Field display name')).not.toBeInTheDocument());
|
|
230
|
+
|
|
231
|
+
await user.clear(searchInput);
|
|
232
|
+
await user.type(searchInput, 'display');
|
|
233
|
+
await waitFor(() => expect(screen.getByText('Field display name')).toBeInTheDocument());
|
|
234
|
+
});
|
|
191
235
|
});
|
|
192
236
|
|
|
193
237
|
describe('transformItems - hide', () => {
|
|
@@ -100,6 +100,30 @@ describe('subModel/utils', () => {
|
|
|
100
100
|
expect(groups[0].children).toBeTruthy();
|
|
101
101
|
});
|
|
102
102
|
|
|
103
|
+
it('preserves searchable meta on generated groups', async () => {
|
|
104
|
+
const engine = new FlowEngine();
|
|
105
|
+
|
|
106
|
+
class Base extends FlowModel {}
|
|
107
|
+
Base.define({
|
|
108
|
+
label: 'Base Group',
|
|
109
|
+
searchable: true,
|
|
110
|
+
searchPlaceholder: 'Search fields',
|
|
111
|
+
});
|
|
112
|
+
const BaseDC = attachDefineChildren(Base, async () => [{ key: 'title', label: 'Title' }]);
|
|
113
|
+
|
|
114
|
+
engine.registerModels({ Base: BaseDC });
|
|
115
|
+
|
|
116
|
+
const model = engine.createModel({ use: 'FlowModel' });
|
|
117
|
+
const ctx = model.context;
|
|
118
|
+
|
|
119
|
+
const groupsFactory = buildSubModelGroups([BaseDC]);
|
|
120
|
+
const groups = await groupsFactory(ctx);
|
|
121
|
+
|
|
122
|
+
expect(groups).toHaveLength(1);
|
|
123
|
+
expect(groups[0].searchable).toBe(true);
|
|
124
|
+
expect(groups[0].searchPlaceholder).toBe('Search fields');
|
|
125
|
+
});
|
|
126
|
+
|
|
103
127
|
it('invokes buildSubModelItems when meta.children is false', async () => {
|
|
104
128
|
const engine = new FlowEngine();
|
|
105
129
|
|
|
@@ -196,12 +196,16 @@ export function buildSubModelGroups(subModelBaseClasses: (string | ModelConstruc
|
|
|
196
196
|
const baseKey = typeof subModelBaseClass === 'string' ? subModelBaseClass : BaseClass.name;
|
|
197
197
|
const menuType = BaseClass?.meta?.menuType || 'group';
|
|
198
198
|
const groupSort = BaseClass?.meta?.sort ?? 1000;
|
|
199
|
+
const searchable = !!BaseClass?.meta?.searchable;
|
|
200
|
+
const searchPlaceholder = BaseClass?.meta?.searchPlaceholder;
|
|
199
201
|
if (menuType === 'submenu') {
|
|
200
202
|
// 作为可点击的一级项,展开二级子菜单
|
|
201
203
|
items.push({
|
|
202
204
|
key: baseKey,
|
|
203
205
|
label: groupLabel,
|
|
204
206
|
sort: groupSort,
|
|
207
|
+
searchable,
|
|
208
|
+
searchPlaceholder,
|
|
205
209
|
children,
|
|
206
210
|
});
|
|
207
211
|
} else {
|
|
@@ -211,6 +215,8 @@ export function buildSubModelGroups(subModelBaseClasses: (string | ModelConstruc
|
|
|
211
215
|
type: 'group',
|
|
212
216
|
label: groupLabel,
|
|
213
217
|
sort: groupSort,
|
|
218
|
+
searchable,
|
|
219
|
+
searchPlaceholder,
|
|
214
220
|
children,
|
|
215
221
|
});
|
|
216
222
|
}
|
package/src/data-source/index.ts
CHANGED
|
@@ -841,7 +841,7 @@ export class CollectionField {
|
|
|
841
841
|
{
|
|
842
842
|
..._.omit(this.options.uiSchema?.['x-component-props'] || {}, 'fieldNames'),
|
|
843
843
|
options: this.enum.length ? this.enum : undefined,
|
|
844
|
-
mode: this.
|
|
844
|
+
mode: this.interface === 'multipleSelect' ? 'multiple' : undefined,
|
|
845
845
|
multiple: target ? ['belongsToMany', 'hasMany', 'belongsToArray'].includes(type) : undefined,
|
|
846
846
|
maxCount: target && !['belongsToMany', 'hasMany', 'belongsToArray'].includes(type) ? 1 : undefined,
|
|
847
847
|
target: target,
|