@nocobase/flow-engine 2.1.0-beta.26 → 2.1.0-beta.27
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/FormItem.d.ts +6 -0
- package/lib/components/FormItem.js +11 -3
- package/lib/data-source/index.d.ts +2 -0
- package/lib/data-source/index.js +35 -3
- package/package.json +4 -4
- package/src/components/FormItem.tsx +7 -1
- package/src/components/__tests__/FormItem.test.tsx +25 -0
- package/src/data-source/__tests__/collection.test.ts +41 -2
- package/src/data-source/__tests__/index.test.ts +34 -0
- package/src/data-source/index.ts +45 -3
|
@@ -14,5 +14,11 @@ interface ExtendedFormItemProps extends FormItemProps {
|
|
|
14
14
|
labelWrap?: boolean;
|
|
15
15
|
showLabel?: boolean;
|
|
16
16
|
}
|
|
17
|
+
export declare const verticalFormItemLabelStyle: {
|
|
18
|
+
paddingBottom: number;
|
|
19
|
+
};
|
|
20
|
+
export declare const formItemStyle: {
|
|
21
|
+
marginBottom: number;
|
|
22
|
+
};
|
|
17
23
|
export declare const FormItem: ({ children, showLabel, labelWidth, ...rest }: ExtendedFormItemProps & ChildExtraProps) => React.JSX.Element;
|
|
18
24
|
export {};
|
|
@@ -37,11 +37,15 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
37
37
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
38
38
|
var FormItem_exports = {};
|
|
39
39
|
__export(FormItem_exports, {
|
|
40
|
-
FormItem: () => FormItem
|
|
40
|
+
FormItem: () => FormItem,
|
|
41
|
+
formItemStyle: () => formItemStyle,
|
|
42
|
+
verticalFormItemLabelStyle: () => verticalFormItemLabelStyle
|
|
41
43
|
});
|
|
42
44
|
module.exports = __toCommonJS(FormItem_exports);
|
|
43
45
|
var import_react = __toESM(require("react"));
|
|
44
46
|
var import_antd = require("antd");
|
|
47
|
+
const verticalFormItemLabelStyle = { paddingBottom: 0 };
|
|
48
|
+
const formItemStyle = { marginBottom: 12 };
|
|
45
49
|
const formItemPropKeys = [
|
|
46
50
|
"colon",
|
|
47
51
|
"dependencies",
|
|
@@ -90,6 +94,7 @@ const FormItem = /* @__PURE__ */ __name(({
|
|
|
90
94
|
});
|
|
91
95
|
const { label, labelWrap, colon = true, layout } = rest;
|
|
92
96
|
const effectiveLabelWrap = !layout || layout === "vertical" ? true : labelWrap;
|
|
97
|
+
const labelColStyle = layout === "vertical" ? { width: labelWidth, ...verticalFormItemLabelStyle } : { width: labelWidth };
|
|
93
98
|
const renderLabel = /* @__PURE__ */ __name(() => {
|
|
94
99
|
if (!showLabel) return null;
|
|
95
100
|
if (effectiveLabelWrap) {
|
|
@@ -132,7 +137,8 @@ const FormItem = /* @__PURE__ */ __name(({
|
|
|
132
137
|
import_antd.Form.Item,
|
|
133
138
|
{
|
|
134
139
|
...rest,
|
|
135
|
-
|
|
140
|
+
style: { ...formItemStyle, ...rest.style },
|
|
141
|
+
labelCol: { style: labelColStyle },
|
|
136
142
|
layout,
|
|
137
143
|
label: renderLabel(),
|
|
138
144
|
colon: false,
|
|
@@ -147,5 +153,7 @@ const FormItem = /* @__PURE__ */ __name(({
|
|
|
147
153
|
}, "FormItem");
|
|
148
154
|
// Annotate the CommonJS export names for ESM import in node:
|
|
149
155
|
0 && (module.exports = {
|
|
150
|
-
FormItem
|
|
156
|
+
FormItem,
|
|
157
|
+
formItemStyle,
|
|
158
|
+
verticalFormItemLabelStyle
|
|
151
159
|
});
|
|
@@ -90,6 +90,8 @@ export declare class DataSourceManager {
|
|
|
90
90
|
force: boolean;
|
|
91
91
|
}): Promise<void>;
|
|
92
92
|
}
|
|
93
|
+
export type CollectionFieldInterfaceDataSourceManager = Pick<DataSourceManager, 'collectionFieldInterfaceManager'>;
|
|
94
|
+
export declare function getCollectionFieldInterface(interfaceName: string | undefined, ...dataSourceManagers: Array<CollectionFieldInterfaceDataSourceManager | null | undefined>): any;
|
|
93
95
|
export declare class DataSource {
|
|
94
96
|
dataSourceManager: DataSourceManager;
|
|
95
97
|
collectionManager: CollectionManager;
|
package/lib/data-source/index.js
CHANGED
|
@@ -42,6 +42,7 @@ __export(data_source_exports, {
|
|
|
42
42
|
CollectionManager: () => CollectionManager,
|
|
43
43
|
DataSource: () => DataSource,
|
|
44
44
|
DataSourceManager: () => DataSourceManager,
|
|
45
|
+
getCollectionFieldInterface: () => getCollectionFieldInterface,
|
|
45
46
|
isFieldInterfaceMatch: () => isFieldInterfaceMatch,
|
|
46
47
|
jioToJoiSchema: () => import_jioToJoiSchema.jioToJoiSchema
|
|
47
48
|
});
|
|
@@ -279,6 +280,20 @@ const _DataSourceManager = class _DataSourceManager {
|
|
|
279
280
|
};
|
|
280
281
|
__name(_DataSourceManager, "DataSourceManager");
|
|
281
282
|
let DataSourceManager = _DataSourceManager;
|
|
283
|
+
function getCollectionFieldInterface(interfaceName, ...dataSourceManagers) {
|
|
284
|
+
if (!interfaceName) {
|
|
285
|
+
return void 0;
|
|
286
|
+
}
|
|
287
|
+
for (const dataSourceManager of dataSourceManagers) {
|
|
288
|
+
const collectionFieldInterfaceManager = dataSourceManager == null ? void 0 : dataSourceManager.collectionFieldInterfaceManager;
|
|
289
|
+
const getFieldInterface = collectionFieldInterfaceManager == null ? void 0 : collectionFieldInterfaceManager.getFieldInterface;
|
|
290
|
+
if (typeof getFieldInterface === "function") {
|
|
291
|
+
return getFieldInterface.call(collectionFieldInterfaceManager, interfaceName);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return void 0;
|
|
295
|
+
}
|
|
296
|
+
__name(getCollectionFieldInterface, "getCollectionFieldInterface");
|
|
282
297
|
const _DataSource = class _DataSource {
|
|
283
298
|
dataSourceManager;
|
|
284
299
|
collectionManager;
|
|
@@ -982,7 +997,17 @@ const _CollectionField = class _CollectionField {
|
|
|
982
997
|
abortEarly: false
|
|
983
998
|
});
|
|
984
999
|
if (error) {
|
|
985
|
-
const message = error.details.map((d) =>
|
|
1000
|
+
const message = error.details.map((d) => {
|
|
1001
|
+
const translated = this.flowEngine.translate(d.type, {
|
|
1002
|
+
...d.context,
|
|
1003
|
+
ns: "data-source-main",
|
|
1004
|
+
label
|
|
1005
|
+
});
|
|
1006
|
+
if (translated && translated !== d.type) {
|
|
1007
|
+
return translated;
|
|
1008
|
+
}
|
|
1009
|
+
return d.message.replace(/"value"/g, `"${label}"`);
|
|
1010
|
+
}).join(", ");
|
|
986
1011
|
return Promise.reject(message);
|
|
987
1012
|
}
|
|
988
1013
|
return Promise.resolve();
|
|
@@ -1002,8 +1027,14 @@ const _CollectionField = class _CollectionField {
|
|
|
1002
1027
|
return this.targetCollection.getFields();
|
|
1003
1028
|
}
|
|
1004
1029
|
getInterfaceOptions() {
|
|
1005
|
-
|
|
1006
|
-
|
|
1030
|
+
var _a, _b, _c;
|
|
1031
|
+
const ctx = this.flowEngine.context;
|
|
1032
|
+
return getCollectionFieldInterface(
|
|
1033
|
+
this.interface,
|
|
1034
|
+
(_b = (_a = this.collection) == null ? void 0 : _a.dataSource) == null ? void 0 : _b.dataSourceManager,
|
|
1035
|
+
ctx.dataSourceManager,
|
|
1036
|
+
(_c = ctx.app) == null ? void 0 : _c.dataSourceManager
|
|
1037
|
+
);
|
|
1007
1038
|
}
|
|
1008
1039
|
getFilterOperators() {
|
|
1009
1040
|
var _a;
|
|
@@ -1068,6 +1099,7 @@ __name(isFieldInterfaceMatch, "isFieldInterfaceMatch");
|
|
|
1068
1099
|
CollectionManager,
|
|
1069
1100
|
DataSource,
|
|
1070
1101
|
DataSourceManager,
|
|
1102
|
+
getCollectionFieldInterface,
|
|
1071
1103
|
isFieldInterfaceMatch,
|
|
1072
1104
|
jioToJoiSchema
|
|
1073
1105
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/flow-engine",
|
|
3
|
-
"version": "2.1.0-beta.
|
|
3
|
+
"version": "2.1.0-beta.27",
|
|
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.1.0-beta.
|
|
12
|
-
"@nocobase/shared": "2.1.0-beta.
|
|
11
|
+
"@nocobase/sdk": "2.1.0-beta.27",
|
|
12
|
+
"@nocobase/shared": "2.1.0-beta.27",
|
|
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": "6d9e2d4ac3c6bf40951c8eb252860e47aa3a3c37"
|
|
41
41
|
}
|
|
@@ -19,6 +19,9 @@ interface ExtendedFormItemProps extends FormItemProps {
|
|
|
19
19
|
showLabel?: boolean;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
export const verticalFormItemLabelStyle = { paddingBottom: 0 };
|
|
23
|
+
export const formItemStyle = { marginBottom: 12 };
|
|
24
|
+
|
|
22
25
|
const formItemPropKeys: (keyof ExtendedFormItemProps)[] = [
|
|
23
26
|
'colon',
|
|
24
27
|
'dependencies',
|
|
@@ -73,6 +76,8 @@ export const FormItem = ({
|
|
|
73
76
|
});
|
|
74
77
|
const { label, labelWrap, colon = true, layout } = rest;
|
|
75
78
|
const effectiveLabelWrap = !layout || layout === 'vertical' ? true : labelWrap;
|
|
79
|
+
const labelColStyle =
|
|
80
|
+
layout === 'vertical' ? { width: labelWidth, ...verticalFormItemLabelStyle } : { width: labelWidth };
|
|
76
81
|
const renderLabel = () => {
|
|
77
82
|
if (!showLabel) return null;
|
|
78
83
|
if (effectiveLabelWrap) {
|
|
@@ -118,7 +123,8 @@ export const FormItem = ({
|
|
|
118
123
|
return (
|
|
119
124
|
<Form.Item
|
|
120
125
|
{...rest}
|
|
121
|
-
|
|
126
|
+
style={{ ...formItemStyle, ...rest.style }}
|
|
127
|
+
labelCol={{ style: labelColStyle }}
|
|
122
128
|
layout={layout}
|
|
123
129
|
label={renderLabel()}
|
|
124
130
|
colon={false}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, expect, it } from 'vitest';
|
|
11
|
+
import { formItemStyle, verticalFormItemLabelStyle } from '../FormItem';
|
|
12
|
+
|
|
13
|
+
describe('FormItem', () => {
|
|
14
|
+
it('keeps vertical label-to-value spacing consistent with v1', () => {
|
|
15
|
+
expect(verticalFormItemLabelStyle).toEqual({
|
|
16
|
+
paddingBottom: 0,
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('keeps spacing between form items consistent with v1', () => {
|
|
21
|
+
expect(formItemStyle).toEqual({
|
|
22
|
+
marginBottom: 12,
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { describe, expect, it } from 'vitest';
|
|
11
|
-
import { DataSource, DataSourceManager, isFieldInterfaceMatch } from '../index';
|
|
10
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
11
|
+
import { DataSource, DataSourceManager, getCollectionFieldInterface, isFieldInterfaceMatch } from '../index';
|
|
12
12
|
import { FlowEngine } from '../../flowEngine';
|
|
13
13
|
|
|
14
14
|
describe('Collection/Field helpers', () => {
|
|
@@ -55,4 +55,43 @@ describe('Collection/Field helpers', () => {
|
|
|
55
55
|
const field = posts.getFieldByPath('category.name');
|
|
56
56
|
expect(field?.name).toBe('name');
|
|
57
57
|
});
|
|
58
|
+
|
|
59
|
+
it('resolves collection field interfaces from the first available manager', () => {
|
|
60
|
+
const first = { collectionFieldInterfaceManager: { getFieldInterface: vi.fn((name) => ({ name })) } };
|
|
61
|
+
const second = { collectionFieldInterfaceManager: { getFieldInterface: vi.fn((name) => ({ name })) } };
|
|
62
|
+
|
|
63
|
+
expect(getCollectionFieldInterface('input', {}, first, second)).toEqual({ name: 'input' });
|
|
64
|
+
expect(first.collectionFieldInterfaceManager.getFieldInterface).toHaveBeenCalledWith('input');
|
|
65
|
+
expect(second.collectionFieldInterfaceManager.getFieldInterface).not.toHaveBeenCalled();
|
|
66
|
+
expect(getCollectionFieldInterface(undefined, first)).toBeUndefined();
|
|
67
|
+
expect(getCollectionFieldInterface('input', {})).toBeUndefined();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('uses collection field interface resolver from getInterfaceOptions', () => {
|
|
71
|
+
const { ds, m } = setup();
|
|
72
|
+
const ctx = m.flowEngine.context;
|
|
73
|
+
const getOwnerFieldInterface = vi.fn((name: string) => ({ name, source: 'owner' }));
|
|
74
|
+
const getLegacyFieldInterface = vi.fn((name: string) => ({ name, source: 'legacy' }));
|
|
75
|
+
|
|
76
|
+
ctx.defineProperty('app', {
|
|
77
|
+
value: {
|
|
78
|
+
dataSourceManager: {
|
|
79
|
+
collectionFieldInterfaceManager: {
|
|
80
|
+
getFieldInterface: getLegacyFieldInterface,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
ds.addCollection({
|
|
86
|
+
name: 'posts',
|
|
87
|
+
fields: [{ name: 'title', type: 'string', interface: 'input' }],
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const field = ds.getCollection('posts')!.getField('title')!;
|
|
91
|
+
expect(field.getInterfaceOptions()).toEqual({ name: 'input', source: 'legacy' });
|
|
92
|
+
|
|
93
|
+
m.setCollectionFieldInterfaceManager({ getFieldInterface: getOwnerFieldInterface });
|
|
94
|
+
expect(field.getInterfaceOptions()).toEqual({ name: 'input', source: 'owner' });
|
|
95
|
+
expect(getLegacyFieldInterface).toHaveBeenCalledTimes(1);
|
|
96
|
+
});
|
|
58
97
|
});
|
|
@@ -80,6 +80,40 @@ describe('DataSource & Collection APIs', () => {
|
|
|
80
80
|
).toThrow(/circular/);
|
|
81
81
|
});
|
|
82
82
|
|
|
83
|
+
it('translates validation messages from data-source-main in component rules', async () => {
|
|
84
|
+
const { m, engine } = makeManager();
|
|
85
|
+
engine.context.i18n = {
|
|
86
|
+
t: (key: string, options?: Record<string, any>) => {
|
|
87
|
+
if (key === 'string.length' && options?.ns === 'data-source-main') {
|
|
88
|
+
return `${options.label} 长度必须为 ${options.limit} 个字符`;
|
|
89
|
+
}
|
|
90
|
+
return key;
|
|
91
|
+
},
|
|
92
|
+
} as any;
|
|
93
|
+
|
|
94
|
+
const ds = new DataSource({ key: 'main' });
|
|
95
|
+
m.addDataSource(ds);
|
|
96
|
+
ds.addCollection({
|
|
97
|
+
name: 'posts',
|
|
98
|
+
fields: [
|
|
99
|
+
{
|
|
100
|
+
name: 'title',
|
|
101
|
+
type: 'string',
|
|
102
|
+
interface: 'text',
|
|
103
|
+
title: '单行文本',
|
|
104
|
+
validation: {
|
|
105
|
+
type: 'string',
|
|
106
|
+
rules: [{ name: 'length', args: { limit: 18 } }],
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const rules = ds.getCollection('posts')!.getField('title')!.getComponentProps().rules;
|
|
113
|
+
|
|
114
|
+
await expect(rules[0].validator({}, '123')).rejects.toBe('单行文本 长度必须为 18 个字符');
|
|
115
|
+
});
|
|
116
|
+
|
|
83
117
|
it('ensureLoaded, reload and data source events work for main loader', async () => {
|
|
84
118
|
const { m, engine } = makeManager();
|
|
85
119
|
const loadedListener = vi.fn();
|
package/src/data-source/index.ts
CHANGED
|
@@ -295,6 +295,29 @@ export class DataSourceManager {
|
|
|
295
295
|
}
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
+
export type CollectionFieldInterfaceDataSourceManager = Pick<DataSourceManager, 'collectionFieldInterfaceManager'>;
|
|
299
|
+
|
|
300
|
+
export function getCollectionFieldInterface(
|
|
301
|
+
interfaceName: string | undefined,
|
|
302
|
+
...dataSourceManagers: Array<CollectionFieldInterfaceDataSourceManager | null | undefined>
|
|
303
|
+
) {
|
|
304
|
+
if (!interfaceName) {
|
|
305
|
+
return undefined;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// TODO: Once legacy client is removed and all runtimes share the client-v2 flow-engine
|
|
309
|
+
// DataSourceManager, callers should only pass the flow-engine context DataSourceManager.
|
|
310
|
+
for (const dataSourceManager of dataSourceManagers) {
|
|
311
|
+
const collectionFieldInterfaceManager = dataSourceManager?.collectionFieldInterfaceManager;
|
|
312
|
+
const getFieldInterface = collectionFieldInterfaceManager?.getFieldInterface;
|
|
313
|
+
if (typeof getFieldInterface === 'function') {
|
|
314
|
+
return getFieldInterface.call(collectionFieldInterfaceManager, interfaceName);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return undefined;
|
|
319
|
+
}
|
|
320
|
+
|
|
298
321
|
export class DataSource {
|
|
299
322
|
dataSourceManager: DataSourceManager;
|
|
300
323
|
collectionManager: CollectionManager;
|
|
@@ -1112,7 +1135,21 @@ export class CollectionField {
|
|
|
1112
1135
|
});
|
|
1113
1136
|
|
|
1114
1137
|
if (error) {
|
|
1115
|
-
const message = error.details
|
|
1138
|
+
const message = error.details
|
|
1139
|
+
.map((d: any) => {
|
|
1140
|
+
const translated = this.flowEngine.translate(d.type, {
|
|
1141
|
+
...d.context,
|
|
1142
|
+
ns: 'data-source-main',
|
|
1143
|
+
label,
|
|
1144
|
+
});
|
|
1145
|
+
|
|
1146
|
+
if (translated && translated !== d.type) {
|
|
1147
|
+
return translated;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
return d.message.replace(/"value"/g, `"${label}"`);
|
|
1151
|
+
})
|
|
1152
|
+
.join(', ');
|
|
1116
1153
|
return Promise.reject(message);
|
|
1117
1154
|
}
|
|
1118
1155
|
|
|
@@ -1135,8 +1172,13 @@ export class CollectionField {
|
|
|
1135
1172
|
}
|
|
1136
1173
|
|
|
1137
1174
|
getInterfaceOptions() {
|
|
1138
|
-
const
|
|
1139
|
-
return
|
|
1175
|
+
const ctx = this.flowEngine.context;
|
|
1176
|
+
return getCollectionFieldInterface(
|
|
1177
|
+
this.interface,
|
|
1178
|
+
this.collection?.dataSource?.dataSourceManager,
|
|
1179
|
+
ctx.dataSourceManager,
|
|
1180
|
+
ctx.app?.dataSourceManager,
|
|
1181
|
+
);
|
|
1140
1182
|
}
|
|
1141
1183
|
|
|
1142
1184
|
getFilterOperators() {
|