(
() => ({
/**
* Whether to enable the background
*/
background: true,
/**
* Whether it is read-only or not, the node cannot be dragged in read-only mode
*/
readonly: false,
/**
* Initial data
* 初始化数据
*/
initialData,
/**
* 画布节点定义
*/
nodeRegistries,
/**
* Get the default node registry, which will be merged with the 'nodeRegistries'
* 提供默认的节点注册,这个会和 nodeRegistries 做合并
*/
getNodeDefaultRegistry(type) {
return {
type,
meta: {
defaultExpanded: true,
},
formMeta: {
/**
* Render form
*/
render: () => <>
name="title">
{({ field }) => {field.value}
}
name="content">
>
}
};
},
/**
* Materials, components can be customized based on the key
* 可以通过 key 自定义 UI 组件
*/
materials: {
renderNodes: {
...defaultFixedSemiMaterials,
/**
* Components can be customized based on key business-side requirements.
* 这里可以根据 key 业务侧定制组件
*/
[FlowRendererKey.ADDER]: NodeAdder,
[FlowRendererKey.BRANCH_ADDER]: BranchAdder,
// [FlowRendererKey.DRAG_NODE]: DragNode,
},
renderDefaultNode: BaseNode, // 节点渲染
renderTexts: {
[FlowTextKey.LOOP_END_TEXT]: 'loop end',
[FlowTextKey.LOOP_TRAVERSE_TEXT]: 'looping',
},
},
/**
* Node engine enable, you can configure formMeta in the FlowNodeRegistry
*/
nodeEngine: {
enable: true,
},
history: {
enable: true,
enableChangeNode: true, // Listen Node engine data change
onApply(ctx, opt) {
// Listen change to trigger auto save
// console.log('auto save: ', ctx.document.toJSON(), opt);
},
},
/**
* 画布初始化
*/
onInit: ctx => {
/**
* Data can also be dynamically loaded via fromJSON
* 也可以通过 fromJSON 动态加载数据
*/
// ctx.document.fromJSON(initialData)
console.log('---- Playground Init ----');
},
/**
* 画布销毁
*/
onDispose: () => {
console.log('---- Playground Dispose ----');
},
plugins: () => [
/**
* Minimap plugin
* 缩略图插件
*/
createMinimapPlugin({
disableLayer: true,
enableDisplayAllNodes: true,
canvasStyle: {
canvasWidth: 182,
canvasHeight: 102,
canvasPadding: 50,
canvasBackground: 'rgba(245, 245, 245, 1)',
canvasBorderRadius: 10,
viewportBackground: 'rgba(235, 235, 235, 1)',
viewportBorderRadius: 4,
viewportBorderColor: 'rgba(201, 201, 201, 1)',
viewportBorderWidth: 1,
viewportBorderDashLength: 2,
nodeColor: 'rgba(255, 255, 255, 1)',
nodeBorderRadius: 2,
nodeBorderWidth: 0.145,
nodeBorderColor: 'rgba(6, 7, 9, 0.10)',
overlayColor: 'rgba(255, 255, 255, 0)',
},
inactiveDebounceTime: 1,
}),
],
}),
[],
);
}
`;
const baseNodeCode = `import { FlowNodeEntity, useNodeRender } from '@flowgram.ai/fixed-layout-editor';
export const BaseNode = ({ node }: { node: FlowNodeEntity }) => {
/**
* Provides methods related to node rendering
* 提供节点渲染相关的方法
*/
const nodeRender = useNodeRender();
/**
* It can only be used when nodeEngine is enabled
* 只有在节点引擎开启时候才能使用表单
*/
const form = nodeRender.form;
return (
{
// trigger drag node
nodeRender.startDrag(e);
e.stopPropagation();
}}
style={{
...(nodeRender.isBlockOrderIcon || nodeRender.isBlockIcon ? { width: 260 } : {}),
}}
>
{form?.render()}
);
};
`;
const branchAdderCode = `import { type FlowNodeEntity, useClientContext } from '@flowgram.ai/fixed-layout-editor';
import { IconPlus } from '@douyinfe/semi-icons';
import { nanoid } from 'nanoid';
interface PropsType {
activated?: boolean;
node: FlowNodeEntity;
}
export function BranchAdder(props: PropsType) {
const { activated, node } = props;
const nodeData = node.firstChild!.renderData;
const ctx = useClientContext();
const { operation, playground } = ctx;
const { isVertical } = node;
function addBranch() {
const block = operation.addBlock(node, {
id: \`branch_\${nanoid(5)}\`,
type: 'block',
data: {
title: 'New Branch',
content: ''
}
});
setTimeout(() => {
playground.scrollToView({
bounds: block.bounds,
scrollToCenter: true,
});
}, 10);
}
if (playground.config.readonlyOrDisabled) return null;
const className = [
'demo-fixed-adder',
isVertical ? '' : 'isHorizontal',
activated ? 'activated' : ''
].join(' ');
return (
nodeData?.toggleMouseEnter()}
onMouseLeave={() => nodeData?.toggleMouseLeave()}
>
{
addBranch();
}}
aria-hidden="true"
style={{ flexGrow: 1, textAlign: 'center' }}
>
);
}
`;
const miniMapCode = `import { FlowMinimapService, MinimapRender } from '@flowgram.ai/minimap-plugin';
import { useService } from '@flowgram.ai/fixed-layout-editor';
export const Minimap = () => {
const minimapService = useService(FlowMinimapService);
return (
);
};
`;
const nodeAdderCode = `import { FlowNodeEntity, FlowOperationService, useClientContext, usePlayground, useService } from "@flowgram.ai/fixed-layout-editor"
import { Dropdown } from '@douyinfe/semi-ui'
import { IconPlusCircle } from "@douyinfe/semi-icons";
import { nodeRegistries } from '../node-registries';
export const NodeAdder = (props: {
from: FlowNodeEntity;
to?: FlowNodeEntity;
hoverActivated: boolean;
}) => {
const { from, hoverActivated } = props;
const playground = usePlayground();
const context = useClientContext();
const flowOperationService = useService(FlowOperationService) as FlowOperationService;
const add = (addProps: any) => {
const blocks = addProps.blocks ? addProps.blocks : undefined;
const block = flowOperationService.addFromNode(from, {
...addProps,
blocks,
});
setTimeout(() => {
playground.scrollToView({
bounds: block.bounds,
scrollToCenter: true,
});
}, 10);
};
if (playground.config.readonlyOrDisabled) return null;
return (
{nodeRegistries.map(registry => {
const props = registry?.onAdd(context, from);
add(props);
}}>{registry.type})}
}
>
{hoverActivated ?
: null
}
);
}
`;
const toolsCode = `import { useEffect, useState } from 'react'
import { usePlaygroundTools, useClientContext } from '@flowgram.ai/fixed-layout-editor';
export function Tools() {
const { history } = useClientContext();
const tools = usePlaygroundTools();
const [canUndo, setCanUndo] = useState(false);
const [canRedo, setCanRedo] = useState(false);
useEffect(() => {
const disposable = history.undoRedoService.onChange(() => {
setCanUndo(history.canUndo());
setCanRedo(history.canRedo());
});
return () => disposable.dispose();
}, [history]);
return
{Math.floor(tools.zoom * 100)}%
}
`;
export const FixedLayoutSimplePreview = () => (
{
return
}
export default App;`,
'index.tsx': indexCode,
'index.css': indexCssCode,
'initial-data.ts': initialDataCode,
'node-registries.ts': nodeRegistriesCode,
'use-editor-props.tsx': useEditorPropsCode,
'base-node.tsx': baseNodeCode,
'branch-adder.tsx': branchAdderCode,
'minimap.tsx': miniMapCode,
'node-adder.tsx': nodeAdderCode,
'tools.tsx': toolsCode,
}}
previewStyle={{
height: 500,
}}
editorStyle={{
height: 500,
}}
>
);