mirror of
https://gitee.com/ByteDance/flowgram.ai.git
synced 2025-07-07 17:43:29 +08:00
fix(fixed-layout): multi-outputs/multi-inputs collapsed and move branches (#249)
* feat(demo): demo-fixed-layout-simple add tryCatch node * feat(demo): use-editor-props add fromNodeJSON/toNodeJSON config * fix(demo): demo-fixed-layout-simple readonly refresh * fix(fixed-layout): multi-outputs collapsed and move branches * chore: update codeowners * fix(fixed-layout): multi-inputs branch adder * test(fixed-layout-core): test snapshots update * test(fixed-layout-editor): move block to other dynamicSplit
This commit is contained in:
parent
245bee3389
commit
92b3adc5d0
4
.github/CODEOWNERS
vendored
4
.github/CODEOWNERS
vendored
@ -1,9 +1,9 @@
|
|||||||
# 文件路径与代码负责人分配
|
# 文件路径与代码负责人分配
|
||||||
# 对整个仓库设置代码负责人
|
# 对整个仓库设置代码负责人
|
||||||
* @xiamidaxia @luics @dragooncjw
|
* @xiamidaxia @luics @dragooncjw @YuanHeDx @sanmaopep @louisyoungx
|
||||||
|
|
||||||
# 对特定目录设置代码负责人
|
# 对特定目录设置代码负责人
|
||||||
/apps/docs/ @xiamidaxia @dragooncjw @YuanHeDx @sanmaopep
|
/apps/docs/ @xiamidaxia @dragooncjw @YuanHeDx @sanmaopep @louisyoungx
|
||||||
/apps/demo-node-form/ @xiamidaxia @dragooncjw @YuanHeDx
|
/apps/demo-node-form/ @xiamidaxia @dragooncjw @YuanHeDx
|
||||||
/packages/node-engine/ @xiamidaxia @dragooncjw @YuanHeDx
|
/packages/node-engine/ @xiamidaxia @dragooncjw @YuanHeDx
|
||||||
/packages/variable-engine/ @xiamidaxia @dragooncjw @sanmaopep
|
/packages/variable-engine/ @xiamidaxia @dragooncjw @sanmaopep
|
||||||
|
|||||||
@ -38,10 +38,12 @@ export const BaseNode = ({ node }: { node: FlowNodeEntity }) => {
|
|||||||
...(nodeRender.isBlockOrderIcon || nodeRender.isBlockIcon ? { width: 260 } : {}),
|
...(nodeRender.isBlockOrderIcon || nodeRender.isBlockIcon ? { width: 260 } : {}),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
{!nodeRender.readonly && (
|
||||||
<IconDeleteStroked
|
<IconDeleteStroked
|
||||||
style={{ position: 'absolute', right: 4, top: 4 }}
|
style={{ position: 'absolute', right: 4, top: 4 }}
|
||||||
onClick={() => ctx.operation.deleteNode(nodeRender.node)}
|
onClick={() => ctx.operation.deleteNode(nodeRender.node)}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
{form?.render()}
|
{form?.render()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -25,6 +25,15 @@ export function BranchAdder(props: PropsType) {
|
|||||||
content: '',
|
content: '',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
} else if (node.flowNodeType === 'multiInputs') {
|
||||||
|
block = operation.addBlock(node, {
|
||||||
|
id: `input_${nanoid(5)}`,
|
||||||
|
type: 'input',
|
||||||
|
data: {
|
||||||
|
title: 'New Input',
|
||||||
|
content: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
block = operation.addBlock(node, {
|
block = operation.addBlock(node, {
|
||||||
id: `branch_${nanoid(5)}`,
|
id: `branch_${nanoid(5)}`,
|
||||||
@ -43,6 +52,7 @@ export function BranchAdder(props: PropsType) {
|
|||||||
});
|
});
|
||||||
}, 10);
|
}, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playground.config.readonlyOrDisabled) return null;
|
if (playground.config.readonlyOrDisabled) return null;
|
||||||
|
|
||||||
const className = [
|
const className = [
|
||||||
|
|||||||
@ -13,9 +13,10 @@ export function FlowSelect() {
|
|||||||
const targetDemoJSON = FLOW_LIST[demoKey];
|
const targetDemoJSON = FLOW_LIST[demoKey];
|
||||||
if (targetDemoJSON) {
|
if (targetDemoJSON) {
|
||||||
clientContext.history.stop(); // Stop redo/undo
|
clientContext.history.stop(); // Stop redo/undo
|
||||||
clientContext.document.fromJSON(targetDemoJSON);
|
clientContext.history.clear(); // Clear redo/undo
|
||||||
console.log(clientContext.document.toString());
|
clientContext.document.fromJSON(targetDemoJSON); // Reload Data
|
||||||
clientContext.history.start();
|
console.log(clientContext.document.toString()); // Print the document tree
|
||||||
|
clientContext.history.start(); // Restart redo/undo
|
||||||
clientContext.document.setLayout(
|
clientContext.document.setLayout(
|
||||||
targetDemoJSON.defaultLayout || FlowLayoutDefault.VERTICAL_FIXED_LAYOUT
|
targetDemoJSON.defaultLayout || FlowLayoutDefault.VERTICAL_FIXED_LAYOUT
|
||||||
);
|
);
|
||||||
@ -26,7 +27,7 @@ export function FlowSelect() {
|
|||||||
}
|
}
|
||||||
// Fit View
|
// Fit View
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
clientContext.playground.config.fitView(clientContext.document.root.bounds);
|
clientContext.playground.config.fitView(clientContext.document.root.bounds, true, 40);
|
||||||
}, 20);
|
}, 20);
|
||||||
}
|
}
|
||||||
}, [demoKey]);
|
}, [demoKey]);
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
import { useEffect, useState, useCallback } from 'react';
|
import { useEffect, useState, useCallback } from 'react';
|
||||||
|
|
||||||
import { usePlaygroundTools, useClientContext } from '@flowgram.ai/fixed-layout-editor';
|
import { usePlaygroundTools, useClientContext, useRefresh } from '@flowgram.ai/fixed-layout-editor';
|
||||||
import { IconButton, Space } from '@douyinfe/semi-ui';
|
import { IconButton, Space } from '@douyinfe/semi-ui';
|
||||||
import { IconUnlock, IconLock } from '@douyinfe/semi-icons';
|
import { IconUnlock, IconLock } from '@douyinfe/semi-icons';
|
||||||
|
|
||||||
export function Tools() {
|
export function Tools() {
|
||||||
const { history, playground } = useClientContext();
|
const { history, playground } = useClientContext();
|
||||||
const tools = usePlaygroundTools();
|
const tools = usePlaygroundTools();
|
||||||
|
const refresh = useRefresh();
|
||||||
const [canUndo, setCanUndo] = useState(false);
|
const [canUndo, setCanUndo] = useState(false);
|
||||||
const [canRedo, setCanRedo] = useState(false);
|
const [canRedo, setCanRedo] = useState(false);
|
||||||
const toggleReadonly = useCallback(() => {
|
const toggleReadonly = useCallback(() => {
|
||||||
@ -21,6 +22,11 @@ export function Tools() {
|
|||||||
return () => disposable.dispose();
|
return () => disposable.dispose();
|
||||||
}, [history]);
|
}, [history]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const disposable = playground.config.onReadonlyOrDisabledChange(() => refresh());
|
||||||
|
return () => disposable.dispose();
|
||||||
|
}, [playground]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Space
|
<Space
|
||||||
style={{ position: 'absolute', zIndex: 10, bottom: 16, left: 16, display: 'flex', gap: 8 }}
|
style={{ position: 'absolute', zIndex: 10, bottom: 16, left: 16, display: 'flex', gap: 8 }}
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
import { FlowDocumentJSON, FlowLayoutDefault } from '@flowgram.ai/fixed-layout-editor';
|
import { FlowDocumentJSON, FlowLayoutDefault } from '@flowgram.ai/fixed-layout-editor';
|
||||||
|
|
||||||
|
import { tryCatch } from './trycatch';
|
||||||
import { mindmap } from './mindmap';
|
import { mindmap } from './mindmap';
|
||||||
import { condition } from './condition';
|
import { condition } from './condition';
|
||||||
|
|
||||||
export const FLOW_LIST: Record<string, FlowDocumentJSON & { defaultLayout?: FlowLayoutDefault }> = {
|
export const FLOW_LIST: Record<string, FlowDocumentJSON & { defaultLayout?: FlowLayoutDefault }> = {
|
||||||
condition,
|
condition,
|
||||||
mindmap: { ...mindmap, defaultLayout: FlowLayoutDefault.HORIZONTAL_FIXED_LAYOUT },
|
mindmap: { ...mindmap, defaultLayout: FlowLayoutDefault.HORIZONTAL_FIXED_LAYOUT },
|
||||||
|
tryCatch,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -20,6 +20,13 @@ export const mindmap: FlowDocumentJSON = {
|
|||||||
title: 'input_1',
|
title: 'input_1',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'input_3',
|
||||||
|
type: 'input',
|
||||||
|
data: {
|
||||||
|
title: 'input_3',
|
||||||
|
},
|
||||||
|
},
|
||||||
// {
|
// {
|
||||||
// id: 'multiInputs_2',
|
// id: 'multiInputs_2',
|
||||||
// type: 'multiInputs',
|
// type: 'multiInputs',
|
||||||
|
|||||||
@ -0,0 +1,56 @@
|
|||||||
|
import { FlowDocumentJSON } from '@flowgram.ai/fixed-layout-editor';
|
||||||
|
|
||||||
|
export const tryCatch: FlowDocumentJSON = {
|
||||||
|
nodes: [
|
||||||
|
// 开始节点
|
||||||
|
{
|
||||||
|
id: 'start_0',
|
||||||
|
type: 'start',
|
||||||
|
data: {
|
||||||
|
title: 'Start',
|
||||||
|
content: 'start content',
|
||||||
|
},
|
||||||
|
blocks: [],
|
||||||
|
},
|
||||||
|
// 分支节点
|
||||||
|
{
|
||||||
|
id: 'tryCatch_0',
|
||||||
|
type: 'tryCatch',
|
||||||
|
data: {
|
||||||
|
title: 'TryCatch',
|
||||||
|
},
|
||||||
|
blocks: [
|
||||||
|
{
|
||||||
|
id: 'tryBlock_0',
|
||||||
|
type: 'tryBlock',
|
||||||
|
blocks: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'catchBlock_0',
|
||||||
|
type: 'catchBlock',
|
||||||
|
data: {
|
||||||
|
title: 'Catch Block 1',
|
||||||
|
},
|
||||||
|
blocks: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'catchBlock_1',
|
||||||
|
type: 'catchBlock',
|
||||||
|
data: {
|
||||||
|
title: 'Catch Block 2',
|
||||||
|
},
|
||||||
|
blocks: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
// 结束节点
|
||||||
|
{
|
||||||
|
id: 'end_0',
|
||||||
|
type: 'end',
|
||||||
|
data: {
|
||||||
|
title: 'End',
|
||||||
|
content: 'end content',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
@ -6,12 +6,16 @@ import './index.css';
|
|||||||
import { nodeRegistries } from './node-registries';
|
import { nodeRegistries } from './node-registries';
|
||||||
import { initialData } from './initial-data';
|
import { initialData } from './initial-data';
|
||||||
import { useEditorProps } from './hooks/use-editor-props';
|
import { useEditorProps } from './hooks/use-editor-props';
|
||||||
|
import { FLOW_LIST } from './data';
|
||||||
import { Tools } from './components/tools';
|
import { Tools } from './components/tools';
|
||||||
import { Minimap } from './components/minimap';
|
import { Minimap } from './components/minimap';
|
||||||
import { FlowSelect } from './components/flow-select';
|
import { FlowSelect } from './components/flow-select';
|
||||||
|
|
||||||
export const Editor = () => {
|
export const Editor = (props: { demoKey?: string }) => {
|
||||||
const editorProps = useEditorProps(initialData, nodeRegistries);
|
const editorProps = useEditorProps(
|
||||||
|
props.demoKey ? FLOW_LIST[props.demoKey] : initialData,
|
||||||
|
nodeRegistries
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<FixedLayoutEditorProvider {...editorProps}>
|
<FixedLayoutEditorProvider {...editorProps}>
|
||||||
<div className="demo-fixed-container">
|
<div className="demo-fixed-container">
|
||||||
|
|||||||
@ -106,6 +106,7 @@ export function useEditorProps(
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
|
* Playground init
|
||||||
* 画布初始化
|
* 画布初始化
|
||||||
*/
|
*/
|
||||||
onInit: (ctx) => {
|
onInit: (ctx) => {
|
||||||
@ -117,11 +118,30 @@ export function useEditorProps(
|
|||||||
console.log('---- Playground Init ----');
|
console.log('---- Playground Init ----');
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
|
* Playground dispose
|
||||||
* 画布销毁
|
* 画布销毁
|
||||||
*/
|
*/
|
||||||
onDispose: () => {
|
onDispose: () => {
|
||||||
console.log('---- Playground Dispose ----');
|
console.log('---- Playground Dispose ----');
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 节点数据转换, 由 ctx.document.fromJSON 调用
|
||||||
|
* Node data transformation, called by ctx.document.fromJSON
|
||||||
|
* @param node
|
||||||
|
* @param json
|
||||||
|
*/
|
||||||
|
fromNodeJSON(node, json) {
|
||||||
|
return json;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 节点数据转换, 由 ctx.document.toJSON 调用
|
||||||
|
* Node data transformation, called by ctx.document.toJSON
|
||||||
|
* @param node
|
||||||
|
* @param json
|
||||||
|
*/
|
||||||
|
toNodeJSON(node, json) {
|
||||||
|
return json;
|
||||||
|
},
|
||||||
plugins: () => [
|
plugins: () => [
|
||||||
/**
|
/**
|
||||||
* Minimap plugin
|
* Minimap plugin
|
||||||
|
|||||||
@ -1,9 +1,5 @@
|
|||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import {
|
import { FlowNodeRegistry } from '@flowgram.ai/fixed-layout-editor';
|
||||||
FlowNodeRegistry,
|
|
||||||
FlowNodeEntity,
|
|
||||||
FlowNodeBaseType,
|
|
||||||
} from '@flowgram.ai/fixed-layout-editor';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自定义节点注册
|
* 自定义节点注册
|
||||||
@ -77,35 +73,4 @@ export const nodeRegistries: FlowNodeRegistry[] = [
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
type: 'multiStart2',
|
|
||||||
extend: 'dynamicSplit',
|
|
||||||
meta: {
|
|
||||||
isStart: true,
|
|
||||||
},
|
|
||||||
onCreate(node, json) {
|
|
||||||
const doc = node.document;
|
|
||||||
const addedNodes: FlowNodeEntity[] = [];
|
|
||||||
const blocks = json.blocks || [];
|
|
||||||
|
|
||||||
if (blocks.length > 0) {
|
|
||||||
// 水平布局
|
|
||||||
const inlineBlocksNode = doc.addNode({
|
|
||||||
id: `$inlineBlocks$${node.id}`,
|
|
||||||
type: FlowNodeBaseType.INLINE_BLOCKS,
|
|
||||||
originParent: node,
|
|
||||||
parent: node,
|
|
||||||
});
|
|
||||||
addedNodes.push(inlineBlocksNode);
|
|
||||||
blocks.forEach((blockData) => {
|
|
||||||
doc.addBlock(node, blockData, addedNodes);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return addedNodes;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'tree',
|
|
||||||
extend: 'simpleSplit',
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|||||||
@ -63,6 +63,24 @@ export function useEditorProps(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 节点数据转换, 由 ctx.document.fromJSON 调用
|
||||||
|
* Node data transformation, called by ctx.document.fromJSON
|
||||||
|
* @param node
|
||||||
|
* @param json
|
||||||
|
*/
|
||||||
|
fromNodeJSON(node, json) {
|
||||||
|
return json;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 节点数据转换, 由 ctx.document.toJSON 调用
|
||||||
|
* Node data transformation, called by ctx.document.toJSON
|
||||||
|
* @param node
|
||||||
|
* @param json
|
||||||
|
*/
|
||||||
|
toNodeJSON(node, json) {
|
||||||
|
return json;
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* Set default layout
|
* Set default layout
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -34,6 +34,24 @@ export const useEditorProps = () =>
|
|||||||
* 节点注册
|
* 节点注册
|
||||||
*/
|
*/
|
||||||
nodeRegistries,
|
nodeRegistries,
|
||||||
|
/**
|
||||||
|
* 节点数据转换, 由 ctx.document.fromJSON 调用
|
||||||
|
* Node data transformation, called by ctx.document.fromJSON
|
||||||
|
* @param node
|
||||||
|
* @param json
|
||||||
|
*/
|
||||||
|
fromNodeJSON(node, json) {
|
||||||
|
return json;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 节点数据转换, 由 ctx.document.toJSON 调用
|
||||||
|
* Node data transformation, called by ctx.document.toJSON
|
||||||
|
* @param node
|
||||||
|
* @param json
|
||||||
|
*/
|
||||||
|
toNodeJSON(node, json) {
|
||||||
|
return json;
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* Get the default node registry, which will be merged with the 'nodeRegistries'
|
* Get the default node registry, which will be merged with the 'nodeRegistries'
|
||||||
* 提供默认的节点注册,这个会和 nodeRegistries 做合并
|
* 提供默认的节点注册,这个会和 nodeRegistries 做合并
|
||||||
|
|||||||
@ -57,6 +57,24 @@ export function useEditorProps(
|
|||||||
formMeta: defaultFormMeta,
|
formMeta: defaultFormMeta,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 节点数据转换, 由 ctx.document.fromJSON 调用
|
||||||
|
* Node data transformation, called by ctx.document.fromJSON
|
||||||
|
* @param node
|
||||||
|
* @param json
|
||||||
|
*/
|
||||||
|
fromNodeJSON(node, json) {
|
||||||
|
return json;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 节点数据转换, 由 ctx.document.toJSON 调用
|
||||||
|
* Node data transformation, called by ctx.document.toJSON
|
||||||
|
* @param node
|
||||||
|
* @param json
|
||||||
|
*/
|
||||||
|
toNodeJSON(node, json) {
|
||||||
|
return json;
|
||||||
|
},
|
||||||
lineColor: {
|
lineColor: {
|
||||||
hidden: 'transparent',
|
hidden: 'transparent',
|
||||||
default: '#4d53e8',
|
default: '#4d53e8',
|
||||||
|
|||||||
@ -344,8 +344,6 @@ export class FlowDocument<T = FlowDocumentJSON> implements Disposable {
|
|||||||
parent: node,
|
parent: node,
|
||||||
});
|
});
|
||||||
addedNodes.push(blockIconNode);
|
addedNodes.push(blockIconNode);
|
||||||
// inlineblocks 为空则不创建
|
|
||||||
if (blocks.length > 0) {
|
|
||||||
// 水平布局
|
// 水平布局
|
||||||
const inlineBlocksNode = this.addNode({
|
const inlineBlocksNode = this.addNode({
|
||||||
id: `$inlineBlocks$${node.id}`,
|
id: `$inlineBlocks$${node.id}`,
|
||||||
@ -357,7 +355,6 @@ export class FlowDocument<T = FlowDocumentJSON> implements Disposable {
|
|||||||
blocks.forEach((blockData) => {
|
blocks.forEach((blockData) => {
|
||||||
this.addBlock(node, blockData, addedNodes);
|
this.addBlock(node, blockData, addedNodes);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
return addedNodes;
|
return addedNodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -29,6 +29,7 @@ export interface FlowTransitionLine {
|
|||||||
arrow?: boolean; // 是否有箭头
|
arrow?: boolean; // 是否有箭头
|
||||||
renderKey?: string; // 只有自定义线条需要
|
renderKey?: string; // 只有自定义线条需要
|
||||||
isHorizontal?: boolean; // 是否为水平布局
|
isHorizontal?: boolean; // 是否为水平布局
|
||||||
|
isDraggingLine?: boolean; // 是否是拖拽线条
|
||||||
activated?: boolean; // 是否激活态
|
activated?: boolean; // 是否激活态
|
||||||
side?: LABEL_SIDE_TYPE; // 区分是否分支前缀线条
|
side?: LABEL_SIDE_TYPE; // 区分是否分支前缀线条
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
|
|||||||
@ -118,6 +118,7 @@ exports[`flow-activities > extend block addChild 1`] = `
|
|||||||
|-------- $blockOrderIcon$test-extend-block
|
|-------- $blockOrderIcon$test-extend-block
|
||||||
|-- empty-split
|
|-- empty-split
|
||||||
|---- $blockIcon$empty-split
|
|---- $blockIcon$empty-split
|
||||||
|
|---- $inlineBlocks$empty-split
|
||||||
|-- end_0"
|
|-- end_0"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@ -38,8 +38,13 @@ export const BlockRegistry: FlowNodeRegistry = {
|
|||||||
// 当有其余分支的时候,绘制一条两个分支之间的线条
|
// 当有其余分支的时候,绘制一条两个分支之间的线条
|
||||||
if (hasBranchDraggingAdder) {
|
if (hasBranchDraggingAdder) {
|
||||||
if (isVertical) {
|
if (isVertical) {
|
||||||
const currentOffsetRightX = currentTransform.firstChild?.bounds?.right || 0;
|
const currentOffsetRightX = currentTransform.firstChild
|
||||||
const nextOffsetLeftX = currentTransform.next?.firstChild?.bounds?.left || 0;
|
? currentTransform.firstChild.bounds.right
|
||||||
|
: currentTransform.bounds.right;
|
||||||
|
const nextOffsetLeftX =
|
||||||
|
(currentTransform.next?.firstChild
|
||||||
|
? currentTransform.next?.firstChild.bounds?.left
|
||||||
|
: currentTransform.next?.bounds?.left) || 0;
|
||||||
const currentInputPointY = currentTransform.inputPoint.y;
|
const currentInputPointY = currentTransform.inputPoint.y;
|
||||||
if (currentTransform?.next) {
|
if (currentTransform?.next) {
|
||||||
lines.push({
|
lines.push({
|
||||||
@ -53,8 +58,13 @@ export const BlockRegistry: FlowNodeRegistry = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const currentOffsetRightY = currentTransform.firstChild?.bounds?.bottom || 0;
|
const currentOffsetBottomX = currentTransform.firstChild
|
||||||
const nextOffsetLeftY = currentTransform.next?.firstChild?.bounds?.top || 0;
|
? currentTransform.firstChild.bounds.bottom
|
||||||
|
: currentTransform.bounds.bottom;
|
||||||
|
const nextOffsetTopX =
|
||||||
|
(currentTransform.next?.firstChild
|
||||||
|
? currentTransform.next?.firstChild.bounds?.top
|
||||||
|
: currentTransform.next?.bounds?.top) || 0;
|
||||||
const currentInputPointX = currentTransform.inputPoint.x;
|
const currentInputPointX = currentTransform.inputPoint.x;
|
||||||
if (currentTransform?.next) {
|
if (currentTransform?.next) {
|
||||||
lines.push({
|
lines.push({
|
||||||
@ -62,7 +72,7 @@ export const BlockRegistry: FlowNodeRegistry = {
|
|||||||
from: currentTransform.parent!.inputPoint,
|
from: currentTransform.parent!.inputPoint,
|
||||||
to: {
|
to: {
|
||||||
x: currentInputPointX,
|
x: currentInputPointX,
|
||||||
y: (currentOffsetRightY + nextOffsetLeftY) / 2,
|
y: (currentOffsetBottomX + nextOffsetTopX) / 2,
|
||||||
},
|
},
|
||||||
side: LABEL_SIDE_TYPE.NORMAL_BRANCH,
|
side: LABEL_SIDE_TYPE.NORMAL_BRANCH,
|
||||||
});
|
});
|
||||||
@ -112,8 +122,13 @@ export const BlockRegistry: FlowNodeRegistry = {
|
|||||||
// 获取两个分支节点中间点作为拖拽标签插入位置
|
// 获取两个分支节点中间点作为拖拽标签插入位置
|
||||||
if (hasBranchDraggingAdder) {
|
if (hasBranchDraggingAdder) {
|
||||||
if (isVertical) {
|
if (isVertical) {
|
||||||
const currentOffsetRightX = currentTransform.firstChild?.bounds?.right || 0;
|
const currentOffsetRightX = currentTransform.firstChild
|
||||||
const nextOffsetLeftX = currentTransform.next?.firstChild?.bounds?.left || 0;
|
? currentTransform.firstChild.bounds.right
|
||||||
|
: currentTransform.bounds.right;
|
||||||
|
const nextOffsetLeftX =
|
||||||
|
(currentTransform.next?.firstChild
|
||||||
|
? currentTransform.next.firstChild.bounds?.left
|
||||||
|
: currentTransform.next?.bounds?.left) || 0;
|
||||||
const currentInputPointY = currentTransform.inputPoint.y;
|
const currentInputPointY = currentTransform.inputPoint.y;
|
||||||
if (currentTransform?.next) {
|
if (currentTransform?.next) {
|
||||||
draggingLabel.push({
|
draggingLabel.push({
|
||||||
@ -129,17 +144,22 @@ export const BlockRegistry: FlowNodeRegistry = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const currentOffsetRightY = currentTransform.firstChild?.bounds?.bottom || 0;
|
const currentOffsetBottomX = currentTransform.firstChild
|
||||||
const nextOffsetLeftY = currentTransform.next?.firstChild?.bounds?.top || 0;
|
? currentTransform.firstChild.bounds.bottom
|
||||||
|
: currentTransform.bounds.bottom;
|
||||||
|
const nextOffsetTopX =
|
||||||
|
(currentTransform.next?.firstChild
|
||||||
|
? currentTransform.next.firstChild.bounds?.top
|
||||||
|
: currentTransform.next?.bounds?.top) || 0;
|
||||||
const currentInputPointX = currentTransform.inputPoint.x;
|
const currentInputPointX = currentTransform.inputPoint.x;
|
||||||
if (currentTransform?.next) {
|
if (currentTransform?.next) {
|
||||||
draggingLabel.push({
|
draggingLabel.push({
|
||||||
offset: {
|
offset: {
|
||||||
x: currentInputPointX,
|
x: currentInputPointX,
|
||||||
y: (currentOffsetRightY + nextOffsetLeftY) / 2,
|
y: (currentOffsetBottomX + nextOffsetTopX) / 2,
|
||||||
},
|
},
|
||||||
type: FlowTransitionLabelEnum.BRANCH_DRAGGING_LABEL,
|
type: FlowTransitionLabelEnum.BRANCH_DRAGGING_LABEL,
|
||||||
width: nextOffsetLeftY - currentOffsetRightY,
|
width: nextOffsetTopX - currentOffsetBottomX,
|
||||||
props: {
|
props: {
|
||||||
side: LABEL_SIDE_TYPE.NORMAL_BRANCH,
|
side: LABEL_SIDE_TYPE.NORMAL_BRANCH,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import {
|
|||||||
type FlowTransitionLine,
|
type FlowTransitionLine,
|
||||||
FlowTransitionLineEnum,
|
FlowTransitionLineEnum,
|
||||||
LABEL_SIDE_TYPE,
|
LABEL_SIDE_TYPE,
|
||||||
|
FlowTransitionLabelEnum,
|
||||||
} from '@flowgram.ai/document';
|
} from '@flowgram.ai/document';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,7 +16,7 @@ export const InputRegistry: FlowNodeRegistry = {
|
|||||||
meta: {
|
meta: {
|
||||||
hidden: false,
|
hidden: false,
|
||||||
},
|
},
|
||||||
getLines(transition) {
|
getLines(transition, layout) {
|
||||||
const currentTransform = transition.transform;
|
const currentTransform = transition.transform;
|
||||||
const { isVertical } = transition.entity;
|
const { isVertical } = transition.entity;
|
||||||
const lines: FlowTransitionLine[] = [];
|
const lines: FlowTransitionLine[] = [];
|
||||||
@ -27,32 +28,44 @@ export const InputRegistry: FlowNodeRegistry = {
|
|||||||
// 当有其余分支的时候,绘制一条两个分支之间的线条
|
// 当有其余分支的时候,绘制一条两个分支之间的线条
|
||||||
if (hasBranchDraggingAdder) {
|
if (hasBranchDraggingAdder) {
|
||||||
if (isVertical) {
|
if (isVertical) {
|
||||||
const currentOffsetRightX = currentTransform.firstChild?.bounds?.right || 0;
|
const currentOffsetRightX = currentTransform.firstChild
|
||||||
const nextOffsetLeftX = currentTransform.next?.firstChild?.bounds?.left || 0;
|
? currentTransform.firstChild.bounds.right
|
||||||
const currentInputPointY = currentTransform.inputPoint.y;
|
: currentTransform.bounds.right;
|
||||||
|
const nextOffsetLeftX =
|
||||||
|
(currentTransform.next?.firstChild
|
||||||
|
? currentTransform.next?.firstChild.bounds?.left
|
||||||
|
: currentTransform.next?.bounds?.left) || 0;
|
||||||
|
const currentInputPointY = currentTransform.outputPoint.y;
|
||||||
if (currentTransform?.next) {
|
if (currentTransform?.next) {
|
||||||
lines.push({
|
lines.push({
|
||||||
type: FlowTransitionLineEnum.DRAGGING_LINE,
|
type: FlowTransitionLineEnum.MERGE_LINE,
|
||||||
from: currentTransform.parent!.inputPoint,
|
isDraggingLine: true,
|
||||||
to: {
|
from: {
|
||||||
x: (currentOffsetRightX + nextOffsetLeftX) / 2,
|
x: (currentOffsetRightX + nextOffsetLeftX) / 2,
|
||||||
y: currentInputPointY,
|
y: currentInputPointY,
|
||||||
},
|
},
|
||||||
|
to: currentTransform.parent!.outputPoint,
|
||||||
side: LABEL_SIDE_TYPE.NORMAL_BRANCH,
|
side: LABEL_SIDE_TYPE.NORMAL_BRANCH,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const currentOffsetRightY = currentTransform.firstChild?.bounds?.bottom || 0;
|
const currentOffsetBottomX = currentTransform.firstChild
|
||||||
const nextOffsetLeftY = currentTransform.next?.firstChild?.bounds?.top || 0;
|
? currentTransform.firstChild.bounds.bottom
|
||||||
const currentInputPointX = currentTransform.inputPoint.x;
|
: currentTransform.bounds.bottom;
|
||||||
|
const nextOffsetTopX =
|
||||||
|
(currentTransform.next?.firstChild
|
||||||
|
? currentTransform.next?.firstChild.bounds?.top
|
||||||
|
: currentTransform.next?.bounds?.top) || 0;
|
||||||
|
const currentInputPointX = currentTransform.outputPoint.x;
|
||||||
if (currentTransform?.next) {
|
if (currentTransform?.next) {
|
||||||
lines.push({
|
lines.push({
|
||||||
type: FlowTransitionLineEnum.DRAGGING_LINE,
|
type: FlowTransitionLineEnum.MERGE_LINE,
|
||||||
from: currentTransform.parent!.inputPoint,
|
isDraggingLine: true,
|
||||||
to: {
|
from: {
|
||||||
x: currentInputPointX,
|
x: currentInputPointX,
|
||||||
y: (currentOffsetRightY + nextOffsetLeftY) / 2,
|
y: (currentOffsetBottomX + nextOffsetTopX) / 2,
|
||||||
},
|
},
|
||||||
|
to: currentTransform.parent!.outputPoint,
|
||||||
side: LABEL_SIDE_TYPE.NORMAL_BRANCH,
|
side: LABEL_SIDE_TYPE.NORMAL_BRANCH,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -71,7 +84,64 @@ export const InputRegistry: FlowNodeRegistry = {
|
|||||||
|
|
||||||
return lines;
|
return lines;
|
||||||
},
|
},
|
||||||
getLabels() {
|
getLabels(transition) {
|
||||||
return [];
|
const currentTransform = transition.transform;
|
||||||
|
const { isVertical } = transition.entity;
|
||||||
|
|
||||||
|
const draggingLabel = [];
|
||||||
|
|
||||||
|
const hasBranchDraggingAdder =
|
||||||
|
currentTransform && currentTransform.entity.isInlineBlock && transition.renderData.draggable;
|
||||||
|
|
||||||
|
// 获取两个分支节点中间点作为拖拽标签插入位置
|
||||||
|
if (hasBranchDraggingAdder) {
|
||||||
|
if (isVertical) {
|
||||||
|
const currentOffsetRightX = currentTransform.firstChild
|
||||||
|
? currentTransform.firstChild.bounds.right
|
||||||
|
: currentTransform.bounds.right;
|
||||||
|
const nextOffsetLeftX =
|
||||||
|
(currentTransform.next?.firstChild
|
||||||
|
? currentTransform.next.firstChild.bounds?.left
|
||||||
|
: currentTransform.next?.bounds?.left) || 0;
|
||||||
|
const currentInputPointY = currentTransform.outputPoint.y;
|
||||||
|
if (currentTransform?.next) {
|
||||||
|
draggingLabel.push({
|
||||||
|
offset: {
|
||||||
|
x: (currentOffsetRightX + nextOffsetLeftX) / 2,
|
||||||
|
y: currentInputPointY,
|
||||||
|
},
|
||||||
|
type: FlowTransitionLabelEnum.BRANCH_DRAGGING_LABEL,
|
||||||
|
width: nextOffsetLeftX - currentOffsetRightX,
|
||||||
|
props: {
|
||||||
|
side: LABEL_SIDE_TYPE.NORMAL_BRANCH,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const currentOffsetBottomX = currentTransform.firstChild
|
||||||
|
? currentTransform.firstChild.bounds.bottom
|
||||||
|
: currentTransform.bounds.bottom;
|
||||||
|
const nextOffsetTopX =
|
||||||
|
(currentTransform.next?.firstChild
|
||||||
|
? currentTransform.next.firstChild.bounds?.top
|
||||||
|
: currentTransform.next?.bounds?.top) || 0;
|
||||||
|
const currentInputPointX = currentTransform.outputPoint.x;
|
||||||
|
if (currentTransform?.next) {
|
||||||
|
draggingLabel.push({
|
||||||
|
offset: {
|
||||||
|
x: currentInputPointX,
|
||||||
|
y: (currentOffsetBottomX + nextOffsetTopX) / 2,
|
||||||
|
},
|
||||||
|
type: FlowTransitionLabelEnum.BRANCH_DRAGGING_LABEL,
|
||||||
|
width: nextOffsetTopX - currentOffsetBottomX,
|
||||||
|
props: {
|
||||||
|
side: LABEL_SIDE_TYPE.NORMAL_BRANCH,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...draggingLabel];
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,4 +1,14 @@
|
|||||||
import { FlowNodeBaseType, FlowNodeSplitType, type FlowNodeRegistry } from '@flowgram.ai/document';
|
import { Point } from '@flowgram.ai/utils';
|
||||||
|
import { FlowRendererKey } from '@flowgram.ai/renderer';
|
||||||
|
import {
|
||||||
|
FlowNodeBaseType,
|
||||||
|
type FlowNodeRegistry,
|
||||||
|
FlowNodeRenderData,
|
||||||
|
FlowTransitionLabelEnum,
|
||||||
|
FlowNodeSplitType,
|
||||||
|
getDefaultSpacing,
|
||||||
|
ConstantKeys,
|
||||||
|
} from '@flowgram.ai/document';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 多输入节点, 只能作为 开始节点
|
* 多输入节点, 只能作为 开始节点
|
||||||
@ -15,6 +25,7 @@ export const MultiInputsRegistry: FlowNodeRegistry = {
|
|||||||
type: FlowNodeBaseType.BLOCK_ICON,
|
type: FlowNodeBaseType.BLOCK_ICON,
|
||||||
meta: {
|
meta: {
|
||||||
hidden: true,
|
hidden: true,
|
||||||
|
spacing: 0,
|
||||||
},
|
},
|
||||||
getLines() {
|
getLines() {
|
||||||
return [];
|
return [];
|
||||||
@ -25,8 +36,54 @@ export const MultiInputsRegistry: FlowNodeRegistry = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: FlowNodeBaseType.INLINE_BLOCKS,
|
type: FlowNodeBaseType.INLINE_BLOCKS,
|
||||||
getLabels() {
|
meta: {
|
||||||
return [];
|
inlineSpacingPre: 0,
|
||||||
|
},
|
||||||
|
getLabels(transition) {
|
||||||
|
const isVertical = transition.entity.isVertical;
|
||||||
|
const currentTransform = transition.transform;
|
||||||
|
const spacing = getDefaultSpacing(
|
||||||
|
transition.entity,
|
||||||
|
ConstantKeys.INLINE_BLOCKS_PADDING_BOTTOM
|
||||||
|
);
|
||||||
|
|
||||||
|
if (currentTransform.collapsed || transition.entity.childrenLength === 0) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
type: FlowTransitionLabelEnum.CUSTOM_LABEL,
|
||||||
|
renderKey: FlowRendererKey.BRANCH_ADDER,
|
||||||
|
offset: Point.move(
|
||||||
|
currentTransform.outputPoint,
|
||||||
|
isVertical ? { y: spacing } : { x: spacing }
|
||||||
|
),
|
||||||
|
props: {
|
||||||
|
// 激活状态
|
||||||
|
activated: transition.entity.getData(FlowNodeRenderData)!.activated,
|
||||||
|
transform: currentTransform,
|
||||||
|
// 传给外部使用的 node 信息
|
||||||
|
node: currentTransform.originParent?.entity,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
type: FlowTransitionLabelEnum.CUSTOM_LABEL,
|
||||||
|
renderKey: FlowRendererKey.BRANCH_ADDER,
|
||||||
|
offset: Point.move(
|
||||||
|
currentTransform.outputPoint,
|
||||||
|
isVertical ? { y: -spacing / 2 } : { x: -spacing / 2 }
|
||||||
|
),
|
||||||
|
props: {
|
||||||
|
// 激活状态
|
||||||
|
activated: transition.entity.getData(FlowNodeRenderData)!.activated,
|
||||||
|
transform: currentTransform,
|
||||||
|
// 传给外部使用的 node 信息
|
||||||
|
node: currentTransform.originParent?.entity,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@ -1,5 +1,11 @@
|
|||||||
import { FlowNodeBaseType, type FlowNodeRegistry, FlowNodeSplitType } from '@flowgram.ai/document';
|
import {
|
||||||
|
FlowLayoutDefault,
|
||||||
|
type FlowNodeRegistry,
|
||||||
|
FlowNodeSplitType,
|
||||||
|
FlowNodeBaseType,
|
||||||
|
} from '@flowgram.ai/document';
|
||||||
|
|
||||||
|
import { DynamicSplitRegistry } from './dynamic-split';
|
||||||
import { BlockRegistry } from './block';
|
import { BlockRegistry } from './block';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -13,10 +19,42 @@ import { BlockRegistry } from './block';
|
|||||||
export const MultiOuputsRegistry: FlowNodeRegistry = {
|
export const MultiOuputsRegistry: FlowNodeRegistry = {
|
||||||
type: FlowNodeBaseType.MULTI_OUTPUTS,
|
type: FlowNodeBaseType.MULTI_OUTPUTS,
|
||||||
extend: FlowNodeSplitType.SIMPLE_SPLIT,
|
extend: FlowNodeSplitType.SIMPLE_SPLIT,
|
||||||
|
meta: {
|
||||||
|
isNodeEnd: true,
|
||||||
|
},
|
||||||
getLines: (transition, layout) => {
|
getLines: (transition, layout) => {
|
||||||
|
// 嵌套在 mutliOutputs 下边
|
||||||
if (transition.entity.parent?.flowNodeType === FlowNodeBaseType.INLINE_BLOCKS) {
|
if (transition.entity.parent?.flowNodeType === FlowNodeBaseType.INLINE_BLOCKS) {
|
||||||
return BlockRegistry.getLines!(transition, layout);
|
return BlockRegistry.getLines!(transition, layout);
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
},
|
},
|
||||||
|
getLabels: (transition, layout) => [
|
||||||
|
...DynamicSplitRegistry.getLabels!(transition, layout),
|
||||||
|
...BlockRegistry.getLabels!(transition, layout),
|
||||||
|
],
|
||||||
|
getOutputPoint(transform, layout) {
|
||||||
|
const isVertical = FlowLayoutDefault.isVertical(layout);
|
||||||
|
const lastChildOutput = transform.lastChild?.outputPoint;
|
||||||
|
|
||||||
|
if (isVertical) {
|
||||||
|
return {
|
||||||
|
x: lastChildOutput ? lastChildOutput.x : transform.bounds.center.x,
|
||||||
|
y: transform.bounds.bottom,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: transform.bounds.right,
|
||||||
|
y: lastChildOutput ? lastChildOutput.y : transform.bounds.center.y,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
extendChildRegistries: [
|
||||||
|
{
|
||||||
|
type: FlowNodeBaseType.BLOCK_ICON,
|
||||||
|
meta: {
|
||||||
|
// isNodeEnd: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@ -37,11 +37,11 @@ export function createLines(props: PropsType): void {
|
|||||||
const { lineActivated } = renderData || {};
|
const { lineActivated } = renderData || {};
|
||||||
|
|
||||||
const draggingLineHide =
|
const draggingLineHide =
|
||||||
line.type === FlowTransitionLineEnum.DRAGGING_LINE &&
|
(line.type === FlowTransitionLineEnum.DRAGGING_LINE || line.isDraggingLine) &&
|
||||||
!dragService.isDroppableBranch(data.entity, line.side);
|
!dragService.isDroppableBranch(data.entity, line.side);
|
||||||
|
|
||||||
const draggingLineActivated =
|
const draggingLineActivated =
|
||||||
line.type === FlowTransitionLineEnum.DRAGGING_LINE &&
|
(line.type === FlowTransitionLineEnum.DRAGGING_LINE || line.isDraggingLine) &&
|
||||||
data.entity?.id === dragService.dropNodeId &&
|
data.entity?.id === dragService.dropNodeId &&
|
||||||
line.side === dragService.labelSide;
|
line.side === dragService.labelSide;
|
||||||
|
|
||||||
|
|||||||
@ -89,11 +89,10 @@ describe('history-operation-service moveNode', () => {
|
|||||||
const split = flowDocument.getNode('dynamicSplit_0');
|
const split = flowDocument.getNode('dynamicSplit_0');
|
||||||
const split1 = flowDocument.getNode('dynamicSplit_1');
|
const split1 = flowDocument.getNode('dynamicSplit_1');
|
||||||
|
|
||||||
// 没有执行成功,因为没有 children 的分支节点,$inlineBlocks$dynamicSplit_1 不存在
|
expect(getNodeChildrenIds(split, true)).toEqual(['block_0', 'block_2']);
|
||||||
expect(getNodeChildrenIds(split, true)).toEqual(['block_0', 'block_1', 'block_2']);
|
expect(getNodeChildrenIds(split1, true)).toEqual(['block_1']);
|
||||||
expect(getNodeChildrenIds(split1, true)).toEqual([]);
|
|
||||||
|
|
||||||
expect(historyService.canUndo()).toBe(false);
|
expect(historyService.canUndo()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('move node without parent and index', async () => {
|
it('move node without parent and index', async () => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user