npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

cyf-react-flow

v1.2.14

Published

一个基于 React 的流程图/思维导图编辑器组件库,支持:

Readme

cyf-react-flow

一个基于 React 的流程图/思维导图编辑器组件库,支持:

  • 节点拖拽、缩放、多选、对齐、分组
  • 普通连线(曲线/折线)、虚线、箭头、流动动画
  • 自由直线箭头(可旋转、改变长度)
  • 画布缩放、平移、背景网格

本仓库既是组件库源码,又可以本地直接 npm start 作为 Demo 运行。


安装

npm install cyf-react-flow
# 或
yarn add cyf-react-flow

要求:

  • React >=18
  • React DOM >=18

reactreact-dom 在库里是 peerDependencies,会复用你项目中的 React,不会再额外装一份。


快速上手

在你的项目中(例如 src/App.tsx):

import "cyf-react-flow/index.css";

import React, { useState } from "react";
import { FlowEditorReact, Nodes, Connections } from "cyf-react-flow";

const defaultNodeTypes = [
  { type: "start", label: "开始", width: 80, height: 80, color: "#3b82f6" },
  { type: "decision", label: "判断", width: 140, height: 60, color: "#f97316" },
  { type: "custom-card", label: "插槽", width: 50, height: 30, color: "#fbbf24" }
];

function App() {
  const [nodes, setNodes] = useState<Nodes[]>([]);
  const [connections, setConnections] = useState<Connections[]>([]);

  return (
    <div style={{ width: "100vw", height: "100vh" }}>
      <FlowEditorReact
        nodeTypes={defaultNodeTypes}
        nodesValue={nodes}
        connections={connections}
        isPview={false}
        isZoom
        isPanning
        isConnectionType
        isShowConfig
        isCzsm
        isRightConfig={false}
        onNodesChange={setNodes}
        onConnectionsChange={setConnections}
        renderNodeContent={(node) =>
          node.type === "custom-card" ? <div>自定义节点内容:{node.label}</div> : null
        }
      />
    </div>
  );
}

export default App;

关键点:

  • 必须单独引入样式:import "cyf-react-flow/index.css";
  • FlowEditorReact 是具名导出:import { FlowEditorReact } from "cyf-react-flow";
  • NodesConnections 为 TypeScript 类型导出,可选用。

我能做什么(能力速览)

使用一个组件 FlowEditorReact,你可以:

  • 作为「纯画布引擎」使用,只关心 nodes / connections 数据;
  • 通过插槽定制:
    • 左侧面板(renderLeft)—— 自己画节点列表 / 模板中心;
    • 节点内容(renderNodeContent)—— 在节点里放任意 React 组件;
    • 顶部工具栏(renderToolbar)—— 自己写放大缩小、保存、发布等按钮;
    • 右侧配置(renderRightConfig)—— 选中节点时在右侧展示配置表单;
  • 完全受控地管理数据:
    • onNodesChange / onConnectionsChange 拿到所有操作结果,存数据库或和后端同步。

下面的章节会按「从简单到进阶」的方式介绍这些能力。


API 说明

组件导出

入口文件:dist/index.build.js(源码对应 src/index.build.ts

导出内容:

  • FlowEditorReact:流程图编辑器组件
  • FlowEditorReactProps:组件 Props 类型
  • FlowEditorPublicApi:对外暴露的内部状态和方法集合类型
  • NodesConnections:节点和连线的数据结构类型

在 TS 项目中也可以这样单独引入类型:

import type { Nodes, Connections } from "cyf-react-flow";
// 或
// import type { Nodes, Connections } from "cyf-react-flow/type";

FlowEditorReact Props 概览

组件定义:src/ulits/FlowEditorReact.tsx:19-40

  • nodeTypes: NodeType[](必填)
    左侧节点面板的配置,控制能拖入画布的节点类型:

    type NodeType = {
      type: string;
      label: string;
      width: number;
      height: number;
      color: string;
    };
  • nodesValue?: Nodes[]
    初始节点列表。配合 onNodesChange 可以做成受控组件。

  • connections?: Connections[]
    初始连线列表。配合 onConnectionsChange 做受控。

  • onNodesChange?: (nodes: Nodes[]) => void
    节点变更时触发(新增、拖拽、缩放、多选修改等)。

  • containerColor?: #fff
    画外面背景颜色,默认 #fff

  • onConnectionsChange?: (connections: Connections[]) => void
    连线变更时触发(新增、删除、样式修改、自由箭头调整等)。

  • isPview?: boolean
    是否只读预览模式(true 时禁止编辑操作)。

  • editorConfig?: { hideSidebar?: boolean; disableBackspaceDelete?: boolean }
    编辑器行为配置:

    • hideSidebar:是否隐藏左侧节点面板(包含 nodeTypes 列表与 renderLeft 区域),默认 false
    • disableBackspaceDelete:是否禁用 Backspace 删除选中内容(用于避免误删/触发浏览器回退),默认 false
  • isPanning?: boolean
    是否允许平移画布。

  • panRequiresSpace?: boolean
    平移画布是否需要按住空格键。默认 false(空白处直接左键拖动即可平移,此时框选需要按住 Ctrl 在空白处拖拽);设为 true 时需要「按住空格 + 左键拖动」才能平移(此时空白处直接拖拽为框选)。

  • isZoom?: boolean
    是否允许缩放画布。

  • canvasScale?: number
    画布缩放比例(受控)。传入后以该值为准;配合 onCanvasScaleChange/onCanvasTransformChange 在外部保存并回灌即可实现“缩放受控”。

  • canvasOffset?: { x: number; y: number }
    画布平移偏移(受控,单位 px)。传入后以该值为准;配合 onCanvasOffsetChange/onCanvasTransformChange 可实现“拖拽位置受控”。

  • onCanvasScaleChange?: (scale: number) => void
    画布缩放变化时触发(滚轮缩放、调用内置缩放按钮、调用 methods.zoomIn/zoomOut/resetZoom 等)。

  • onCanvasOffsetChange?: (offset: { x: number; y: number }) => void
    画布平移偏移变化时触发(拖拽平移、缩放回推偏移等)。

  • onCanvasTransformChange?: (transform: { scale: number; offset: { x: number; y: number } }) => void
    画布缩放或平移任一变化时触发(便于一次性同步到外部 store)。

  • canvasSize?: { width: number; height: number }
    逻辑画布尺寸,和右侧配置联动。

  • width?: number / height?: number
    组件外层容器尺寸(一般保持全屏即可)。

  • bg?: string / bgColor?: string
    背景图片或背景颜色。

  • dotColor?: string / gridColor?: string
    点状背景的颜色(默认黑色)或网格背景的颜色(默认 rgba(0, 0, 0, 0.1))。

  • gridSize?: number / dotSpacing?: number
    网格间距或点状间距(默认值均为 40)。

  • backgroundType?: "grid" | "dots" | "color" | "none"
    画布背景类型:grid(网格)、dots(点状)、color(纯色)、none(无)。默认值为 grid

  • isConnectionType?: boolean
    是否显示顶部的「曲线 / 折线」切换。

  • isShowConfig?: boolean
    是否显示右侧配置面板。

  • isCzsm?: boolean
    是否在右侧显示操作说明列表。

  • isRightConfig?: boolean
    是否开启右侧自定义配置区域。

  • renderLeft?: () => ReactNode
    左侧自定义区域插槽(可以放自定义工具、额外可拖拽元素等)。

  • renderNodeContent?: (node: Nodes) => ReactNode
    节点内部插槽,可以按节点类型渲染不同内容。

  • renderRightConfig?: (selectNode: Nodes | null, helpers: { setNodeConfig: (id: string, key: string, value: any) => void }) => ReactNode
    选中单个节点时,右侧自定义配置区域。通过 helpers.setNodeConfig 可以直接修改当前节点的数据。

  • renderToolbar?: (api: FlowEditorPublicApi) => ReactNode
    顶部工具栏插槽。传入后会完全替换内置的「缩放 / 重置 / 撤销重做 / 图层 / 操作说明」工具条,并拿到内部公开的 statemethodsrefscontroller 以便在外部直接调用。

editorConfig 示例

<FlowEditorReact
  nodeTypes={defaultNodeTypes}
  nodesValue={nodes}
  connections={connections}
  editorConfig={{ hideSidebar: true, disableBackspaceDelete: true }}
/>

自定义左侧面板、顶部工具栏和右侧配置

1. 自定义左侧拖拽节点(renderLeft)

renderLeft 可以用来自定义左侧面板的内容,例如增加一个自定义节点入口,只要在 onDragStart 中写入节点配置即可:

<FlowEditorReact
  nodeTypes={defaultNodeTypes}
  nodesValue={nodes}
  connections={connections}
  renderLeft={() => (
    <div
      draggable
      onDragStart={(e) => {
        const nodeData = {
          type: "slotName",
          label: "自定义节点",
          width: 140,
          height: 45,
          color: "#fff",
        };
        e.dataTransfer.setData("text/plain", JSON.stringify(nodeData));
        e.dataTransfer.effectAllowed = "copy";
      }}
    >
      自定义节点
    </div>
  )}
  onNodesChange={setNodes}
  onConnectionsChange={setConnections}
/>

当你把这个元素拖到画布上并松手时,内部会从 dataTransfer.getData("text/plain") 解析出 nodeData,在鼠标位置创建一个对应的节点。

2. 自定义顶部工具栏并直接调用内部方法(renderToolbar)

renderToolbar 用于替换默认的顶部工具栏,它接收一个 FlowEditorPublicApi 对象,包含内部的 statemethodsrefscontroller,可以在外部直接调用内部方法或读取状态:

import {
  FlowEditorReact,
  type FlowEditorPublicApi,
} from "cyf-react-flow";

function App() {
  const [nodes, setNodes] = useState<Nodes[]>([]);
  const [connections, setConnections] = useState<Connections[]>([]);

  return (
    <FlowEditorReact
      nodeTypes={defaultNodeTypes}
      nodesValue={nodes}
      connections={connections}
      onNodesChange={setNodes}
      onConnectionsChange={setConnections}
      renderToolbar={(api: FlowEditorPublicApi) => {
        const { methods, state, controller } = api;
        const scale = state.canvasScale || 1;
        const selectNode =
          controller?.state?.selectNodes?.value || null;

        return (
          <div
            style={{
              display: "flex",
              alignItems: "center",
              gap: 8,
              padding: "4px 8px",
              borderBottom: "1px solid #eee",
            }}
          >
            <button onClick={() => methods.zoomIn()}>放大</button>
            <button onClick={() => methods.zoomOut()}>缩小</button>
            <button onClick={() => methods.resetZoom()}>重置</button>
            <button onClick={() => methods.undo()}>撤销</button>
            <button onClick={() => methods.redo()}>重做</button>
            <span style={{ marginLeft: 12 }}>
              当前缩放:{Math.round(scale * 100)}%
            </span>
            {selectNode && (
              <span style={{ marginLeft: 12 }}>
                选中节点:{selectNode.label}
              </span>
            )}
          </div>
        );
      }}
    />
  );
}

不传 renderToolbar 时,会使用默认工具栏;传入 renderToolbar 后,默认工具栏不再渲染,由你完全接管顶部区域。

上面的示例中也演示了如何在 App 层拿到「当前选中节点」:

  • 顶部展示:selectNode && <span>选中节点:{selectNode.label}</span>
  • 或在按钮点击时打印:console.log("当前选中节点:", selectNode)

3. 自定义右侧配置面板(renderRightConfig)

renderRightConfig 接收两个参数:

renderRightConfig?: (
  selectNode: Nodes | null,
  helpers: { setNodeConfig: (id: string, key: string, value: any) => void }
) => ReactNode;
  • selectNode:当前选中的单个节点对象(没有选中或多选时为 null
  • helpers.setNodeConfig(id, key, value):直接修改指定节点某个字段的值,并触发画布和外部 onNodesChange 更新

例如在右侧直接修改“选中节点”的名称:

<FlowEditorReact
  nodeTypes={defaultNodeTypes}
  nodesValue={nodes}
  connections={connections}
  isRightConfig
  renderRightConfig={(node, { setNodeConfig }) => {
    if (!node) return null;
    const label = node.label ?? "";
    return (
      <div style={{ padding: 8 }}>
        <div style={{ marginBottom: 4 }}>节点名称:</div>
        <input
          style={{ width: "100%" }}
          placeholder="请输入节点名称"
          value={label}
          onChange={(e) => {
            const v = e.target.value;
            setNodeConfig(node.id, "label", v);
          }}
        />
      </div>
    );
  }}
  onNodesChange={setNodes}
  onConnectionsChange={setConnections}
/>

上面的代码会直接修改“当前选中节点”的 label 字段,画布上的节点文字会实时更新,无需手动刷新或重新挂载组件。

典型用法是:

  • 在不同节点类型下渲染不同的配置表单(条件节点、审批节点、子流程节点等);
  • 把复杂配置收集到 node.obj 中,外部通过 onNodesChange 拿到完整数据;
  • 和你的业务后端协议对接,持久化整个流程配置。

数据结构

类型定义:src/ulits/type.ts(打包到 dist/ulits/type.d.ts

Nodes

type Nodes = {
  id: string;
  x: number;
  y: number;
  width: number;
  height: number;
  type: string;
  label: string;
  color?: string;
  src?: string;
  zIndex?: number;
  obj?: any;
  slotName?: string;
  target?: string;
  groupId?: string;
};

Connections

type Connections = {
  id: string | number;
  source: string;
  target: string;
  type?: string;
  zIndex?: number;
  isFree?: boolean;
  freeSource?: { x: number; y: number };
  freeTarget?: { x: number; y: number };
  stroke?: string;
  strokeWidth?: number;
  dashed?: boolean;
  arrow?: "none" | "start" | "end" | "both";
  arrowStyle?: string;
  animated?: boolean;
  animationSpeed?: number;
  hitWidth?: number;
  sourceAnchor?: string;
  targetAnchor?: string;
  label?: string;
  bendOffsetX?: number;
  bendTopY?: number;
  bendBottomY?: number;
  cornerAngleDeg?: number;
  rotationDeg?: number;
};

FlowEditorReactProps 常用属性

| 属性名 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | nodesValue | Nodes[] | [] | 节点数据 | | connections | Connections[] | [] | 连线数据 | | backgroundType | "grid" \| "dots" \| "color" \| "none" | "grid" | 背景类型 | | dotColor | string | "#000000" | 点状背景颜色 | | gridColor | string | "rgba(0, 0, 0, 0.1)" | 网格背景颜色 | | gridSize | number | 40 | 网格间距 | | dotSpacing | number | 40 | 点状背景间距 | | isShowNodeLabel | boolean | false | 是否显示节点上方的 Label | | isPview | boolean | false | 是否为预览模式(不可编辑) |

你可以直接把 onNodesChange / onConnectionsChange 返回的数组序列化存数据库,后续再回填到 nodesValue / connections 即可还原画布。


常见问题

1. Invalid hook call / Hooks can only be called inside of the body of a function component

如果在你的项目里使用 cyf-react-flow 出现这个错误,通常是因为页面中存在两份 React:

  • 你的项目本身有一份 react
  • 另一份 react 来自某个库自己的 dependencies

本库已经将 reactreact-dom 挪到了 peerDependencies,不会再额外安装一份。如果你遇到这个问题,可以在你的项目根目录运行:

npm ls react
npm ls react-dom

确保只出现一份 React 版本,如果有多份,请调整依赖或使用 resolutions / overrides 统一。

2. 样式没有生效

确认是否在入口引入了样式:

import "cyf-react-flow/index.css";

如果你的打包工具对来自 node_modules 的 CSS 有特殊配置,需要保证可以正常处理第三方包的样式。