@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,88 @@
|
|
|
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
|
+
@injectable()
|
|
9
|
+
export class FormPathService {
|
|
10
|
+
static readonly ROOT = '/';
|
|
11
|
+
|
|
12
|
+
static readonly DIVIDER = '/';
|
|
13
|
+
|
|
14
|
+
static readonly RELATIVE_PARENT = '..';
|
|
15
|
+
|
|
16
|
+
static readonly RELATIVE_CURRENT = '.';
|
|
17
|
+
|
|
18
|
+
static readonly ARRAY = '[]';
|
|
19
|
+
|
|
20
|
+
static normalize(path: string) {
|
|
21
|
+
if (path === FormPathService.ROOT) {
|
|
22
|
+
return path;
|
|
23
|
+
}
|
|
24
|
+
// 去掉末尾的斜杠
|
|
25
|
+
if (path.endsWith(FormPathService.DIVIDER)) {
|
|
26
|
+
path = path.slice(0, -1);
|
|
27
|
+
}
|
|
28
|
+
return path;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static join(paths: string[]): string {
|
|
32
|
+
if (paths[1].startsWith(FormPathService.ROOT)) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`FormPathService Error: join failed, invalid paths[1], paths[1]= ${paths[1]}`,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
if (paths[0].endsWith(FormPathService.DIVIDER)) {
|
|
38
|
+
return `${paths[0]}${paths[1]}`;
|
|
39
|
+
}
|
|
40
|
+
return paths.join(FormPathService.DIVIDER);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static toArrayPath(path: string): string {
|
|
44
|
+
return FormPathService.join([path, FormPathService.ARRAY]);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
static parseArrayItemPath(path: string) {
|
|
48
|
+
const names = path.split('/');
|
|
49
|
+
|
|
50
|
+
let i = 0;
|
|
51
|
+
while (i < names.length) {
|
|
52
|
+
const itemIndex = parseInt(names[i]);
|
|
53
|
+
|
|
54
|
+
if (!isNaN(itemIndex)) {
|
|
55
|
+
const arrayPath = FormPathService.toArrayPath(
|
|
56
|
+
names.slice(0, i).join(FormPathService.DIVIDER),
|
|
57
|
+
);
|
|
58
|
+
const restPath = names.slice(i + 1).join(FormPathService.DIVIDER);
|
|
59
|
+
const itemMetaPath = FormPathService.join([arrayPath, restPath]);
|
|
60
|
+
return { itemIndex, arrayPath, itemMetaPath };
|
|
61
|
+
}
|
|
62
|
+
i = i + 1;
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
simplify(path: string) {
|
|
68
|
+
const segments = path.split(FormPathService.DIVIDER);
|
|
69
|
+
const resSegments: string[] = [];
|
|
70
|
+
|
|
71
|
+
for (let i = 0; i < segments.length; i++) {
|
|
72
|
+
if (!segments[i]) {
|
|
73
|
+
throw new Error('FormPathService: join failed');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (segments[i] === FormPathService.RELATIVE_CURRENT) {
|
|
77
|
+
// eslint-disable-next-line no-continue
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (segments[i] === FormPathService.RELATIVE_PARENT) {
|
|
82
|
+
resSegments.pop();
|
|
83
|
+
}
|
|
84
|
+
resSegments.push(segments[i]);
|
|
85
|
+
}
|
|
86
|
+
return resSegments.join(FormPathService.DIVIDER);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { FlowNodeEntity } from '@flowgram-vue/document';
|
|
7
|
+
import { PlaygroundContext } from '@flowgram-vue/core';
|
|
8
|
+
import { MaybePromise } from '@flowgram-vue/utils';
|
|
9
|
+
|
|
10
|
+
import { type IFormItem } from './form-model.types';
|
|
11
|
+
import { IFormItemMeta } from './form-meta.types';
|
|
12
|
+
|
|
13
|
+
export interface FormItemAbilityMeta<Options = any> {
|
|
14
|
+
type: string;
|
|
15
|
+
options: Options;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @deprecated
|
|
20
|
+
*/
|
|
21
|
+
export interface FormItemContext {
|
|
22
|
+
/**
|
|
23
|
+
* @deprecated Use context.node instead
|
|
24
|
+
*/
|
|
25
|
+
formItemMeta: IFormItemMeta;
|
|
26
|
+
/**
|
|
27
|
+
* @deprecated
|
|
28
|
+
*/
|
|
29
|
+
formItem: IFormItem;
|
|
30
|
+
/**
|
|
31
|
+
* @deprecated Use context.node instead
|
|
32
|
+
*/
|
|
33
|
+
flowNodeEntity: FlowNodeEntity;
|
|
34
|
+
/**
|
|
35
|
+
* @deprecated Use context.playgroundContext instead
|
|
36
|
+
*/
|
|
37
|
+
playgroundContext: PlaygroundContext;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface FormItemHookParams extends FormItemContext {
|
|
41
|
+
formItem: IFormItem;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface FormItemHooks<T> {
|
|
45
|
+
/**
|
|
46
|
+
* FormItem初始化钩子
|
|
47
|
+
*/
|
|
48
|
+
onInit?: (params: FormItemHookParams & T) => void;
|
|
49
|
+
/**
|
|
50
|
+
* FormItem提交时钩子
|
|
51
|
+
*/
|
|
52
|
+
onSubmit?: (params: FormItemHookParams & T) => void;
|
|
53
|
+
/**
|
|
54
|
+
* FormItem克隆时钩子
|
|
55
|
+
*/
|
|
56
|
+
onClone?: (params: FormItemHookParams & T) => MaybePromise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* 克隆后执行的逻辑
|
|
59
|
+
*/
|
|
60
|
+
afterClone?: (params: FormItemHookParams & T) => void;
|
|
61
|
+
/**
|
|
62
|
+
* FormItem全局校验时钩子
|
|
63
|
+
*/
|
|
64
|
+
onValidate?: (params: FormItemHookParams & T) => void;
|
|
65
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { MaybePromise } from '@flowgram-vue/utils';
|
|
7
|
+
import { FlowNodeEntity } from '@flowgram-vue/document';
|
|
8
|
+
import { PlaygroundContext, PluginContext } from '@flowgram-vue/core';
|
|
9
|
+
|
|
10
|
+
import { type FormItemAbilityMeta } from './form-ability.types';
|
|
11
|
+
|
|
12
|
+
export type FormDataTypeName =
|
|
13
|
+
| 'string'
|
|
14
|
+
| 'number'
|
|
15
|
+
| 'integer'
|
|
16
|
+
| 'boolean'
|
|
17
|
+
| 'object'
|
|
18
|
+
| 'array'
|
|
19
|
+
| 'null';
|
|
20
|
+
|
|
21
|
+
export type FormDataType =
|
|
22
|
+
| string //
|
|
23
|
+
| number
|
|
24
|
+
| boolean
|
|
25
|
+
| FormDataObject
|
|
26
|
+
| DataArray
|
|
27
|
+
| null;
|
|
28
|
+
|
|
29
|
+
export interface FormDataObject {
|
|
30
|
+
[key: string]: FormDataType;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type DataArray = Array<FormDataType>;
|
|
34
|
+
|
|
35
|
+
export const FORM_VOID = 'form-void' as const;
|
|
36
|
+
|
|
37
|
+
export interface TreeNode<T> {
|
|
38
|
+
name: string;
|
|
39
|
+
children?: TreeNode<T>[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface IFormItemMeta extends TreeNode<IFormItemMeta> {
|
|
43
|
+
/**
|
|
44
|
+
* 表单项名称
|
|
45
|
+
*/
|
|
46
|
+
name: string;
|
|
47
|
+
/**
|
|
48
|
+
* 数据类型
|
|
49
|
+
*/
|
|
50
|
+
type: FormDataTypeName | typeof FORM_VOID;
|
|
51
|
+
/**
|
|
52
|
+
* 枚举值
|
|
53
|
+
*/
|
|
54
|
+
enum?: FormDataType[];
|
|
55
|
+
/**
|
|
56
|
+
* 数组类型item的数据类型描述
|
|
57
|
+
*/
|
|
58
|
+
items?: IFormItemMeta;
|
|
59
|
+
/**
|
|
60
|
+
* 表单项标题
|
|
61
|
+
*/
|
|
62
|
+
title?: string;
|
|
63
|
+
/**
|
|
64
|
+
* 表单项描述
|
|
65
|
+
*/
|
|
66
|
+
description?: string;
|
|
67
|
+
/**
|
|
68
|
+
* 表单项默认值
|
|
69
|
+
*/
|
|
70
|
+
default?: FormDataType;
|
|
71
|
+
/**
|
|
72
|
+
* 是否必填
|
|
73
|
+
*/
|
|
74
|
+
required?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* 扩展能力
|
|
77
|
+
*/
|
|
78
|
+
abilities?: FormItemAbilityMeta[];
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 子表单项
|
|
82
|
+
*/
|
|
83
|
+
children?: IFormItemMeta[];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface IFormMeta {
|
|
87
|
+
/**
|
|
88
|
+
* 表单树结构root
|
|
89
|
+
*/
|
|
90
|
+
root?: IFormItemMeta;
|
|
91
|
+
/**
|
|
92
|
+
* 表单全局配置
|
|
93
|
+
*/
|
|
94
|
+
options?: IFormMetaOptions;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface NodeFormContext {
|
|
98
|
+
node: FlowNodeEntity;
|
|
99
|
+
playgroundContext: PlaygroundContext;
|
|
100
|
+
clientContext: PluginContext & Record<string, any>;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface IFormMetaOptions {
|
|
104
|
+
formatOnInit?: (value: any, context: NodeFormContext) => any;
|
|
105
|
+
|
|
106
|
+
formatOnSubmit?: (value: any, context: NodeFormContext) => any;
|
|
107
|
+
|
|
108
|
+
[key: string]: any;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface FormMetaGeneratorParams<PlaygroundContext, FormValue = any> {
|
|
112
|
+
node: FlowNodeEntity;
|
|
113
|
+
playgroundContext: PlaygroundContext;
|
|
114
|
+
initialValue?: FormValue;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export type FormMetaGenerator<PlaygroundContext = any, FormValue = any> = (
|
|
118
|
+
params: FormMetaGeneratorParams<FormValue, FormValue>
|
|
119
|
+
) => MaybePromise<IFormMeta>;
|
|
120
|
+
|
|
121
|
+
export type FormMetaOrFormMetaGenerator = FormMetaGenerator | IFormMeta;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface IFormItem<T = any> {
|
|
7
|
+
value: T;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export enum FormItemEventName {
|
|
11
|
+
onFormValueChange = 'onFormValueChange',
|
|
12
|
+
onFormItemInit = 'onFormItemInit',
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type FormModelValid = boolean | null;
|
|
16
|
+
|
|
17
|
+
export type FeedbackStatus = 'error' | 'warning' | 'pending';
|
|
18
|
+
export type FeedbackText = string;
|
|
19
|
+
|
|
20
|
+
export interface FormItemFeedback {
|
|
21
|
+
feedbackStatus?: FeedbackStatus;
|
|
22
|
+
feedbackText?: FeedbackText;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface FormFeedback {
|
|
26
|
+
feedbackStatus?: FeedbackStatus;
|
|
27
|
+
feedbackText?: FeedbackText;
|
|
28
|
+
path: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface FormItemDomRef {
|
|
32
|
+
current: HTMLElement | null;
|
|
33
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { NodeContribution } from './node-contribution';
|
|
7
|
+
export * from './node-container-module';
|
|
8
|
+
export * from './node-manager';
|
|
9
|
+
export * from './types';
|
|
10
|
+
export * from './core-plugins';
|
|
11
|
+
export * from './core-materials';
|
|
12
|
+
export * from './node-engine';
|
|
13
|
+
export * from './node-engine-context';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ContainerModule } from 'inversify';
|
|
7
|
+
|
|
8
|
+
import { NodeManager } from './node-manager';
|
|
9
|
+
import { NodeEngineContext } from './node-engine-context';
|
|
10
|
+
import { NodeEngine } from './node-engine';
|
|
11
|
+
|
|
12
|
+
export const NodeContainerModule = new ContainerModule((bind, unbind, isBound, rebind) => {
|
|
13
|
+
bind(NodeEngine).toSelf().inSingletonScope();
|
|
14
|
+
bind(NodeManager).toSelf().inSingletonScope();
|
|
15
|
+
bind(NodeEngineContext).toSelf().inSingletonScope();
|
|
16
|
+
});
|
|
@@ -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 NodeManager } from './node-manager';
|
|
7
|
+
|
|
8
|
+
export const NodeContribution = Symbol('NodeContribution');
|
|
9
|
+
|
|
10
|
+
export interface NodeContribution {
|
|
11
|
+
onRegister?(nodeManager: NodeManager): void;
|
|
12
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
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 { Emitter } from '@flowgram-vue/utils';
|
|
8
|
+
|
|
9
|
+
export interface INodeEngineContext {
|
|
10
|
+
readonly: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* NodeEngineContext 在 Node Engine 中为全局单例, 它的作用是让Node之间共享数据。
|
|
15
|
+
* context 分为内置context(如 readonly) 和 自定义context(业务可以按需注入)
|
|
16
|
+
*/
|
|
17
|
+
@injectable()
|
|
18
|
+
export class NodeEngineContext {
|
|
19
|
+
static DEFAULT_READONLY = false;
|
|
20
|
+
|
|
21
|
+
static DEFAULT_JSON = { readonly: NodeEngineContext.DEFAULT_READONLY };
|
|
22
|
+
|
|
23
|
+
readonly onChangeEmitter = new Emitter<NodeEngineContext>();
|
|
24
|
+
|
|
25
|
+
readonly onChange = this.onChangeEmitter.event;
|
|
26
|
+
|
|
27
|
+
private _readonly: boolean = NodeEngineContext.DEFAULT_READONLY;
|
|
28
|
+
|
|
29
|
+
private _json: INodeEngineContext = NodeEngineContext.DEFAULT_JSON;
|
|
30
|
+
|
|
31
|
+
get json(): INodeEngineContext {
|
|
32
|
+
return this._json;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get readonly(): boolean {
|
|
36
|
+
return this._readonly;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
set readonly(value: boolean) {
|
|
40
|
+
this._readonly = value;
|
|
41
|
+
this.fireChange();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private fireChange(): void {
|
|
45
|
+
this.updateJSON();
|
|
46
|
+
this.onChangeEmitter.fire(this);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private updateJSON(): void {
|
|
50
|
+
this._json = {
|
|
51
|
+
readonly: this._readonly,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -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, inject } from 'inversify';
|
|
7
|
+
|
|
8
|
+
import { NodeManager } from './node-manager';
|
|
9
|
+
import { NodeEngineContext } from './node-engine-context';
|
|
10
|
+
|
|
11
|
+
@injectable()
|
|
12
|
+
export class NodeEngine {
|
|
13
|
+
@inject(NodeManager) nodeManager: NodeManager;
|
|
14
|
+
|
|
15
|
+
@inject(NodeEngineContext) context: NodeEngineContext;
|
|
16
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { flow } from 'lodash-es';
|
|
7
|
+
import { injectable, multiInject, optional, postConstruct } from 'inversify';
|
|
8
|
+
|
|
9
|
+
import { NodeErrorRenderProps } from '../error';
|
|
10
|
+
import { NodePluginRender, NodeRenderHoc, Render } from './types';
|
|
11
|
+
import { NodeContribution } from './node-contribution';
|
|
12
|
+
|
|
13
|
+
export enum MaterialRenderKey {
|
|
14
|
+
CustomNodeError = 'Material_CustomNodeError',
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@injectable()
|
|
18
|
+
export class NodeManager {
|
|
19
|
+
readonly materialRenderRegistry: Map<string, Render> = new Map();
|
|
20
|
+
|
|
21
|
+
readonly pluginRenderRegistry: Map<string, Render> = new Map();
|
|
22
|
+
|
|
23
|
+
readonly nodeRenderHocs: NodeRenderHoc[] = [];
|
|
24
|
+
|
|
25
|
+
@multiInject(NodeContribution) @optional() protected nodeContributions: NodeContribution[] = [];
|
|
26
|
+
|
|
27
|
+
registerMaterialRender(key: string, render: Render) {
|
|
28
|
+
this.materialRenderRegistry.set(key, render);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
getMaterialRender(key: string): Render | undefined {
|
|
32
|
+
return this.materialRenderRegistry.get(key);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
registerPluginRender(key: string, render: NodePluginRender): void {
|
|
36
|
+
this.pluginRenderRegistry.set(key, render);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getPluginRender(key: string): NodePluginRender | undefined {
|
|
40
|
+
return this.pluginRenderRegistry.get(key);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
registerNodeErrorRender(render: Render<NodeErrorRenderProps>) {
|
|
44
|
+
this.registerMaterialRender(MaterialRenderKey.CustomNodeError, render);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get nodeRenderHoc() {
|
|
48
|
+
return flow(this.nodeRenderHocs);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
registerNodeRenderHoc(hoc: NodeRenderHoc) {
|
|
52
|
+
this.nodeRenderHocs.push(hoc);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get nodeErrorRender() {
|
|
56
|
+
return this.materialRenderRegistry.get(MaterialRenderKey.CustomNodeError);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@postConstruct()
|
|
60
|
+
protected init(): void {
|
|
61
|
+
this.nodeContributions.forEach((contrib) => contrib.onRegister?.(this));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Component } from 'vue';
|
|
7
|
+
|
|
8
|
+
import { FlowNodeEntity } from '@flowgram-vue/document';
|
|
9
|
+
|
|
10
|
+
import type { NodeFormContext } from '../form/types/form-meta.types';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @deprecated
|
|
14
|
+
* use `NodeFormContext` instead
|
|
15
|
+
*/
|
|
16
|
+
export type NodeContext = NodeFormContext;
|
|
17
|
+
|
|
18
|
+
export type Render<T = any> = (props: T) => any;
|
|
19
|
+
|
|
20
|
+
export type NodePluginRender = Render<NodeFormContext>;
|
|
21
|
+
|
|
22
|
+
export type NodePlaceholderRender = Render<NodeFormContext>;
|
|
23
|
+
|
|
24
|
+
export interface NodeRenderProps {
|
|
25
|
+
node: FlowNodeEntity;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type NodeRenderHoc = (
|
|
29
|
+
Component: Component<NodeRenderProps> | (() => any)
|
|
30
|
+
) => Component<NodeRenderProps> | (() => any);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { onBeforeUnmount } from 'vue';
|
|
7
|
+
|
|
8
|
+
import { FlowNodeEntity } from '@flowgram-vue/document';
|
|
9
|
+
import { useEntityFromContext, useRefresh } from '@flowgram-vue/core';
|
|
10
|
+
|
|
11
|
+
import { FlowNodeFormData, FormModel, IFormItem } from '../../form';
|
|
12
|
+
|
|
13
|
+
export function useFormItem(path: string): IFormItem | undefined {
|
|
14
|
+
const refresh = useRefresh();
|
|
15
|
+
const node = useEntityFromContext<FlowNodeEntity>();
|
|
16
|
+
const formData = node.getData<FlowNodeFormData>(FlowNodeFormData);
|
|
17
|
+
const formItem = formData.getFormModel<FormModel>().getFormItemByPath(path);
|
|
18
|
+
|
|
19
|
+
const disposable = formData.onDataChange(() => {
|
|
20
|
+
refresh();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
onBeforeUnmount(() => {
|
|
24
|
+
disposable.dispose();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
return formItem;
|
|
28
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { onBeforeUnmount } from 'vue';
|
|
7
|
+
|
|
8
|
+
import { useService, useRefresh } from '@flowgram-vue/core';
|
|
9
|
+
|
|
10
|
+
import { NodeEngineContext } from '../../node';
|
|
11
|
+
|
|
12
|
+
export function useNodeEngineContext(): NodeEngineContext {
|
|
13
|
+
const refresh = useRefresh();
|
|
14
|
+
const nodeEngineContext = useService<NodeEngineContext>(NodeEngineContext);
|
|
15
|
+
|
|
16
|
+
const disposable = nodeEngineContext.onChange(() => {
|
|
17
|
+
refresh();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
onBeforeUnmount(() => {
|
|
21
|
+
disposable.dispose();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return nodeEngineContext;
|
|
25
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { InjectionKey } from 'vue';
|
|
7
|
+
import { inject, provide } from 'vue';
|
|
8
|
+
|
|
9
|
+
import { INodeEngineContext, NodeEngineContext } from '../../node';
|
|
10
|
+
|
|
11
|
+
export const NodeEngineVueContextKey: InjectionKey<INodeEngineContext> = Symbol(
|
|
12
|
+
'NodeEngineVueContext'
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
export function provideNodeEngineContext(ctx: INodeEngineContext): void {
|
|
16
|
+
provide(NodeEngineVueContextKey, ctx);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function useNodeEngineVueContext(): INodeEngineContext {
|
|
20
|
+
return inject(NodeEngineVueContextKey, NodeEngineContext.DEFAULT_JSON);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** @deprecated use NodeEngineVueContextKey */
|
|
24
|
+
export const NodeEngineVueContext = NodeEngineVueContextKey;
|