mirror of
https://gitee.com/ByteDance/flowgram.ai.git
synced 2025-07-07 17:43:29 +08:00
feat(demo-fixed-layout): add case node for condition branches (#302)
* refactor: FlowNodeRegistry.addChild depreacted * feat: FlowDocument.toString(true) support showType * feat(demo-fixed-layout): add case node and catch-block node
This commit is contained in:
parent
b477181502
commit
f0d9c5062c
@ -15,7 +15,7 @@ export function FlowSelect() {
|
||||
clientContext.history.stop(); // Stop redo/undo
|
||||
clientContext.history.clear(); // Clear redo/undo
|
||||
clientContext.document.fromJSON(targetDemoJSON); // Reload Data
|
||||
console.log(clientContext.document.toString()); // Print the document tree
|
||||
console.log(clientContext.document.toString(true)); // Print the document tree
|
||||
clientContext.history.start(); // Restart redo/undo
|
||||
clientContext.document.setLayout(
|
||||
targetDemoJSON.defaultLayout || FlowLayoutDefault.VERTICAL_FIXED_LAYOUT
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { type FlowNodeEntity, useClientContext } from '@flowgram.ai/fixed-layout-editor';
|
||||
import { IconPlus } from '@douyinfe/semi-icons';
|
||||
|
||||
import { BlockNodeRegistry } from '../../nodes/block';
|
||||
import { CatchBlockNodeRegistry } from '../../nodes/catch-block';
|
||||
import { CaseNodeRegistry } from '../../nodes/case';
|
||||
import { Container } from './styles';
|
||||
|
||||
interface PropsType {
|
||||
@ -17,7 +18,12 @@ export default function BranchAdder(props: PropsType) {
|
||||
const { isVertical } = node;
|
||||
|
||||
function addBranch() {
|
||||
const block = operation.addBlock(node, BlockNodeRegistry.onAdd!(ctx, node));
|
||||
const block = operation.addBlock(
|
||||
node,
|
||||
node.flowNodeType === 'condition'
|
||||
? CaseNodeRegistry.onAdd!(ctx, node)
|
||||
: CatchBlockNodeRegistry.onAdd!(ctx, node)
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
playground.scrollToView({
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { debounce } from 'lodash-es';
|
||||
import { createMinimapPlugin } from '@flowgram.ai/minimap-plugin';
|
||||
import { createGroupPlugin } from '@flowgram.ai/group-plugin';
|
||||
import { defaultFixedSemiMaterials } from '@flowgram.ai/fixed-semi-materials';
|
||||
@ -139,10 +140,10 @@ export function useEditorProps(
|
||||
history: {
|
||||
enable: true,
|
||||
enableChangeNode: true, // Listen Node engine data change
|
||||
onApply(ctx, opt) {
|
||||
onApply: debounce((ctx, opt) => {
|
||||
// Listen change to trigger auto save
|
||||
// console.log('auto save: ', ctx.document.toJSON(), opt);
|
||||
},
|
||||
console.log('auto save: ', ctx.document.toJSON());
|
||||
}, 100),
|
||||
},
|
||||
/**
|
||||
* Node engine enable, you can configure formMeta in the FlowNodeRegistry
|
||||
@ -201,7 +202,7 @@ export function useEditorProps(
|
||||
setTimeout(() => {
|
||||
ctx.playground.config.fitView(ctx.document.root.bounds.pad(30));
|
||||
}, 10);
|
||||
console.log(ctx.document.toString()); // Get the document tree
|
||||
console.log(ctx.document.toString(true)); // Get the document tree
|
||||
},
|
||||
/**
|
||||
* Playground dispose
|
||||
|
||||
@ -106,8 +106,8 @@ export const initialData: FlowDocumentJSON = {
|
||||
},
|
||||
blocks: [
|
||||
{
|
||||
id: 'branch_0',
|
||||
type: 'block',
|
||||
id: 'case_0',
|
||||
type: 'case',
|
||||
data: {
|
||||
title: 'If_0',
|
||||
inputsValues: {
|
||||
@ -126,8 +126,8 @@ export const initialData: FlowDocumentJSON = {
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
id: 'branch_1',
|
||||
type: 'block',
|
||||
id: 'case_1',
|
||||
type: 'case',
|
||||
data: {
|
||||
title: 'If_1',
|
||||
inputsValues: {
|
||||
|
||||
@ -5,8 +5,13 @@ import iconIf from '../../assets/icon-if.png';
|
||||
import { formMeta } from './form-meta';
|
||||
|
||||
let id = 2;
|
||||
export const BlockNodeRegistry: FlowNodeRegistry = {
|
||||
type: 'block',
|
||||
export const CaseNodeRegistry: FlowNodeRegistry = {
|
||||
type: 'case',
|
||||
/**
|
||||
* 分支节点需要继承自 block
|
||||
* Branch nodes need to inherit from 'block'
|
||||
*/
|
||||
extend: 'block',
|
||||
meta: {
|
||||
copyDisable: true,
|
||||
},
|
||||
@ -15,19 +20,13 @@ export const BlockNodeRegistry: FlowNodeRegistry = {
|
||||
description: 'Execute the branch when the condition is met.',
|
||||
},
|
||||
canAdd: () => false,
|
||||
canDelete: (ctx, node) => {
|
||||
if (node.originParent!.flowNodeType === 'tryCatch') {
|
||||
return node.parent!.blocks.length >= 2;
|
||||
}
|
||||
return node.parent!.blocks.length >= 3;
|
||||
},
|
||||
canDelete: (ctx, node) => node.parent!.blocks.length >= 3,
|
||||
onAdd(ctx, from) {
|
||||
const isTryCatch = from.flowNodeType === 'tryCatch';
|
||||
return {
|
||||
id: `if_${nanoid(5)}`,
|
||||
type: isTryCatch ? 'catchBlock' : 'block',
|
||||
type: 'case',
|
||||
data: {
|
||||
title: isTryCatch ? `Catch Block ${id++}` : `If_${id++}`,
|
||||
title: `If_${id++}`,
|
||||
inputs: {
|
||||
type: 'object',
|
||||
required: ['condition'],
|
||||
32
apps/demo-fixed-layout/src/nodes/catch-block/form-meta.tsx
Normal file
32
apps/demo-fixed-layout/src/nodes/catch-block/form-meta.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
import { FormRenderProps, FormMeta, ValidateTrigger } from '@flowgram.ai/fixed-layout-editor';
|
||||
|
||||
import { FlowNodeJSON } from '../../typings';
|
||||
import { FormHeader, FormContent, FormInputs, FormOutputs } from '../../form-components';
|
||||
|
||||
export const renderForm = ({ form }: FormRenderProps<FlowNodeJSON['data']>) => (
|
||||
<>
|
||||
<FormHeader />
|
||||
<FormContent>
|
||||
<FormInputs />
|
||||
<FormOutputs />
|
||||
</FormContent>
|
||||
</>
|
||||
);
|
||||
|
||||
export const formMeta: FormMeta<FlowNodeJSON['data']> = {
|
||||
render: renderForm,
|
||||
validateTrigger: ValidateTrigger.onChange,
|
||||
validate: {
|
||||
'inputsValues.*': ({ value, context, formValues, name }) => {
|
||||
const valuePropetyKey = name.replace(/^inputsValues\./, '');
|
||||
const required = formValues.inputs?.required || [];
|
||||
if (
|
||||
required.includes(valuePropetyKey) &&
|
||||
(value === '' || value === undefined || value?.content === '')
|
||||
) {
|
||||
return `${valuePropetyKey} is required`;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
},
|
||||
};
|
||||
41
apps/demo-fixed-layout/src/nodes/catch-block/index.ts
Normal file
41
apps/demo-fixed-layout/src/nodes/catch-block/index.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { nanoid } from 'nanoid';
|
||||
|
||||
import { FlowNodeRegistry } from '../../typings';
|
||||
import iconIf from '../../assets/icon-if.png';
|
||||
import { formMeta } from './form-meta';
|
||||
|
||||
let id = 3;
|
||||
export const CatchBlockNodeRegistry: FlowNodeRegistry = {
|
||||
type: 'catchBlock',
|
||||
meta: {
|
||||
copyDisable: true,
|
||||
},
|
||||
info: {
|
||||
icon: iconIf,
|
||||
description: 'Execute the catch branch when the condition is met.',
|
||||
},
|
||||
canAdd: () => false,
|
||||
canDelete: (ctx, node) => node.parent!.blocks.length >= 2,
|
||||
onAdd(ctx, from) {
|
||||
return {
|
||||
id: `Catch_${nanoid(5)}`,
|
||||
type: 'catchblock',
|
||||
data: {
|
||||
title: `Catch Block ${id++}`,
|
||||
inputs: {
|
||||
type: 'object',
|
||||
required: ['condition'],
|
||||
inputsValues: {
|
||||
condition: '',
|
||||
},
|
||||
properties: {
|
||||
condition: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
formMeta,
|
||||
};
|
||||
@ -27,7 +27,7 @@ export const ConditionNodeRegistry: FlowNodeRegistry = {
|
||||
blocks: [
|
||||
{
|
||||
id: nanoid(5),
|
||||
type: 'block',
|
||||
type: 'case',
|
||||
data: {
|
||||
title: 'If_0',
|
||||
inputsValues: {
|
||||
@ -47,7 +47,7 @@ export const ConditionNodeRegistry: FlowNodeRegistry = {
|
||||
},
|
||||
{
|
||||
id: nanoid(5),
|
||||
type: 'block',
|
||||
type: 'case',
|
||||
data: {
|
||||
title: 'If_1',
|
||||
inputsValues: {
|
||||
|
||||
@ -5,7 +5,8 @@ import { LoopNodeRegistry } from './loop';
|
||||
import { LLMNodeRegistry } from './llm';
|
||||
import { EndNodeRegistry } from './end';
|
||||
import { ConditionNodeRegistry } from './condition';
|
||||
import { BlockNodeRegistry } from './block';
|
||||
import { CatchBlockNodeRegistry } from './catch-block';
|
||||
import { CaseNodeRegistry } from './case';
|
||||
|
||||
export const FlowNodeRegistries: FlowNodeRegistry[] = [
|
||||
StartNodeRegistry,
|
||||
@ -13,6 +14,7 @@ export const FlowNodeRegistries: FlowNodeRegistry[] = [
|
||||
ConditionNodeRegistry,
|
||||
LLMNodeRegistry,
|
||||
LoopNodeRegistry,
|
||||
BlockNodeRegistry,
|
||||
CaseNodeRegistry,
|
||||
TryCatchNodeRegistry,
|
||||
CatchBlockNodeRegistry,
|
||||
];
|
||||
|
||||
@ -1,3 +1,14 @@
|
||||
# 常见问题
|
||||
|
||||
todo
|
||||
|
||||
## 运行报报错
|
||||
|
||||
|
||||
## 如何修改节点的数据
|
||||
|
||||
|
||||
## 是否支持 vue
|
||||
|
||||
|
||||
##
|
||||
|
||||
|
||||
@ -581,8 +581,8 @@ export class FlowDocument<T = FlowDocumentJSON> implements Disposable {
|
||||
return this.entityManager.getEntities(FlowNodeEntity);
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return this.originTree.toString();
|
||||
toString(showType?: boolean): string {
|
||||
return this.originTree.toString(showType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,10 +1,14 @@
|
||||
import { type Disposable, Emitter } from '@flowgram.ai/utils';
|
||||
|
||||
import { type FlowNodeType } from './typings';
|
||||
|
||||
/**
|
||||
* 存储节点的 tree 结构信息
|
||||
* 策略是 "重修改轻查询",即修改时候做的事情更多,查询都通过指针来操作
|
||||
*/
|
||||
export class FlowVirtualTree<T extends { id: string }> implements Disposable {
|
||||
export class FlowVirtualTree<T extends { id: string; flowNodeType?: FlowNodeType }>
|
||||
implements Disposable
|
||||
{
|
||||
protected onTreeChangeEmitter = new Emitter<void>();
|
||||
|
||||
/**
|
||||
@ -208,13 +212,17 @@ export class FlowVirtualTree<T extends { id: string }> implements Disposable {
|
||||
return this.map.size;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
toString(showType?: boolean): string {
|
||||
const ret: string[] = [];
|
||||
this.traverse((node, depth) => {
|
||||
if (depth === 0) {
|
||||
ret.push(node.id);
|
||||
} else {
|
||||
ret.push(`|${new Array(depth).fill('--').join('')} ${node.id}`);
|
||||
ret.push(
|
||||
`|${new Array(depth).fill('--').join('')} ${
|
||||
showType ? `${node.flowNodeType}(${node.id})` : node.id
|
||||
}`
|
||||
);
|
||||
}
|
||||
});
|
||||
return `${ret.join('\n')}`;
|
||||
|
||||
@ -261,6 +261,7 @@ export interface FlowNodeRegistry<M extends FlowNodeMeta = FlowNodeMeta> {
|
||||
extendChildRegistries?: FlowNodeRegistry[];
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* 自定义子节点添加逻辑
|
||||
* @param node 节点
|
||||
* @param json 添加的节点 JSON
|
||||
|
||||
@ -171,6 +171,9 @@ export const BlockRegistry: FlowNodeRegistry = {
|
||||
return [...draggingLabel];
|
||||
},
|
||||
|
||||
/**
|
||||
* @depreacted
|
||||
*/
|
||||
addChild(node, json, options = {}) {
|
||||
const { index } = options;
|
||||
const document = node.document;
|
||||
|
||||
@ -68,6 +68,9 @@ export const DynamicSplitRegistry: FlowNodeRegistry = {
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* @depreacted
|
||||
*/
|
||||
addChild(node, json, options = {}) {
|
||||
const { index } = options;
|
||||
const document = node.document;
|
||||
|
||||
@ -177,6 +177,9 @@ export const LoopRegistry: FlowNodeRegistry = {
|
||||
LoopInlineBlocksNodeRegistry,
|
||||
],
|
||||
|
||||
/**
|
||||
* @depreacted
|
||||
*/
|
||||
addChild(node, json, options = {}) {
|
||||
const { index } = options;
|
||||
const document = node.document;
|
||||
|
||||
@ -60,7 +60,7 @@ export const TryCatchRegistry: FlowNodeRegistry = {
|
||||
});
|
||||
const tryBlockNode = document.addNode({
|
||||
id: tryBlock.id,
|
||||
type: TryCatchTypeEnum.TRY_BLOCK,
|
||||
type: tryBlock.type || TryCatchTypeEnum.TRY_BLOCK,
|
||||
originParent: node,
|
||||
parent: mainBlockNode,
|
||||
data: tryBlock.data,
|
||||
@ -109,7 +109,7 @@ export const TryCatchRegistry: FlowNodeRegistry = {
|
||||
const parent = node.document.getNode(`$catchInlineBlocks$${node.id}`);
|
||||
const block = node.document.addNode({
|
||||
id: blockData.id,
|
||||
type: TryCatchTypeEnum.CATCH_BLOCK,
|
||||
type: node.type || TryCatchTypeEnum.CATCH_BLOCK,
|
||||
originParent: node,
|
||||
parent,
|
||||
data: blockData.data,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user