# 创建固定布局画布 本案例可通过 `npx @flowgram.ai/create-app@latest fixed-layout-simple` 安装,完整代码及效果见:
固定布局基础用法
文件结构: ``` - hooks - use-editor-props.ts # 画布配置 - components - base-node.tsx # 节点渲染 - tools.tsx # 画布工具栏 - initial-data.ts # 初始化数据 - node-registries.ts # 节点配置 - app.tsx # 画布入口 ``` ### 1. 画布入口 - `FixedLayoutEditorProvider`: 画布配置器, 内部会生成 react-context 供子组件消费 - `EditorRenderer`: 为最终渲染的画布,可以包装在其他组件下边方便定制画布位置 ```tsx pure title="app.tsx" import { FixedLayoutEditorProvider, EditorRenderer, } from '@flowgram.ai/fixed-layout-editor'; import '@flowgram.ai/fixed-layout-editor/index.css'; // 加载样式 import { useEditorProps } from './hooks/use-editor-props' // 画布详细的 props 配置 import { Tools } from './components/tools' // 画布工具 function App() { const editorProps = useEditorProps() return ( ); } ``` ### 2. 配置画布 画布配置采用声明式,提供 数据、渲染、事件、插件相关配置 ```tsx pure title="hooks/use-editor-props.tsx" import { useMemo } from 'react'; import { type FixedLayoutProps } from '@flowgram.ai/fixed-layout-editor'; import { defaultFixedSemiMaterials } from '@flowgram.ai/fixed-semi-materials'; import { createMinimapPlugin } from '@flowgram.ai/minimap-plugin'; import { initialData } from './initial-data' // 初始化数据 import { nodeRegistries } from './node-registries' // 节点声明配置 import { BaseNode } from './base-node' // 节点渲染 export function useEditorProps( ): FixedLayoutProps { return useMemo( () => ({ /** * 初始化数据 */ initialData, /** * 画布节点定义 */ nodeRegistries, /** * 可以通过 key 自定义 UI 组件, 比如添加按钮,这里提供了一套 semi 组件方便快速验证, 如果需要深度定制,参考: * https://github.com/bytedance/flowgram.ai/blob/main/packages/materials/fixed-semi-materials/src/components/index.tsx */ materials: { components: { ...defaultFixedSemiMaterials, // [FlowRendererKey.ADDER]: NodeAdder, // [FlowRendererKey.BRANCH_ADDER]: BranchAdder, }, renderDefaultNode: BaseNode, // 节点渲染组件 }, /** * 节点引擎, 用于渲染节点表单 */ nodeEngine: { enable: true, }, /** * 画布历史记录, 用于控制 redo/undo */ history: { enable: true, enableChangeNode: true, // 用于监听节点表单数据变化 }, /** * 画布初始化回调 */ onInit: ctx => { // 如果要动态加载数据,可以通过如下方法异步执行 // ctx.docuemnt.fromJSON(initialData) }, /** * 画布第一次渲染完成回调 */ onAllLayersRendered: (ctx) => {}, /** * 画布销毁回调 */ onDispose: () => { }, plugins: () => [ /** * 缩略图插件 */ createMinimapPlugin({}), ], }), [], ); } ``` ### 3. 配置数据 画布文档数据采用树形结构,支持嵌套 :::note 文档数据基本结构: - nodes `array` 节点列表, 支持嵌套 ::: :::note 节点数据基本结构: - id: `string` 节点唯一标识, 必须保证唯一 - meta: `object` 节点的 ui 配置信息,如自由布局的 `position` 信息放这里 - type: `string | number` 节点类型,会和 `nodeRegistries` 中的 `type` 对应 - data: `object` 节点表单数据 - blocks: `array` 节点的分支, 采用 `block` 更贴近 `Gramming` ::: ```tsx pure title="initial-data.tsx" import { FlowDocumentJSON } from '@flowgram.ai/fixed-layout-editor'; /** * 配置流程数据,数据为 blocks 嵌套的格式 */ export const initialData: FlowDocumentJSON = { nodes: [ // 开始节点 { id: 'start_0', type: 'start', data: { title: 'Start', content: 'start content' }, blocks: [], }, // 分支节点 { id: 'condition_0', type: 'condition', data: { title: 'Condition' }, blocks: [ { id: 'branch_0', type: 'block', data: { title: 'Branch 0', content: 'branch 1 content' }, blocks: [ { id: 'custom_0', type: 'custom', data: { title: 'Custom', content: 'custrom content' }, }, ], }, { id: 'branch_1', type: 'block', data: { title: 'Branch 1', content: 'branch 1 content' }, blocks: [], }, ], }, // 结束节点 { id: 'end_0', type: 'end', data: { title: 'End', content: 'end content' }, }, ], }; ``` ### 4. 声明节点 声明节点可以用于确定节点的类型及渲染方式 ```tsx pure title="node-registries.tsx" import { FlowNodeRegistry, ValidateTrigger } from '@flowgram.ai/fixed-layout-editor'; /** * 自定义节点注册 */ export const nodeRegistries: FlowNodeRegistry[] = [ { /** * 自定义节点类型 */ type: 'condition', /** * 自定义节点扩展: * - loop: 扩展为循环节点 * - start: 扩展为开始节点 * - dynamicSplit: 扩展为分支节点 * - end: 扩展为结束节点 * - tryCatch: 扩展为 tryCatch 节点 * - default: 扩展为普通节点 (默认) */ extend: 'dynamicSplit', /** * 节点配置信息 */ meta: { // isStart: false, // 是否为开始节点 // isNodeEnd: false, // 是否为结束节点,结束节点后边无法再添加节点 // draggable: false, // 是否可拖拽,如开始节点和结束节点无法拖拽 // selectable: false, // 触发器等开始节点不能被框选 // deleteDisable: true, // 禁止删除 // copyDisable: true, // 禁止copy // addDisable: true, // 禁止添加 }, /** * 配置节点表单的校验及渲染, * 注:validate 采用数据和渲染分离,保证节点即使不渲染也能对数据做校验 */ formMeta: { validateTrigger: ValidateTrigger.onChange, validate: { title: ({ value }) => (value ? undefined : 'Title is required'), }, /** * Render form */ render: () => ( <> {({ field }) =>
{field.value}
}
{({ field }) => } ) }, }, ]; ``` ### 5. 渲染节点 渲染节点用于添加样式、事件及表单渲染的位置 ```tsx pure title="components/base-node.tsx" import { useNodeRender } from '@flowgram.ai/fixed-layout-editor'; export const BaseNode = () => { /** * 提供节点渲染相关的方法 */ const nodeRender = useNodeRender(); /** * 只有在节点引擎开启时候才能使用表单 */ const form = nodeRender.form; return (
{ // 触发拖拽 nodeRender.startDrag(e); e.stopPropagation(); }} style={{ // BlockOrderIcon 表示为分支的第一个节点,BlockIcon 则表示整个 condition 的头部节点 ...(nodeRender.isBlockOrderIcon || nodeRender.isBlockIcon ? { width: 260 } : {}), outline: form?.state.invalid ? '1px solid red' : 'none', // 表单校验错误让边框标红 }} > { // 表单渲染通过 formMeta 生成 form?.render() }
); }; ``` ### 6. 添加工具 工具主要用于控制画布缩放等操作, 工具汇总在 `usePlaygroundTools` 中, 而 `useClientContext` 用于获取画布的上下文, 里边包含画布的核心模块如 `history` ```tsx pure title="components/tools.tsx" 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)}%
} ``` ### 7. 效果 import { FixedLayoutSimple } from '../../../../components';