@flowgram-vue/form-core 0.2.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.
- package/LICENSE +22 -0
- package/dist/index.cjs +0 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +0 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -0
- package/src/client/NodeRender.vue +76 -0
- package/src/client/create-node-container-modules.ts +12 -0
- package/src/client/create-node-entity-datas.ts +13 -0
- package/src/client/index.ts +11 -0
- package/src/client/node-material-client.ts +18 -0
- package/src/client/node-render.ts +6 -0
- package/src/env.d.ts +10 -0
- package/src/error/client.ts +12 -0
- package/src/error/error-container-module.ts +14 -0
- package/src/error/error-node-contribution.ts +17 -0
- package/src/error/flow-node-error-data.ts +26 -0
- package/src/error/index.ts +9 -0
- package/src/error/renders/ErrorRender.vue +61 -0
- package/src/error/renders/default-error-render.ts +15 -0
- package/src/error/renders/error-render.ts +19 -0
- package/src/error/renders/index.ts +6 -0
- package/src/error/types.ts +13 -0
- package/src/form/FormRender.vue +39 -0
- package/src/form/abilities/decorator-ability/decorator-ability.ts +14 -0
- package/src/form/abilities/decorator-ability/index.ts +7 -0
- package/src/form/abilities/decorator-ability/types.ts +28 -0
- package/src/form/abilities/default-ability/default-ability.ts +14 -0
- package/src/form/abilities/default-ability/index.ts +7 -0
- package/src/form/abilities/default-ability/types.ts +15 -0
- package/src/form/abilities/effect-ability/effect-ability.ts +14 -0
- package/src/form/abilities/effect-ability/index.ts +7 -0
- package/src/form/abilities/effect-ability/types.ts +41 -0
- package/src/form/abilities/index.ts +11 -0
- package/src/form/abilities/setter-ability/index.ts +7 -0
- package/src/form/abilities/setter-ability/setter-ability.ts +14 -0
- package/src/form/abilities/setter-ability/types.ts +52 -0
- package/src/form/abilities/validation-ability/form-validate.types.ts +74 -0
- package/src/form/abilities/validation-ability/index.ts +7 -0
- package/src/form/abilities/validation-ability/types.ts +44 -0
- package/src/form/abilities/validation-ability/validation-ability.ts +14 -0
- package/src/form/abilities/visibility-ability/index.ts +6 -0
- package/src/form/abilities/visibility-ability/visibility-ability.ts +25 -0
- package/src/form/client/index.ts +17 -0
- package/src/form/flow-node-form-data.ts +109 -0
- package/src/form/form-contribution.ts +12 -0
- package/src/form/form-core-container-module.ts +19 -0
- package/src/form/form-node-contribution.ts +16 -0
- package/src/form/form-render.ts +13 -0
- package/src/form/index.ts +13 -0
- package/src/form/models/form-ability-extension-registry.ts +30 -0
- package/src/form/models/form-item-ability.ts +18 -0
- package/src/form/models/form-item-material-context.ts +50 -0
- package/src/form/models/form-item.ts +53 -0
- package/src/form/models/form-meta.ts +62 -0
- package/src/form/models/form-model.ts +84 -0
- package/src/form/models/index.ts +11 -0
- package/src/form/services/form-context-maker.ts +35 -0
- package/src/form/services/form-manager.ts +113 -0
- package/src/form/services/form-path-service.ts +88 -0
- package/src/form/services/index.ts +8 -0
- package/src/form/types/form-ability.types.ts +65 -0
- package/src/form/types/form-meta.types.ts +121 -0
- package/src/form/types/form-model.types.ts +33 -0
- package/src/form/types/index.ts +8 -0
- package/src/index.ts +10 -0
- package/src/node/core-materials.ts +9 -0
- package/src/node/core-plugins.ts +9 -0
- package/src/node/index.ts +13 -0
- package/src/node/node-container-module.ts +16 -0
- package/src/node/node-contribution.ts +12 -0
- package/src/node/node-engine-context.ts +54 -0
- package/src/node/node-engine.ts +16 -0
- package/src/node/node-manager.ts +63 -0
- package/src/node/types.ts +30 -0
- package/src/node-vue/composables/index.ts +7 -0
- package/src/node-vue/composables/use-form-item.ts +28 -0
- package/src/node-vue/composables/use-node-engine-context.ts +25 -0
- package/src/node-vue/context/index.ts +6 -0
- package/src/node-vue/context/node-engine-vue-context.ts +24 -0
- package/src/node-vue/hooks/index.ts +6 -0
- package/src/node-vue/index.ts +7 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Disposable, Emitter } from '@flowgram-vue/utils';
|
|
7
|
+
import { FlowNodeEntity } from '@flowgram-vue/document';
|
|
8
|
+
import { EntityData } from '@flowgram-vue/core';
|
|
9
|
+
|
|
10
|
+
import { FlowNodeErrorData } from '../error';
|
|
11
|
+
import { FormMetaOrFormMetaGenerator } from './types';
|
|
12
|
+
import { FormModel, type FormModelFactory } from './models';
|
|
13
|
+
|
|
14
|
+
interface Options {
|
|
15
|
+
formModelFactory: FormModelFactory;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface DetailChangeEvent {
|
|
19
|
+
path: string;
|
|
20
|
+
oldValue: any;
|
|
21
|
+
value: any;
|
|
22
|
+
initialized: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface OnFormValuesChangePayload {
|
|
26
|
+
values: any;
|
|
27
|
+
prevValues: any;
|
|
28
|
+
name: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class FlowNodeFormData extends EntityData {
|
|
32
|
+
static type = 'FlowNodeEntityFormData';
|
|
33
|
+
|
|
34
|
+
readonly formModel: FormModel;
|
|
35
|
+
|
|
36
|
+
protected flowNodeEntity: FlowNodeEntity;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @deprecated rehaje 版表单form Values change 事件
|
|
40
|
+
* @protected
|
|
41
|
+
*/
|
|
42
|
+
protected onDetailChangeEmitter = new Emitter<DetailChangeEvent>();
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @deprecated 该方法为旧版引擎(rehaje)表单数据变更事件, 新版节点引擎请使用
|
|
46
|
+
* this.getFormModel<FormModelV2>().onFormValuesChange.
|
|
47
|
+
* @protected
|
|
48
|
+
*/
|
|
49
|
+
readonly onDetailChange = this.onDetailChangeEmitter.event;
|
|
50
|
+
|
|
51
|
+
constructor(entity: FlowNodeEntity, opts: Options) {
|
|
52
|
+
super(entity);
|
|
53
|
+
|
|
54
|
+
this.flowNodeEntity = entity;
|
|
55
|
+
this.formModel = opts.formModelFactory(entity);
|
|
56
|
+
|
|
57
|
+
this.toDispose.push(this.onDetailChangeEmitter);
|
|
58
|
+
|
|
59
|
+
this.toDispose.push(
|
|
60
|
+
Disposable.create(() => {
|
|
61
|
+
this.formModel.dispose();
|
|
62
|
+
})
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
getFormModel<TFormModel>(): TFormModel {
|
|
67
|
+
// @ts-ignore
|
|
68
|
+
return this.formModel as TFormModel;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
getDefaultData(): any {
|
|
72
|
+
return {};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
createForm(formMetaOrFormMetaGenerator: any, initialValue?: any): void {
|
|
76
|
+
const errorData = this.flowNodeEntity.getData<FlowNodeErrorData>(FlowNodeErrorData);
|
|
77
|
+
|
|
78
|
+
errorData.setError(null);
|
|
79
|
+
try {
|
|
80
|
+
this.formModel.init(formMetaOrFormMetaGenerator, initialValue);
|
|
81
|
+
} catch (e) {
|
|
82
|
+
errorData.setError(e as Error);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
updateFormValues(value: any) {
|
|
87
|
+
this.formModel.updateFormValues(value);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
recreateForm(formMetaOrFormMetaGenerator: FormMetaOrFormMetaGenerator, initialValue?: any): void {
|
|
91
|
+
this.createForm(formMetaOrFormMetaGenerator, initialValue);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
toJSON(): any {
|
|
95
|
+
return this.formModel.toJSON();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
dispose(): void {
|
|
99
|
+
super.dispose();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @deprecated rehaje 版表单form Values change 事件触发函数
|
|
104
|
+
* @protected
|
|
105
|
+
*/
|
|
106
|
+
fireDetaiChange(detailChangeEvent: DetailChangeEvent) {
|
|
107
|
+
this.onDetailChangeEmitter.fire(detailChangeEvent);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { type FormManager } from './services/form-manager';
|
|
7
|
+
|
|
8
|
+
export const FormContribution = Symbol('FormContribution');
|
|
9
|
+
|
|
10
|
+
export interface FormContribution {
|
|
11
|
+
onRegister?(formManager: FormManager): void;
|
|
12
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ContainerModule } from 'inversify';
|
|
7
|
+
import { bindContributions } from '@flowgram-vue/utils';
|
|
8
|
+
|
|
9
|
+
import { FormContextMaker } from './services/form-context-maker';
|
|
10
|
+
import { NodeContribution } from '../node';
|
|
11
|
+
import { FormManager, FormPathService } from './services';
|
|
12
|
+
import { FormNodeContribution } from './form-node-contribution';
|
|
13
|
+
|
|
14
|
+
export const FormCoreContainerModule = new ContainerModule((bind) => {
|
|
15
|
+
bind(FormManager).toSelf().inSingletonScope();
|
|
16
|
+
bind(FormPathService).toSelf().inSingletonScope();
|
|
17
|
+
bind(FormContextMaker).toSelf().inSingletonScope();
|
|
18
|
+
bindContributions(bind, FormNodeContribution, [NodeContribution]);
|
|
19
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { injectable } from 'inversify';
|
|
7
|
+
|
|
8
|
+
import { NodeContribution, NodeManager, PLUGIN_KEY } from '../node';
|
|
9
|
+
import { formPluginRender } from './form-render';
|
|
10
|
+
|
|
11
|
+
@injectable()
|
|
12
|
+
export class FormNodeContribution implements NodeContribution {
|
|
13
|
+
onRegister(nodeManager: NodeManager) {
|
|
14
|
+
nodeManager.registerPluginRender(PLUGIN_KEY.FORM, formPluginRender);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { h } from 'vue';
|
|
7
|
+
|
|
8
|
+
import { NodeContext } from '../node';
|
|
9
|
+
import FormRender from './FormRender.vue';
|
|
10
|
+
|
|
11
|
+
export { default as FormRender } from './FormRender.vue';
|
|
12
|
+
|
|
13
|
+
export const formPluginRender = (props: NodeContext) => h(FormRender, { node: props.node });
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export * from './form-core-container-module';
|
|
7
|
+
export * from './form-contribution';
|
|
8
|
+
export * from './services';
|
|
9
|
+
export * from './models';
|
|
10
|
+
export * from './types';
|
|
11
|
+
export * from './abilities';
|
|
12
|
+
export * from './flow-node-form-data';
|
|
13
|
+
export * from './client';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { injectable } from 'inversify';
|
|
7
|
+
|
|
8
|
+
export interface Extension {
|
|
9
|
+
key: string;
|
|
10
|
+
}
|
|
11
|
+
@injectable()
|
|
12
|
+
export class FormAbilityExtensionRegistry {
|
|
13
|
+
protected registry = new Map<string, Extension>();
|
|
14
|
+
|
|
15
|
+
register(extension: Extension): void {
|
|
16
|
+
this.registry.set(extension.key, extension);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get<T extends Extension>(key: string): T | undefined {
|
|
20
|
+
return this.registry.get(key) as T | undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get objectMap(): Record<string, Extension> {
|
|
24
|
+
return Object.fromEntries(this.registry);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get collection(): Extension[] {
|
|
28
|
+
return Array.from(this.registry.values());
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface FormItemAbility {
|
|
7
|
+
type: string;
|
|
8
|
+
/**
|
|
9
|
+
* 注册到formManager时钩子时调用
|
|
10
|
+
*/
|
|
11
|
+
onAbilityRegister?: () => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface AbilityClass {
|
|
15
|
+
type: string;
|
|
16
|
+
|
|
17
|
+
new (): FormItemAbility;
|
|
18
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { type FlowNodeEntity } from '@flowgram-vue/document';
|
|
7
|
+
import { PlaygroundContext } from '@flowgram-vue/core';
|
|
8
|
+
|
|
9
|
+
import { type IFormItemMeta } from '../types';
|
|
10
|
+
import { type FormModel } from './form-model';
|
|
11
|
+
|
|
12
|
+
export interface FormItemMaterialContext {
|
|
13
|
+
/**
|
|
14
|
+
* 当前表单项的meta
|
|
15
|
+
*/
|
|
16
|
+
meta: IFormItemMeta;
|
|
17
|
+
/**
|
|
18
|
+
* 当前表单项的路径
|
|
19
|
+
*/
|
|
20
|
+
path: string;
|
|
21
|
+
/**
|
|
22
|
+
* 节点引擎全局readonly
|
|
23
|
+
*/
|
|
24
|
+
readonly: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* 通过路径获取表单项的值
|
|
27
|
+
* @param path 表单项在当前表单中的绝对路径,路径协议遵循glob
|
|
28
|
+
*/
|
|
29
|
+
getFormItemValueByPath: <T>(path: string) => T;
|
|
30
|
+
/**
|
|
31
|
+
* 节点表单校验回调函数注册
|
|
32
|
+
*/
|
|
33
|
+
onFormValidate: FormModel['onValidate'];
|
|
34
|
+
/**
|
|
35
|
+
* 获取Node模型
|
|
36
|
+
*/
|
|
37
|
+
node: FlowNodeEntity;
|
|
38
|
+
/**
|
|
39
|
+
* 获取FormModel原始模型
|
|
40
|
+
*/
|
|
41
|
+
form: FormModel;
|
|
42
|
+
/**
|
|
43
|
+
* 业务注入的全局context
|
|
44
|
+
*/
|
|
45
|
+
playgroundContext: PlaygroundContext;
|
|
46
|
+
/**
|
|
47
|
+
* 数组场景下当前项的index
|
|
48
|
+
*/
|
|
49
|
+
index?: number | undefined;
|
|
50
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { DisposableCollection, Emitter } from '@flowgram-vue/utils';
|
|
7
|
+
|
|
8
|
+
import { type FormItemDomRef, type IFormItemMeta } from '../types';
|
|
9
|
+
import { type FormModel } from './form-model';
|
|
10
|
+
|
|
11
|
+
export abstract class FormItem {
|
|
12
|
+
readonly meta: IFormItemMeta;
|
|
13
|
+
|
|
14
|
+
readonly path: string;
|
|
15
|
+
|
|
16
|
+
readonly formModel: FormModel;
|
|
17
|
+
|
|
18
|
+
readonly onInitEventEmitter = new Emitter<FormItem>();
|
|
19
|
+
|
|
20
|
+
readonly onInit = this.onInitEventEmitter.event;
|
|
21
|
+
|
|
22
|
+
protected toDispose: DisposableCollection = new DisposableCollection();
|
|
23
|
+
|
|
24
|
+
readonly onDispose = this.toDispose.onDispose;
|
|
25
|
+
|
|
26
|
+
// todo(heyuan): 将dom 相关逻辑拆到form item插件里
|
|
27
|
+
private _domRef: FormItemDomRef;
|
|
28
|
+
|
|
29
|
+
protected constructor(meta: IFormItemMeta, path: string, formModel: FormModel) {
|
|
30
|
+
this.meta = meta;
|
|
31
|
+
this.path = path;
|
|
32
|
+
this.formModel = formModel;
|
|
33
|
+
this.toDispose.push(this.onInitEventEmitter);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
abstract get value(): any;
|
|
37
|
+
|
|
38
|
+
abstract set value(value: any);
|
|
39
|
+
|
|
40
|
+
abstract validate(): void;
|
|
41
|
+
|
|
42
|
+
set domRef(domRef: FormItemDomRef) {
|
|
43
|
+
this._domRef = domRef;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get domRef() {
|
|
47
|
+
return this._domRef;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
dispose(): void {
|
|
51
|
+
this.toDispose.dispose();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { type IFormItemMeta, type IFormMeta, type IFormMetaOptions } from '../types';
|
|
7
|
+
import { FormPathService } from '../services';
|
|
8
|
+
|
|
9
|
+
export interface FormMetaTraverseParams {
|
|
10
|
+
formItemMeta: IFormItemMeta;
|
|
11
|
+
parentPath?: string;
|
|
12
|
+
handle: (params: { formItemMeta: IFormItemMeta; path: string }) => any;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class FormMeta implements IFormMeta {
|
|
16
|
+
constructor(root: IFormItemMeta, options: IFormMetaOptions) {
|
|
17
|
+
this._root = root;
|
|
18
|
+
this._options = options;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
protected _root: IFormItemMeta;
|
|
22
|
+
|
|
23
|
+
get root(): IFormItemMeta {
|
|
24
|
+
return this._root;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
protected _options: IFormMetaOptions;
|
|
28
|
+
|
|
29
|
+
get options(): IFormMetaOptions {
|
|
30
|
+
return this._options;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static traverse({ formItemMeta, parentPath = '', handle }: FormMetaTraverseParams): void {
|
|
34
|
+
if (!formItemMeta) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const isRoot = !parentPath;
|
|
39
|
+
|
|
40
|
+
const path = isRoot
|
|
41
|
+
? FormPathService.ROOT
|
|
42
|
+
: formItemMeta.name
|
|
43
|
+
? FormPathService.join([parentPath, formItemMeta.name])
|
|
44
|
+
: parentPath;
|
|
45
|
+
|
|
46
|
+
handle({ formItemMeta, path });
|
|
47
|
+
|
|
48
|
+
if (formItemMeta.items) {
|
|
49
|
+
this.traverse({
|
|
50
|
+
formItemMeta: formItemMeta.items,
|
|
51
|
+
handle,
|
|
52
|
+
parentPath: FormPathService.toArrayPath(path),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (formItemMeta.children && formItemMeta.children.length) {
|
|
57
|
+
formItemMeta.children.forEach((child) => {
|
|
58
|
+
this.traverse({ formItemMeta: child, handle, parentPath: path });
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { injectable } from 'inversify';
|
|
7
|
+
import { DisposableCollection, Event, MaybePromise } from '@flowgram-vue/utils';
|
|
8
|
+
import { type FlowNodeEntity } from '@flowgram-vue/document';
|
|
9
|
+
|
|
10
|
+
import { FormFeedback, FormModelValid, IFormItem } from '../types';
|
|
11
|
+
import { type FormManager } from '../services/form-manager';
|
|
12
|
+
import { type FormItem } from './form-item';
|
|
13
|
+
|
|
14
|
+
export type FormModelFactory = (entity: FlowNodeEntity) => FormModel;
|
|
15
|
+
export const FormModelFactory = Symbol('FormModelFactory');
|
|
16
|
+
export const FormModelEntity = Symbol('FormModelEntity');
|
|
17
|
+
|
|
18
|
+
@injectable()
|
|
19
|
+
export abstract class FormModel {
|
|
20
|
+
readonly onValidate: Event<FormModel>;
|
|
21
|
+
|
|
22
|
+
readonly onValidChange: Event<FormModelValid>;
|
|
23
|
+
|
|
24
|
+
readonly onFeedbacksChange: Event<FormFeedback[]>;
|
|
25
|
+
|
|
26
|
+
readonly onInitialized: Event<FormModel>;
|
|
27
|
+
|
|
28
|
+
protected toDispose: DisposableCollection = new DisposableCollection();
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @deprecated
|
|
32
|
+
* use `formModel.node` instead in FormModelV2
|
|
33
|
+
*/
|
|
34
|
+
abstract get flowNodeEntity(): FlowNodeEntity;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @deprecated
|
|
38
|
+
*/
|
|
39
|
+
abstract get formManager(): FormManager;
|
|
40
|
+
|
|
41
|
+
abstract get formMeta(): any;
|
|
42
|
+
|
|
43
|
+
abstract get initialized(): boolean;
|
|
44
|
+
|
|
45
|
+
abstract get valid(): FormModelValid;
|
|
46
|
+
|
|
47
|
+
abstract updateFormValues(value: any): void;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @deprecated
|
|
51
|
+
* use `formModel.getFieldIn` instead in FormModelV2 to get the model of a form field
|
|
52
|
+
* do not use this in FormModelV2 since it only return an empty Map.
|
|
53
|
+
*/
|
|
54
|
+
abstract get formItemPathMap(): Map<string, IFormItem>;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @deprecated
|
|
58
|
+
*/
|
|
59
|
+
abstract clearValid(): void;
|
|
60
|
+
|
|
61
|
+
abstract validate(): Promise<boolean>;
|
|
62
|
+
|
|
63
|
+
abstract validateWithFeedbacks(): Promise<FormFeedback[]>;
|
|
64
|
+
|
|
65
|
+
abstract init(formMetaOrFormMetaGenerator: any, initialValue?: any): MaybePromise<void>;
|
|
66
|
+
|
|
67
|
+
abstract toJSON(): any;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @deprecated
|
|
71
|
+
* use `formModel.getField` instead in FormModelV2
|
|
72
|
+
*/
|
|
73
|
+
abstract getFormItemByPath(path: string): FormItem | undefined;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @deprecated
|
|
77
|
+
* use `formModel.getFieldValue` instead in FormModelV2 to get the model of a form field by path
|
|
78
|
+
*/
|
|
79
|
+
abstract getFormItemValueByPath<T = any>(path: string): any | undefined;
|
|
80
|
+
|
|
81
|
+
abstract render(): any;
|
|
82
|
+
|
|
83
|
+
abstract dispose(): void;
|
|
84
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export * from './form-model';
|
|
7
|
+
export * from './form-item';
|
|
8
|
+
export * from './form-meta';
|
|
9
|
+
export * from './form-ability-extension-registry';
|
|
10
|
+
export * from './form-item-ability';
|
|
11
|
+
export * from './form-item-material-context';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { inject, injectable } from 'inversify';
|
|
7
|
+
import { injectPlaygroundContext, PlaygroundContext } from '@flowgram-vue/core';
|
|
8
|
+
|
|
9
|
+
import { NodeEngineContext } from '../../node/node-engine-context';
|
|
10
|
+
import { FormItem } from '../models/form-item';
|
|
11
|
+
import { type FormItemMaterialContext } from '../models/form-item-material-context';
|
|
12
|
+
|
|
13
|
+
@injectable()
|
|
14
|
+
export class FormContextMaker {
|
|
15
|
+
@inject(NodeEngineContext) readonly nodeEngineContext: NodeEngineContext;
|
|
16
|
+
|
|
17
|
+
@injectPlaygroundContext() readonly playgroundContext: PlaygroundContext;
|
|
18
|
+
|
|
19
|
+
makeFormItemMaterialContext(
|
|
20
|
+
formItem: FormItem,
|
|
21
|
+
options?: { getIndex: () => number | undefined }
|
|
22
|
+
): FormItemMaterialContext {
|
|
23
|
+
return {
|
|
24
|
+
meta: formItem.meta,
|
|
25
|
+
path: formItem.path,
|
|
26
|
+
readonly: this.nodeEngineContext.readonly,
|
|
27
|
+
getFormItemValueByPath: formItem.formModel.getFormItemValueByPath.bind(formItem.formModel),
|
|
28
|
+
onFormValidate: formItem.formModel.onValidate.bind(formItem.formModel),
|
|
29
|
+
form: formItem.formModel,
|
|
30
|
+
node: formItem.formModel.flowNodeEntity,
|
|
31
|
+
playgroundContext: this.playgroundContext,
|
|
32
|
+
index: options?.getIndex(),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { mapValues } from 'lodash-es';
|
|
7
|
+
import { inject, injectable, multiInject, optional, postConstruct } from 'inversify';
|
|
8
|
+
import { Emitter } from '@flowgram-vue/utils';
|
|
9
|
+
import { injectPlaygroundContext, PlaygroundContext } from '@flowgram-vue/core';
|
|
10
|
+
|
|
11
|
+
import { AbilityClass, FormItemAbility } from '../models/form-item-ability';
|
|
12
|
+
import { FormAbilityExtensionRegistry, FormModel } from '../models';
|
|
13
|
+
import { FormContribution } from '../form-contribution';
|
|
14
|
+
import {
|
|
15
|
+
DecoratorAbility,
|
|
16
|
+
DecoratorExtension,
|
|
17
|
+
SetterAbility,
|
|
18
|
+
SetterExtension,
|
|
19
|
+
SetterHoc,
|
|
20
|
+
} from '../abilities';
|
|
21
|
+
import { FormContextMaker } from './form-context-maker';
|
|
22
|
+
import { FormPathService } from './form-path-service';
|
|
23
|
+
|
|
24
|
+
@injectable()
|
|
25
|
+
export class FormManager {
|
|
26
|
+
readonly abilityRegistry: Map<string, FormItemAbility> = new Map();
|
|
27
|
+
|
|
28
|
+
readonly setterHocs: SetterHoc[] = [];
|
|
29
|
+
|
|
30
|
+
readonly extensionRegistryMap: Map<string, FormAbilityExtensionRegistry> = new Map();
|
|
31
|
+
|
|
32
|
+
@inject(FormPathService) readonly pathManager: FormPathService;
|
|
33
|
+
|
|
34
|
+
@inject(FormContextMaker) readonly formContextMaker: FormContextMaker;
|
|
35
|
+
|
|
36
|
+
@injectPlaygroundContext() readonly playgroundContext: PlaygroundContext;
|
|
37
|
+
|
|
38
|
+
@multiInject(FormContribution) @optional() protected formContributions: FormContribution[] = [];
|
|
39
|
+
|
|
40
|
+
private readonly onFormModelWillInitEmitter = new Emitter<{
|
|
41
|
+
model: FormModel;
|
|
42
|
+
data: any;
|
|
43
|
+
}>();
|
|
44
|
+
|
|
45
|
+
readonly onFormModelWillInit = this.onFormModelWillInitEmitter.event;
|
|
46
|
+
|
|
47
|
+
get components(): Record<string, any> {
|
|
48
|
+
return mapValues(
|
|
49
|
+
this.extensionRegistryMap.get(SetterAbility.type)?.objectMap || {},
|
|
50
|
+
(setter: SetterExtension) => setter.component
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
get decorators(): Record<string, any> {
|
|
55
|
+
return mapValues(
|
|
56
|
+
this.extensionRegistryMap.get(DecoratorAbility.type)?.objectMap || {},
|
|
57
|
+
(decorator: DecoratorExtension) => decorator.component
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
registerAbilityExtension(type: string, extension: any): void {
|
|
62
|
+
if (!this.extensionRegistryMap.get(type)) {
|
|
63
|
+
this.extensionRegistryMap.set(type, new FormAbilityExtensionRegistry());
|
|
64
|
+
}
|
|
65
|
+
const registry = this.extensionRegistryMap.get(type);
|
|
66
|
+
if (!registry) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
registry.register(extension);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
getAbilityExtension(abilityType: string, extensionKey: string): any {
|
|
73
|
+
return this.extensionRegistryMap.get(abilityType)?.get(extensionKey);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
registerAbility(Ability: AbilityClass): void {
|
|
77
|
+
const ability = new Ability();
|
|
78
|
+
this.abilityRegistry.set(ability.type, ability);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
registerAbilities(Abilities: AbilityClass[]): void {
|
|
82
|
+
Abilities.forEach(this.registerAbility.bind(this));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
getAbility<ExtendAbility>(type: string): (FormItemAbility & ExtendAbility) | undefined {
|
|
86
|
+
return this.abilityRegistry.get(type) as FormItemAbility & ExtendAbility;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @deprecated
|
|
91
|
+
* Setter Hoc and setter are no longer supported in NodeEngineV2
|
|
92
|
+
* @param hoc
|
|
93
|
+
*/
|
|
94
|
+
registerSetterHoc(hoc: SetterHoc): void {
|
|
95
|
+
this.setterHocs.push(hoc);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
fireFormModelWillInit(model: FormModel, data: any) {
|
|
99
|
+
this.onFormModelWillInitEmitter.fire({
|
|
100
|
+
model,
|
|
101
|
+
data,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
dispose() {
|
|
106
|
+
this.onFormModelWillInitEmitter.dispose();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@postConstruct()
|
|
110
|
+
protected init(): void {
|
|
111
|
+
this.formContributions.forEach((contrib) => contrib.onRegister?.(this));
|
|
112
|
+
}
|
|
113
|
+
}
|