](/api/model/interaction#连线) | -|
+| connectionEdgeOptions | 连线选项中的自定义边 | [EdgeOptions](#connectionEdgeOptions) | - |
+| embedable | 是否允许节点之间组合 | boolean | false |
+| embedOptions | 节点组合配置 | [embedOptions](#embedOptions-参数配置) | - |
+| restrict | 是否限制节点的移动范围 | boolean | false |
+| restrictOptions | 限制节点移动范围配置 | [restrict配置](#restrictOptions-节点的移动范围) | - |
+| selectOptions | 框选配置 | [Selection配置](/tutorial/plugins/selection#配置) | - |
+| keyboardOptions | 快捷键配置 | [Keyboard配置](/tutorial/plugins/keyboard#配置) | - |
+| defaultHighlightOptions | 默认高亮选项,当以下高亮配置缺省时被使用 | [HighlightManager.Options](#高亮-HighlightManager.Options) | - |
+| embedHighlightOptions | 拖动节点进行嵌入操作过程中,节点可以被嵌入时被使用 |[HighlightManager.Options](#高亮-HighlightManager.Options) | - |
+| nodeAvailableHighlightOptions | 连线过程中,节点可以被链接时被使用 |[HighlightManager.Options](#高亮-HighlightManager.Options) | - |
+| magnetAvailableHighlightOptions | 连线过程中,连接桩可以被链接时被使用 |[HighlightManager.Options](#高亮-HighlightManager.Options) | - |
+| magnetAdsorbedHighlightOptions | 连线过程中,自动吸附到连接桩时被使用 |[HighlightManager.Options](#高亮-HighlightManager.Options) | - |
diff --git a/sites/x6-sites/docs/xflow/components/grid.md b/sites/x6-sites/docs/xflow/components/grid.md
new file mode 100644
index 00000000000..1c98a21ae58
--- /dev/null
+++ b/sites/x6-sites/docs/xflow/components/grid.md
@@ -0,0 +1,78 @@
+---
+title: Grid 网格
+order: 4
+redirect_from:
+ - /zh/docs
+ - /zh/docs/xflow
+ - /zh/docs/xflow/components
+---
+
+画布的网格
+
+## 基础用法
+
+:::info{title="注意"}
+
+ `` 组件只能在 `` 组件之内方可正常使用
+
+:::
+
+引入 `` 组件后, 即可设置 `` 的画布网格
+
+```tsx
+
+ ...
+
+
+```
+
+## 网格大小
+
+通过设置 `size` 属性可以控制网格大小,网格默认大小为 10px,渲染节点时表示以 10 为最小单位对齐到网格,如位置为 { x: 24, y: 38 }的节点渲染到画布后的实际位置为 { x: 20, y: 40 }, 移动节点时表示每次移动最小距离为 10px。
+
+## 网格隐藏
+
+添加 `visible` 属性即可让网格处于隐藏状态
+
+## 点状网格
+
+`type` 属性为 `dot` 的点状网格, 通过 `options` 属性来设置网格颜色和宽度
+
+
+
+## 固定网点大小的点状网格
+
+`type` 属性为 `fixedDot` 的固定网点大小的点状网格类型, 通过 `options` 属性来设置网格颜色和宽度。
+注意: 当画布的缩放比例小于 1 时,网点大小随着画布缩放比例缩放,当画布缩放比例大于 1 时,网点大小为给定的 thickness 的值。
+
+
+
+## 网状网格
+
+`type` 属性为 `mesh` 的网状网格, 通过 `options` 属性来设置网格颜色和宽度
+
+
+
+## 双线网状网格
+
+`type` 属性为 `doubleMesh` 的双网线网格, 通过 `options` 属性来设置主次网格线的颜色和宽度
+
+
+## API
+
+### Grid
+
+| 参数名 | 描述 | 类型 | 默认值 |
+|--------|------|------|-------|
+| visible | 网格是否显示 | boolean | `true` |
+| size | 网格大小 | number | 10 |
+| type | 网格类型 | `dot` \| `fixedDot` \| `mesh` \| `doubleMesh` | - |
+| options | 网格类型对应的网格参数 | [args](#options-对应的-args-参数如下) \| [args](#options-对应的-args-参数如下)[] | - |
+
+options 对应的 args 参数如下:
+
+| 参数名 | 描述 | 类型 | 默认值 |
+|--------|------|------|-------|
+|color | 网格线颜色 | string | - |
+|thickness | 网格线宽度或网点大小 | string | -|
+|factor |主次网格线间隔, 仅在 `type` 类型为 `doubleMesh` 时生效 |number | - |
diff --git a/sites/x6-sites/docs/xflow/components/history.md b/sites/x6-sites/docs/xflow/components/history.md
new file mode 100644
index 00000000000..45cc67cf35d
--- /dev/null
+++ b/sites/x6-sites/docs/xflow/components/history.md
@@ -0,0 +1,35 @@
+---
+title: History 撤销重做
+order: 6
+redirect_from:
+ - /zh/docs
+ - /zh/docs/xflow
+ - /zh/docs/xflow/components
+---
+
+元素操作的撤销与重做
+
+## 基础用法
+
+:::info{title="注意"}
+
+ `` 组件只能在 `` 组件之内方可正常使用
+
+:::
+
+引入 `History` 组件, 配合 [useHistory](/xflow/hooks/use-history) 可快速实现元素操作的撤销与重做功能
+
+```tsx
+
+ ...
+
+
+```
+
+
+
+## API
+
+### History
+
+详细配置请参考 [X6 配置](/tutorial/plugins/history#配置)
diff --git a/sites/x6-sites/docs/xflow/components/minimap.md b/sites/x6-sites/docs/xflow/components/minimap.md
new file mode 100644
index 00000000000..06729f1045d
--- /dev/null
+++ b/sites/x6-sites/docs/xflow/components/minimap.md
@@ -0,0 +1,47 @@
+---
+title: Minimap 小地图
+order: 7
+redirect_from:
+ - /zh/docs
+ - /zh/docs/xflow
+ - /zh/docs/xflow/components
+---
+
+画布小地图
+
+## 基础用法
+
+:::info{title="注意"}
+
+ `` 组件只能在 `` 组件之内方可正常使用
+
+:::
+
+引入 `Minimap` 组件, 可快速实现画布的小地图功能
+
+```tsx
+
+ ...
+
+
+```
+
+
+
+## API
+
+### Minimap
+
+| 参数名 | 描述 | 类型 | 默认值 |
+|--------|------|------|-------|
+| style | 语义化结构 style | CSSProperties | - |
+| classNames | 语义化结构 class | string | - |
+| simple|是否展示简单试图|boolean|`false`|
+| simpleNodeBackground|简单视图下的节点背景色|string|-|
+| minScale|最小缩放比例|number |`0.01` |
+|maxScale|最大缩放比例|number |`16` |
+|width|小地图的宽度|number|`300`|
+|height|小地图的高度 |number|`200`|
+|padding|小地图容器的 padding 边距|number|`10`|
+|scalable|是否可缩放 |boolean|`true`|
+|graphOptions|创建小地图 Graph 的选项|Graph.Options|`null`|
diff --git a/sites/x6-sites/docs/xflow/components/snapline.md b/sites/x6-sites/docs/xflow/components/snapline.md
new file mode 100644
index 00000000000..18414de2b9e
--- /dev/null
+++ b/sites/x6-sites/docs/xflow/components/snapline.md
@@ -0,0 +1,35 @@
+---
+title: Snapline 对齐线
+order: 8
+redirect_from:
+ - /zh/docs
+ - /zh/docs/xflow
+ - /zh/docs/xflow/components
+---
+
+画布对齐线, 移动节点排版的辅助工具
+
+## 基础用法
+
+:::info{title="注意"}
+
+ `` 组件只能在 `` 组件之内方可正常使用
+
+:::
+
+引入 `` 后, 即可开启节点对齐辅助线
+
+```tsx
+
+ ...
+
+
+```
+
+
+
+## API
+
+### Snapline
+
+详细配置请参考 [Snapline 配置](/tutorial/plugins/snapline#配置)
diff --git a/sites/x6-sites/docs/xflow/components/transform.md b/sites/x6-sites/docs/xflow/components/transform.md
new file mode 100644
index 00000000000..f3a8f5504a1
--- /dev/null
+++ b/sites/x6-sites/docs/xflow/components/transform.md
@@ -0,0 +1,40 @@
+---
+title: Transform 图形变换
+order: 9
+redirect_from:
+ - /zh/docs
+ - /zh/docs/xflow
+ - /zh/docs/xflow/components
+---
+
+调整节点大小和节点旋转角度
+
+## 基础用法
+
+:::info{title="注意"}
+
+ `` 组件只能在 `` 组件之内方可正常使用
+
+:::
+
+使用 `` 组件 开启调整节点能力
+
+```tsx
+
+ ...
+
+
+```
+
+Transform 组件的 `resizing` 和 `rotating` 属性设置为 `true` ,即可开启调整节点大小及旋转角度, 也可以对 `resizing` 和 `rotating` 属性进行配置
+
+
+
+## API
+
+### Transform
+
+| 参数名 | 描述 | 类型 | 默认值 |
+|--------|------|------|-------|
+|resizing|调整节点尺寸配置| [Transform.Resizing](/tutorial/plugins/transform#调整尺寸) \| `boolean` | - |
+|rotating|调整节点角度配置| [Transform.Rotating](/tutorial/plugins/transform#调整角度) \| `boolean` | - |
diff --git a/sites/x6-sites/docs/xflow/components/wrapper.md b/sites/x6-sites/docs/xflow/components/wrapper.md
new file mode 100644
index 00000000000..2dd5f12d161
--- /dev/null
+++ b/sites/x6-sites/docs/xflow/components/wrapper.md
@@ -0,0 +1,26 @@
+---
+title: Wrapper 包裹组件
+order: 1
+redirect_from:
+ - /zh/docs
+ - /zh/docs/xflow
+ - /zh/docs/xflow/components
+---
+
+画布包裹组件, 只有当画布实例存在的时候,才去渲染 `children`
+
+## 基础用法
+
+```tsx
+
+ {children}
+
+```
+
+## API
+
+### Wrapper
+
+| 参数名 | 描述 | 类型 | 默认值 |
+|--------|------|------|-------|
+| children| 渲染元素 | ReactNode | - |
diff --git a/sites/x6-sites/docs/xflow/components/xflow.md b/sites/x6-sites/docs/xflow/components/xflow.md
new file mode 100644
index 00000000000..4facca55dde
--- /dev/null
+++ b/sites/x6-sites/docs/xflow/components/xflow.md
@@ -0,0 +1,26 @@
+---
+title: XFlow 根组件
+order: 0
+redirect_from:
+- /zh/docs
+- /zh/docs/xflow
+- /zh/docs/xflow/components
+---
+
+XFlow 的根组件,提供画布消费上下文的默认环境。
+
+## 基础用法
+
+```tsx
+
+ {children}
+
+```
+
+## API
+
+### XFlow
+
+| 参数名 | 描述 | 类型 | 默认值 |
+|--------|------|------|-------|
+| children| 渲染元素 | ReactNode | - |
diff --git a/sites/x6-sites/docs/xflow/guide/introduction.md b/sites/x6-sites/docs/xflow/guide/introduction.md
new file mode 100644
index 00000000000..2c4221f2a8b
--- /dev/null
+++ b/sites/x6-sites/docs/xflow/guide/introduction.md
@@ -0,0 +1,48 @@
+---
+title: 简介
+order: 0
+redirect\_from:
+ - /zh/docs
+ - /zh/docs/tutorial
+
+---
+
+:::info{title="你正在阅读的是 XFlow 2.0 的文档"}
+
+- XFlow 1.x 将于 2023 年 12 月 31 日停止维护。详见 [XFlow 1.x 教程](https://xflow.antv.vision/zh-CN/docs/tutorial/intro/about)
+
+:::
+
+## 什么是 XFlow
+
+XFlow 是基于 X6 图编辑引擎、面向 React 技术栈用户的应用级解决方案, 像使用 React 组件一样开发图编辑应用,帮助你高效地开发用户界面。无论是简单还是复杂的界面,XFlow 都可以胜任
+
+## XFlow 特性
+
+🚀 极简易用: 没有复杂概念,像使用 React 组件 一样使用
+🛠️ 配套齐全: 内置常用图编辑配套组件
+📦 开箱即用: 上层提供 DAG 图、ER 图、流程图等成熟解决方案
+
+## 如何交流
+
+如果您有任何的问题、建议、反馈或者交流意愿,可以通过如下方式联系我们:
+
+- 官方推荐: [GitHub issues](https://github.com/antvis/XFlow/issues/new/choose)
+- 邮件:
+- 语雀专栏:
+
+
+
+
+
+## 如何参与贡献
+
+如果您在使用的过程中碰到问题,可以先通过 [issues](https://github.com/antvis/x6/issues) 看看有没有类似的 bug 或者建议。
+
+在你报告一个 bug 之前,请先确保已经搜索过已有的 issue 和阅读了我们的 [FAQ]("/xflow/faq")。
+
+如需提交代码,请遵从我们的[贡献指南](https://github.com/antvis/X6/blob/master/CONTRIBUTING.zh-CN.md)。
+
+
+
+
diff --git a/sites/x6-sites/docs/xflow/guide/quick-start.md b/sites/x6-sites/docs/xflow/guide/quick-start.md
new file mode 100644
index 00000000000..1bda6aab3e2
--- /dev/null
+++ b/sites/x6-sites/docs/xflow/guide/quick-start.md
@@ -0,0 +1,52 @@
+---
+title: 快速上手
+order: 1
+redirect_from:
+ - /zh/docs
+ - /zh/docs/tutorial
+ - /zh/docs/tutorial/basic
+---
+
+# 安装
+
+:::info{title="前提条件"}
+
+- 熟悉命令行
+- 已安装 18.0 或更高版本的 Node.js
+
+:::
+
+## 通过包管理器
+
+```bash [NPM]
+# 使用 npm
+$ npm install @antv/xflow --save
+
+# 使用yarn
+$ yarn add @antv/xflow
+
+# 使用pnpm
+$ pnpm add @antv/xflow
+```
+
+## 通过 CDN
+
+你可以借助 script 标签直接通过 CDN 来使用 XFlow:
+
+```html
+
+```
+
+这里我们使用了 [unpkg](https://unpkg.com/@antv/xflow/dist/index.umd.js),但你也可以使用任何提供 npm 包服务的 CDN,例如 [jsdelivr](https://cdn.jsdelivr.net/npm/@antv/xflow/dist/index.umd.js)。当然你也可以下载此文件并自行提供服务。
+
+对于生产环境, 我们推荐使用一个明确的版本号, 以避免新版本升级造成不可预期的破坏, 例如:
+
+-
+
+-
+
+# 基础使用
+
+接下来我们就一起使用 XFlow 来构建一个简单的图形应用,来体验一下 XFlow 的魅力吧。
+
+
\ No newline at end of file
diff --git a/sites/x6-sites/docs/xflow/hooks/useClipboard.md b/sites/x6-sites/docs/xflow/hooks/useClipboard.md
new file mode 100644
index 00000000000..43edfbae4c0
--- /dev/null
+++ b/sites/x6-sites/docs/xflow/hooks/useClipboard.md
@@ -0,0 +1,56 @@
+---
+title: useClipboard
+order: 4
+redirect_from:
+ - /zh/docs
+ - /zh/docs/xflow
+ - /zh/docs/xflow/hooks
+---
+
+用于复制粘贴节点和边的 Hook
+
+## 基础用法
+
+```tsx
+ const { copy, paste, cut } = useClipboard();
+```
+
+## API
+
+```tsx
+
+const {
+ copy: (ids, copyOptions) => void,
+ paste: (ids, cutOptions) => void,
+ cut: (pasteOptions) => void
+} = useClipboard();
+
+```
+
+## 返回值
+
+| 参数 | 描述 | 类型 |
+|--------|------|------|
+| copy | 复制元素 | (ids: string[], copyOptions?: [CopyOptions](#CopyOptions-参数如下)) => void |
+| paste | 渲染元素 | (ids: string[], cutOptions?: [CopyOptions](#CopyOptions-参数如下)) => void |
+| cut | 渲染元素 | (pasteOptions?: [PasteOptions](#PasteOptions-参数如下)) => [Cell](/zh/docs/api/model/cell#属性)[] |
+
+CopyOptions 参数如下
+
+| 参数 | 描述 | 类型 | 默认值 |
+|--------|------|------|-------|
+| deep | 是否递归复制所有子节点/边。 | `boolean` | - |
+| useLocalStorage | 是否将复制的节点/边保存在 `localStorage` 中 | `boolean` | - |
+
+PasteOptions 参数如下
+
+| 参数 | 描述 | 类型 | 默认值 |
+|--------|------|------|-------|
+| offset | 粘贴到画布的节点/边的偏移量 | `number` \| `{ dx: number; dy: number }` | 20 |
+| useLocalStorage | 是否使用 `localStorage` 中的节点/边 | `boolean` | - |
+| nodeProps | 粘贴到画布的节点的额外属性 | `Node.Properties` | - |
+| edgeProps | 粘贴到画布的边的额外属性 | `Edge.Properties` | - |
+
+## 参数
+
+无
diff --git a/sites/x6-sites/docs/xflow/hooks/useDnd.md b/sites/x6-sites/docs/xflow/hooks/useDnd.md
new file mode 100644
index 00000000000..3ac9f585a96
--- /dev/null
+++ b/sites/x6-sites/docs/xflow/hooks/useDnd.md
@@ -0,0 +1,60 @@
+---
+title: useDnd
+order: 3
+redirect_from:
+ - /zh/docs
+ - /zh/docs/xflow
+ - /zh/docs/xflow/hooks
+---
+
+快速实现节点拖拽的 Hook
+
+## 基础用法
+
+```tsx
+ const { startDrag } = useDnd();
+```
+
+
+
+## API
+
+```tsx
+ const {
+ startDrag: (n, e) => void
+ } = useDnd(options: Options);
+```
+
+## 返回值
+
+| 参数 | 描述 | 类型 | 默认值 |
+|--------|------|------|-----|
+| startDrag | 拖拽节点函数 | (n: [NodeOptions](#use-dnd-startDrag-options), e: React.MouseEvent) => void |-|
+
+NodeOptions 除了继承 Node 类型之外,还拥有两个属性
+
+Node相关文档请参考 [Node](/api/model/node)
+
+```tsx
+interface NodeOptions extends Node {
+ selected?: boolean; // 是否被选中
+ draggable?: boolean; // 是否可拖拽
+}
+```
+
+## 参数
+
+| 参数 | 描述 | 类型 | 默认值 |
+|--------|------|------|-------|
+| options| 拖拽配置 | [Options](#use-dnd-options) | - |
+
+使用 `useDnd` 进行拖拽的时候,可以对其进行配置
+
+Options 类型如下:
+
+| 参数 | 描述 | 类型 | 默认值 |
+|--------|------|------|-----|
+| scaled | 是否应该缩放拖动节点 | `boolean` | `false` |
+| dndContainer | 如果设置 `dndContainer`,在 `dndContainer` 上放开鼠标不会放置节点,常用于 `dnd` 容器处于画布上面的场景 | `HTMLElement` | - |
+| draggingContainer | 自定义拖拽画布容器 | `HTMLElement` | `document.body` |
+| validateNode | 是否应该缩放拖动节点 | `(droppingNode: Node, options: ValidateNodeOptions) => boolean | Promins` | - |
diff --git a/sites/x6-sites/docs/xflow/hooks/useExport.md b/sites/x6-sites/docs/xflow/hooks/useExport.md
new file mode 100644
index 00000000000..55741b938c8
--- /dev/null
+++ b/sites/x6-sites/docs/xflow/hooks/useExport.md
@@ -0,0 +1,40 @@
+---
+title: useExport
+order: 5
+redirect_from:
+ - /zh/docs
+ - /zh/docs/xflow
+ - /zh/docs/xflow/hooks
+---
+
+画布内容通过图片形式导出的 Hook
+
+## 基础用法
+
+```tsx
+ const { exportPNG, exportJPEG, exportSVG } = useExport();
+```
+
+## API
+
+```tsx
+
+const {
+ exportPNG: (fileName, options) => void,
+ exportJPEG: (fileName, options) => void,
+ exportSVG: (fileName, options) => void
+} = useExport();
+
+```
+
+## 返回值
+
+| 参数 | 描述 | 类型 | 默认值
+|--------|------|------| ------|
+| exportPNG | 导出PNG | (fileName:string, options: [Export.ToImageOptions]((/tutorial/plugins/export#graphexportsvg)) = {}) => void |`('chart', {}) => void`|
+| exportJPEG | 导出JPEG | (fileName:string, options: [Export.ToImageOptions]((/tutorial/plugins/export#graphexportsvg)) = {}) => void |`('chart', {}) => void`|
+| exportSVG | 导出SVG | (fileName:string, options: [Export.ToSVGOptions]((/tutorial/plugins/export#graphexportsvg)) = {}) => void |`('chart', {}) => void`|
+
+## 参数
+
+无
diff --git a/sites/x6-sites/docs/xflow/hooks/useGraphEvent.md b/sites/x6-sites/docs/xflow/hooks/useGraphEvent.md
new file mode 100644
index 00000000000..603f3e72e1a
--- /dev/null
+++ b/sites/x6-sites/docs/xflow/hooks/useGraphEvent.md
@@ -0,0 +1,42 @@
+---
+title: useGraphEvent
+order: 2
+redirect_from:
+ - /zh/docs
+ - /zh/docs/xflow
+ - /zh/docs/xflow/hooks
+---
+
+画布事件监听的 Hook
+
+## 基础用法
+
+```tsx
+ useGraphEvent('blank:click', () => { ... });
+```
+
+下面是使用 `useGraphEvent` 的简单示例
+监听节点点击事件,随机改变节点颜色
+
+
+## API
+
+```tsx
+ useGraphEvent(
+ name:T,
+ callback: (args: EventArgs[T]) => void
+);
+```
+
+## 返回值
+
+无
+
+## 参数
+
+具体监听的事件类型请参考 [X6-事件](/tutorial/basic/events)
+| 参数 | 描述 | 类型 | 默认值 |
+|--------|------|------|-------|
+| name| 监听的事件 | `T` | - |
+| callback| 监听事件的回调 | `(args: EventArgs[T]) => void` | - |
+
diff --git a/sites/x6-sites/docs/xflow/hooks/useGraphInstance.md b/sites/x6-sites/docs/xflow/hooks/useGraphInstance.md
new file mode 100644
index 00000000000..e2edf796550
--- /dev/null
+++ b/sites/x6-sites/docs/xflow/hooks/useGraphInstance.md
@@ -0,0 +1,34 @@
+---
+title: useGraphInstance
+order: 0
+redirect_from:
+ - /zh/docs
+ - /zh/docs/xflow
+ - /zh/docs/xflow/hooks
+---
+
+用于获取画布实例的 Hook
+
+## 基础用法
+
+```tsx
+ const graph = useGraphInstance();
+```
+
+## API
+
+```tsx
+
+ const graph: Graph = useGraphInstance()
+
+```
+
+## 返回值
+
+| 参数 | 描述 | 类型 | 默认值 |
+|--------|------|------|-------|
+| graph | 画布实例 | `Graph` | `null` |
+
+## 参数
+
+无
diff --git a/sites/x6-sites/docs/xflow/hooks/useGraphStore.md b/sites/x6-sites/docs/xflow/hooks/useGraphStore.md
new file mode 100644
index 00000000000..34e7cb06144
--- /dev/null
+++ b/sites/x6-sites/docs/xflow/hooks/useGraphStore.md
@@ -0,0 +1,56 @@
+---
+title: useGraphStore
+order: 1
+redirect_from:
+ - /zh/docs
+ - /zh/docs/xflow
+ - /zh/docs/xflow/hooks
+---
+
+获取当前画布 store 以及改变 store 的 Hook
+
+## 基础用法
+
+```tsx
+ const nodes = useGraphStore((state) => state.nodes);
+```
+
+使用 `useGraphStore` 可以方便的对画布的数据进行增删改查
+下面是使用 `useGraphStore` 添加节点和删除节点的简单示例
+
+
+## API
+
+```tsx
+
+ useGraphStore(selector: (state: U) => T): U
+
+```
+
+## 返回值
+
+| 参数 | 描述 | 类型 | 默认值 |
+|--------|------|------|-------|
+| `U` | 画布store的action | [Options](#Options-参数如下) | - |
+
+## 参数
+
+| 参数 | 描述 | 类型 | 默认值 |
+|--------|------|------|-------|
+| selector| 获取store的action的函数 | (state: [Options](#Options-参数如下)) => [Options](#Options-参数如下) | - |
+
+Options 参数如下
+
+| 参数 | 描述 | 类型 | 默认值 |
+|--------|------|------|-------|
+| nodes | 画布所有节点 | [NodeOptions](/api/model/node)[] | - |
+| edges | 画布所有边 | [EdgeOptions](/api/model/edge)[] | - |
+| changeList | store操作记录 | (`init` \| `addNodes` \| `removeNodes` \| `updateNode` \| `addEdges` \| `removeEdges` \| `updateEdge` )[] | - |
+| initData | 初始化数据 | `(data: {nodes: NodeOptions[], edges: EdgeOptions[] }, options?: {silent?: boolean}) => void` | - |
+| addNodes | 添加节点 | `(ns: NodeOptions[], options?: {silent?: boolean}) => void` | - |
+| removeNodes | 移除节点 | `(ids: string[], options?: {silent?: boolean}) => void` | - |
+| updateNode | 更新节点 | `(id: string, data: UpdateNodeDataOrFn, options?: {silent?: boolean}) => void` | - |
+| addEdges | 添加边 | `(es: EdgeOptions[], options?: {silent?: boolean}) => void` | - |
+| removeEdges | 移除边 | `(ids: string[], options?: {silent?: boolean}) => void` | - |
+| updateEdge | 更新边 | `(id: string, data: UpdateEdgeDataOrFn, options?: {silent?: boolean}) => void` | - |
+| clearChangeList | 情况操作记录 | `() => void` | - |
diff --git a/sites/x6-sites/docs/xflow/hooks/useHistory.md b/sites/x6-sites/docs/xflow/hooks/useHistory.md
new file mode 100644
index 00000000000..e559820714d
--- /dev/null
+++ b/sites/x6-sites/docs/xflow/hooks/useHistory.md
@@ -0,0 +1,42 @@
+---
+title: useHistory
+order: 6
+redirect_from:
+ - /zh/docs
+ - /zh/docs/xflow
+ - /zh/docs/xflow/hooks
+---
+
+用于实现画布历史记录的 Hook
+
+## 基础用法
+
+```tsx
+ const { undo, redo, canUndo, canRedo } = useHistory();
+```
+
+## API
+
+```tsx
+
+const {
+ undo: (options) => Graph | null,
+ redo: (options) => Graph | null,
+ canUndo: boolean,
+ canRedo: boolean
+} = useHistory();
+
+```
+
+## 返回值
+
+| 参数 | 描述 | 类型 | 默认值 |
+|--------|------|------|-------|
+| undo| 撤销 `options` 将被传递到事件回调中 | `(options?: KeyValue) => Graph` \| `null` | - |
+| redo| 重做 `options` 将被传递到事件回调中| `(options?: KeyValue) => Graph` \| `null` | - |
+| canUndo| 是否可以撤销 | `boolean` | `false` |
+| canRedo| 是否可以重做 | `boolean` | `false` |
+
+## 参数
+
+无
diff --git a/sites/x6-sites/docs/xflow/hooks/useKeyboard.md b/sites/x6-sites/docs/xflow/hooks/useKeyboard.md
new file mode 100644
index 00000000000..231dccc242b
--- /dev/null
+++ b/sites/x6-sites/docs/xflow/hooks/useKeyboard.md
@@ -0,0 +1,46 @@
+---
+title: useKeyboard
+order: 7
+redirect_from:
+ - /zh/docs
+ - /zh/docs/xflow
+ - /zh/docs/xflow/hooks
+---
+
+实现画布快捷键的 Hook
+
+## 基础用法
+
+```tsx
+ useKeyboard('ctrl+c', () => { ... });
+```
+
+下面是使用 `useKeyboard` 的简单示例
+`Ctrl + C` 复制节点
+`Ctrl + V` 粘贴节点
+
+
+
+## API
+
+```tsx
+
+useKeyboard(
+ key: string | string[],
+ callback: (e) => void,
+ action?: 'keypress' | 'keydown' | 'keyup'
+)
+
+```
+
+## 返回值
+
+无
+
+## 参数
+
+| 参数 | 描述 | 类型 | 默认值 |
+|--------|------|------|-------|
+| key | 绑定的快捷键 | `string` \| `string[]` | - |
+| callback | 执行快捷键的回调 | `(e: KeyboardEvent) => void` | - |
+| action | 触发类型 | `keypress` \| `keydown` \| `keyup` | - |
diff --git a/sites/x6-sites/docs/xflow/store.md b/sites/x6-sites/docs/xflow/store.md
new file mode 100644
index 00000000000..d41ef8e5476
--- /dev/null
+++ b/sites/x6-sites/docs/xflow/store.md
@@ -0,0 +1,88 @@
+---
+title: Store
+order: 4
+redirect_from:
+ - /zh/docs
+ - /zh/docs/xflow
+---
+
+xflow 对画布的数据进行了统一的管理,整个画布的数据存在一个 `store` 中,这让开发变得非常容易
+
+你可以使用 [useGraphStore]((/xflow/hooks/use-graph-store)) 方便快捷的操作 `store` , 从而更新画布数据, 实现更新画布
+
+## 初始化状态
+
+### `initData(data, options)`
+
+这个函数用于初始化状态管理器,设置初始的节点和边。
+
+参数:
+
+- `data`:一个对象,包含`nodes`和`edges`两个数组,分别存储节点和边的数据。
+- `options`:一个可选的对象,当设置为`{ silent: true }`时,初始化的操作不会被记录在变动列表`changeList`中。
+
+## 节点操作
+
+### `addNodes(ns, options)`
+
+添加新的节点到状态管理器中。
+
+参数:
+
+- `ns`:一个节点对象数组。
+- `options`:一个可选的对象。当`{ silent: true }`时,添加操作不会被记录在变动列表中。
+
+### `removeNodes(ids, options)`
+
+通过ID数组移除节点。
+
+参数:
+
+- `ids`:一个包含节点ID的数组。
+- `options`:一个可选的对象。当`{ silent: true }`时,移除操作不会被记录在变动列表中。
+
+### `updateNode(id, data, options)`
+
+通过ID更新某个节点。不允许修改节点的`id`或`shape`属性。
+
+参数:
+
+- `id`:要更新的节点的ID。
+- `data`:一个对象或者一个函数,包含要更新的数据。
+- `options`:一个可选的对象。当`{ silent: true }`时,更新操作不会被记录在变动列表中。
+
+## 边操作
+
+### `addEdges(es, options)`
+
+添加新的边到状态管理器中。
+
+参数:
+
+- `es`:一个边对象数组。
+- `options`:一个可选的对象。当`{ silent: true }`时,添加操作不会被记录在变动列表中。
+
+### `removeEdges(ids, options)`
+
+通过ID数组移除边。
+
+参数:
+
+- `ids`:一个包含边ID的数组。
+- `options`:一个可选的对象。当`{ silent: true }`时,移除操作不会被记录在变动列表中。
+
+### `updateEdge(id, data, options)`
+
+通过ID更新某个边。不允许修改边的`id`或`shape`属性。
+
+参数:
+
+- `id`:要更新的边的ID。
+- `data`:一个对象或者一个函数,包含要更新的数据。
+- `options`:一个可选的对象。当`{ silent: true }`时,更新操作不会被记录在变动列表中。
+
+## 变动列表操作
+
+### `clearChangeList()`
+
+清空变动列表`changeList`,这个列表记录了所有非静默(非silent)的操作。
diff --git a/sites/x6-sites/package.json b/sites/x6-sites/package.json
index 49956c53b25..dadaa8475bf 100644
--- a/sites/x6-sites/package.json
+++ b/sites/x6-sites/package.json
@@ -31,15 +31,16 @@
"@antv/x6-plugin-transform": "^2.x",
"@antv/x6-react-components": "^2.x",
"@antv/x6-react-shape": "^2.x",
+ "@antv/xflow": "^2.0.3",
"antd": "^5.7.3",
+ "classnames": "^2.2.6",
"dagre": "^0.8.5",
"dumi": "^2.1.14",
"elkjs": "^0.8.2",
"highlight.js": "^10.1.2",
"react": "^18.0.0",
"react-dom": "^18.0.0",
- "react-i18next": "^11.5.0",
- "classnames": "^2.2.6"
+ "react-i18next": "^11.5.0"
},
"repository": "https://github.com/antvis/x6"
}
diff --git a/sites/x6-sites/src/xflow/components/background/color/index.less b/sites/x6-sites/src/xflow/components/background/color/index.less
new file mode 100644
index 00000000000..b7e2e89e600
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/background/color/index.less
@@ -0,0 +1,4 @@
+.xflow-background-color {
+ height: 388px;
+ padding: 16px;
+}
diff --git a/sites/x6-sites/src/xflow/components/background/color/index.tsx b/sites/x6-sites/src/xflow/components/background/color/index.tsx
new file mode 100644
index 00000000000..d355a424d89
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/background/color/index.tsx
@@ -0,0 +1,16 @@
+import { XFlow, XFlowGraph, Background } from '@antv/xflow'
+import React from 'react'
+import './index.less'
+
+const Page = () => {
+ return (
+
+
+
+
+
+
+ )
+}
+
+export default Page
diff --git a/sites/x6-sites/src/xflow/components/background/image/index.less b/sites/x6-sites/src/xflow/components/background/image/index.less
new file mode 100644
index 00000000000..5e3053e1648
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/background/image/index.less
@@ -0,0 +1,4 @@
+.xflow-background-image {
+ height: 388px;
+ padding: 16px;
+}
diff --git a/sites/x6-sites/src/xflow/components/background/image/index.tsx b/sites/x6-sites/src/xflow/components/background/image/index.tsx
new file mode 100644
index 00000000000..33a71b36983
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/background/image/index.tsx
@@ -0,0 +1,19 @@
+import { XFlow, XFlowGraph, Background } from '@antv/xflow'
+import React from 'react'
+import './index.less'
+
+const bgImageDataURL =
+ 'data:image/png;base64,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'
+
+const Page = () => {
+ return (
+
+
+
+
+
+
+ )
+}
+
+export default Page
diff --git a/sites/x6-sites/src/xflow/components/background/node.tsx b/sites/x6-sites/src/xflow/components/background/node.tsx
new file mode 100644
index 00000000000..0c6180944f3
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/background/node.tsx
@@ -0,0 +1,34 @@
+import { useGraphStore } from '@antv/xflow'
+import { useCallback, useEffect } from 'react'
+
+export const InitNode = () => {
+ const addNodes = useGraphStore((state) => state.addNodes)
+ const addNodeInit = useCallback(() => {
+ addNodes([
+ {
+ id: '1',
+ shape: 'rect',
+ x: 300,
+ y: 150,
+ width: 100,
+ height: 40,
+ attrs: {
+ body: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ fill: '#fff',
+ rx: 6,
+ ry: 6,
+ },
+ },
+ label: '节点',
+ },
+ ])
+ }, [addNodes])
+
+ useEffect(() => {
+ addNodeInit()
+ }, [addNodeInit])
+
+ return null
+}
diff --git a/sites/x6-sites/src/xflow/components/background/watermark/index.less b/sites/x6-sites/src/xflow/components/background/watermark/index.less
new file mode 100644
index 00000000000..e1a7bebe9f9
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/background/watermark/index.less
@@ -0,0 +1,4 @@
+.xflow-background-watermark {
+ height: 388px;
+ padding: 16px;
+}
diff --git a/sites/x6-sites/src/xflow/components/background/watermark/index.tsx b/sites/x6-sites/src/xflow/components/background/watermark/index.tsx
new file mode 100644
index 00000000000..7f1b0b51f56
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/background/watermark/index.tsx
@@ -0,0 +1,24 @@
+import { XFlow, XFlowGraph, Background } from '@antv/xflow'
+import React from 'react'
+import './index.less'
+
+const bgImageDataURL =
+ 'data:image/png;base64,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'
+
+const Page = () => {
+ return (
+
+
+
+
+
+
+ )
+}
+
+export default Page
diff --git a/sites/x6-sites/src/xflow/components/clipboard/index.less b/sites/x6-sites/src/xflow/components/clipboard/index.less
new file mode 100644
index 00000000000..ded92d31a64
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/clipboard/index.less
@@ -0,0 +1,10 @@
+.xflow-clipboard-content-setting {
+ flex-basis: 300px;
+ width: 300px;
+ height: 388px;
+}
+
+.xflow-clipboard-content-graph {
+ flex: 1;
+ height: 388px;
+}
diff --git a/sites/x6-sites/src/xflow/components/clipboard/index.tsx b/sites/x6-sites/src/xflow/components/clipboard/index.tsx
new file mode 100644
index 00000000000..a63213ab7a3
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/clipboard/index.tsx
@@ -0,0 +1,154 @@
+import {
+ XFlow,
+ XFlowGraph,
+ Background,
+ useGraphStore,
+ useClipboard,
+ Clipboard,
+} from '@antv/xflow'
+import React, { useEffect, useState, useCallback } from 'react'
+import { Card, Row, Col, Slider, Checkbox, Button, Badge } from 'antd'
+import './index.less'
+
+const Page = () => {
+ const [localStorage, setLocalStorage] = useState(true)
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default Page
+
+const Setting = (props) => {
+ const initData = useGraphStore((state) => state.initData)
+
+ const setInitData = useCallback(() => {
+ initData({
+ nodes: [
+ {
+ id: '1',
+ x: 32,
+ y: 32,
+ width: 100,
+ height: 40,
+ label: 'Hello',
+ attrs: {
+ body: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ fill: '#fff',
+ rx: 6,
+ ry: 6,
+ },
+ },
+ },
+ {
+ id: '2',
+ shape: 'circle',
+ x: 160,
+ y: 180,
+ width: 60,
+ height: 60,
+ label: 'World',
+ attrs: {
+ body: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ fill: '#fff',
+ },
+ },
+ },
+ ],
+ edges: [
+ {
+ source: '1',
+ target: '2',
+ attrs: {
+ line: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ },
+ },
+ },
+ ],
+ })
+ }, [initData])
+
+ useEffect(() => {
+ setInitData()
+ }, [setInitData])
+ const [offset, setOffset] = useState(30)
+ const nodes = useGraphStore((state) => state.nodes)
+ const { copy, paste } = useClipboard()
+
+ const onCopy = () => {
+ const selected = nodes.filter((node) => node.selected)
+ const ids: string[] = selected.map((node) => node.id || '')
+ copy(ids)
+ }
+
+ const onPaste = () => {
+ paste({ offset })
+ }
+
+ return (
+
+
+
+ Paste Offset
+
+
+
+
+
+
+ setOffset(value)}
+ />
+
+
+
+
+ props.setLocalStorage(e.target.checked)}
+ >
+ useLocalStorage
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/sites/x6-sites/src/xflow/components/control/index.less b/sites/x6-sites/src/xflow/components/control/index.less
new file mode 100644
index 00000000000..06203404954
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/control/index.less
@@ -0,0 +1,10 @@
+.xflow-control-content-setting {
+ flex-basis: 300px;
+ width: 300px;
+ height: 420px;
+}
+
+.xflow-control-content-graph {
+ flex: 1;
+ height: 420px;
+}
diff --git a/sites/x6-sites/src/xflow/components/control/index.tsx b/sites/x6-sites/src/xflow/components/control/index.tsx
new file mode 100644
index 00000000000..916546bba89
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/control/index.tsx
@@ -0,0 +1,181 @@
+import {
+ XFlow,
+ XFlowGraph,
+ Background,
+ useGraphStore,
+ Control,
+} from '@antv/xflow'
+import React, { useEffect, useState, useCallback } from 'react'
+import { Card, Row, Col, Segmented, Select } from 'antd'
+import './index.less'
+
+const Page = () => {
+ const [options, setOptions] = useState({
+ direction: 'horizontal' as const,
+ placement: 'top' as const,
+ items: ['zoomOut', 'zoomTo', 'zoomIn', 'zoomToFit', 'zoomToOrigin'] as (
+ | 'zoomOut'
+ | 'zoomTo'
+ | 'zoomIn'
+ | 'zoomToFit'
+ | 'zoomToOrigin'
+ )[],
+ })
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default Page
+
+const Setting = ({ setOptions }) => {
+ const initData = useGraphStore((state) => state.initData)
+
+ const setInitData = useCallback(() => {
+ initData({
+ nodes: [
+ {
+ id: '1',
+ x: 32,
+ y: 32,
+ width: 100,
+ height: 40,
+ label: 'Hello',
+ attrs: {
+ body: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ fill: '#fff',
+ rx: 6,
+ ry: 6,
+ },
+ },
+ },
+ {
+ id: '2',
+ shape: 'circle',
+ x: 160,
+ y: 180,
+ width: 60,
+ height: 60,
+ label: 'World',
+ attrs: {
+ body: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ fill: '#fff',
+ },
+ },
+ },
+ ],
+ edges: [
+ {
+ source: '1',
+ target: '2',
+ attrs: {
+ line: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ },
+ },
+ },
+ ],
+ })
+ }, [initData])
+
+ useEffect(() => {
+ setInitData()
+ }, [setInitData])
+
+ const controlItems = [
+ 'zoomOut',
+ 'zoomTo',
+ 'zoomIn',
+ 'zoomToFit',
+ 'zoomToOrigin',
+ ]
+ const selectOptions = controlItems.map((item) => ({
+ label: item,
+ value: item,
+ }))
+
+ return (
+
+
+
+
+
+ setOptions((prev) => ({ ...prev, direction: value }))
+ }
+ />
+
+
+ Tooltip
+
+
+
+ setOptions((prev) => ({ ...prev, placement: value }))
+ }
+ />
+
+
+
+ Options
+
+
+
+
+ )
+}
diff --git a/sites/x6-sites/src/xflow/components/graph/index.less b/sites/x6-sites/src/xflow/components/graph/index.less
new file mode 100644
index 00000000000..27353fbf40c
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/graph/index.less
@@ -0,0 +1,10 @@
+.xflow-graph-content-setting {
+ flex-basis: 300px;
+ width: 300px;
+ height: 445px;
+}
+
+.xflow-graph-content-graph {
+ flex: 1;
+ height: 445px;
+}
diff --git a/sites/x6-sites/src/xflow/components/graph/index.tsx b/sites/x6-sites/src/xflow/components/graph/index.tsx
new file mode 100644
index 00000000000..862529be849
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/graph/index.tsx
@@ -0,0 +1,285 @@
+import { XFlow, XFlowGraph, Background, useGraphStore } from '@antv/xflow'
+import React, { useEffect, useState, useCallback } from 'react'
+import { Card, Row, Col, Checkbox } from 'antd'
+import './index.less'
+
+const Page = () => {
+ const [options, setOptions] = useState({
+ readonly: false,
+ zoomable: false,
+ embedable: false,
+ pannable: false,
+ restrict: false,
+ })
+
+ return (
+
+
+
+
+
+
+ true,
+ }}
+ restrict={options.restrict}
+ centerView
+ fitView
+ minScale={0.5}
+ maxScale={5}
+ connectionOptions={{
+ snap: true,
+ allowBlank: false,
+ allowLoop: false,
+ highlight: true,
+ connectionPoint: 'anchor',
+ anchor: 'center',
+ }}
+ connectionEdgeOptions={{
+ attrs: {
+ line: {
+ stroke: '#C2C8D5',
+ strokeWidth: 1,
+ },
+ },
+ animated: true,
+ zIndex: -1,
+ }}
+ selectOptions={{
+ multiple: true,
+ strict: true,
+ rubberband: true,
+ modifiers: 'shift',
+ showNodeSelectionBox: true,
+ }}
+ magnetAdsorbedHighlightOptions={{
+ name: 'stroke',
+ args: {
+ attrs: {
+ fill: '#5F95FF',
+ stroke: '#5F95FF',
+ },
+ },
+ }}
+ />
+
+
+
+
+ )
+}
+
+export default Page
+
+const ports = {
+ groups: {
+ group1: {
+ position: 'top',
+ attrs: {
+ circle: {
+ stroke: '#D06269',
+ strokeWidth: 1,
+ r: 4,
+ magnet: true,
+ },
+ },
+ },
+ group2: {
+ position: 'right',
+ attrs: {
+ circle: {
+ stroke: '#D06269',
+ strokeWidth: 1,
+ r: 4,
+ magnet: true,
+ },
+ },
+ },
+ group3: {
+ position: 'bottom',
+ attrs: {
+ circle: {
+ stroke: '#D06269',
+ strokeWidth: 1,
+ r: 4,
+ magnet: true,
+ },
+ },
+ },
+ group4: {
+ position: 'left',
+ attrs: {
+ circle: {
+ stroke: '#D06269',
+ strokeWidth: 1,
+ r: 4,
+ magnet: true,
+ },
+ },
+ },
+ },
+ items: [
+ { id: 'group1', group: 'group1' },
+ { id: 'group2', group: 'group2' },
+ { id: 'group3', group: 'group3' },
+ { id: 'group4', group: 'group4' },
+ ],
+}
+
+const Setting = ({ setOptions }) => {
+ const initData = useGraphStore((state) => state.initData)
+
+ const setInitData = useCallback(() => {
+ initData({
+ nodes: [
+ {
+ id: '1',
+ x: 32,
+ y: 32,
+ width: 100,
+ height: 40,
+ label: 'Hello',
+ attrs: {
+ body: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ fill: '#fff',
+ rx: 6,
+ ry: 6,
+ },
+ },
+ ports: {
+ ...ports,
+ },
+ },
+ {
+ id: '2',
+ x: 160,
+ y: 180,
+ width: 60,
+ height: 60,
+ label: 'World',
+ attrs: {
+ body: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ fill: '#fff',
+ },
+ },
+ ports: {
+ ...ports,
+ },
+ },
+ {
+ id: '3',
+ x: 300,
+ y: 180,
+ width: 100,
+ height: 40,
+ label: 'Text',
+ attrs: {
+ body: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ fill: '#fff',
+ },
+ },
+ ports: {
+ ...ports,
+ },
+ },
+ ],
+ edges: [
+ {
+ source: {
+ cell: '1',
+ port: 'group2',
+ },
+ target: {
+ cell: '2',
+ port: 'group1',
+ },
+ attrs: {
+ line: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ },
+ },
+ },
+ ],
+ })
+ }, [initData])
+
+ useEffect(() => {
+ setInitData()
+ }, [setInitData])
+
+ return (
+
+
+
+
+
+ setOptions((prev) => ({ ...prev, readonly: e.target.checked }))
+ }
+ >
+ readonly
+
+
+
+
+
+
+ setOptions((prev) => ({ ...prev, zoomable: e.target.checked }))
+ }
+ >
+ zoomable
+
+
+
+
+
+
+ setOptions((prev) => ({ ...prev, pannable: e.target.checked }))
+ }
+ >
+ pannable
+
+
+
+
+
+
+ setOptions((prev) => ({ ...prev, embedable: e.target.checked }))
+ }
+ >
+ embedable
+
+
+
+
+
+
+ setOptions((prev) => ({ ...prev, restrict: e.target.checked }))
+ }
+ >
+ restrict
+
+
+
+
+
+ )
+}
diff --git a/sites/x6-sites/src/xflow/components/grid/dot/index.less b/sites/x6-sites/src/xflow/components/grid/dot/index.less
new file mode 100644
index 00000000000..817b656a385
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/grid/dot/index.less
@@ -0,0 +1,4 @@
+.xflow-grid-dot {
+ height: 388px;
+ padding: 16px;
+}
diff --git a/sites/x6-sites/src/xflow/components/grid/dot/index.tsx b/sites/x6-sites/src/xflow/components/grid/dot/index.tsx
new file mode 100644
index 00000000000..90675370d50
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/grid/dot/index.tsx
@@ -0,0 +1,22 @@
+import { XFlow, XFlowGraph, Grid } from '@antv/xflow'
+import React from 'react'
+import './index.less'
+
+const Page = () => {
+ return (
+
+
+
+
+
+
+ )
+}
+
+export default Page
diff --git a/sites/x6-sites/src/xflow/components/grid/double-mesh/index.less b/sites/x6-sites/src/xflow/components/grid/double-mesh/index.less
new file mode 100644
index 00000000000..93bdda58675
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/grid/double-mesh/index.less
@@ -0,0 +1,4 @@
+.xflow-grid-double-mesh {
+ height: 388px;
+ padding: 16px;
+}
diff --git a/sites/x6-sites/src/xflow/components/grid/double-mesh/index.tsx b/sites/x6-sites/src/xflow/components/grid/double-mesh/index.tsx
new file mode 100644
index 00000000000..a8bfb47ef38
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/grid/double-mesh/index.tsx
@@ -0,0 +1,29 @@
+import { XFlow, XFlowGraph, Grid } from '@antv/xflow'
+import React from 'react'
+import './index.less'
+
+const Page = () => {
+ return (
+
+
+
+
+
+
+ )
+}
+
+export default Page
diff --git a/sites/x6-sites/src/xflow/components/grid/fixed-dot/index.less b/sites/x6-sites/src/xflow/components/grid/fixed-dot/index.less
new file mode 100644
index 00000000000..837eaa31db2
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/grid/fixed-dot/index.less
@@ -0,0 +1,4 @@
+.xflow-grid-fixed-dot {
+ height: 388px;
+ padding: 16px;
+}
diff --git a/sites/x6-sites/src/xflow/components/grid/fixed-dot/index.tsx b/sites/x6-sites/src/xflow/components/grid/fixed-dot/index.tsx
new file mode 100644
index 00000000000..4aa4740c320
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/grid/fixed-dot/index.tsx
@@ -0,0 +1,22 @@
+import { XFlow, XFlowGraph, Grid } from '@antv/xflow'
+import React from 'react'
+import './index.less'
+
+const Page = () => {
+ return (
+
+
+
+
+
+
+ )
+}
+
+export default Page
diff --git a/sites/x6-sites/src/xflow/components/grid/mesh/index.less b/sites/x6-sites/src/xflow/components/grid/mesh/index.less
new file mode 100644
index 00000000000..3b2069567f5
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/grid/mesh/index.less
@@ -0,0 +1,4 @@
+.xflow-grid-mesh {
+ height: 388px;
+ padding: 16px;
+}
diff --git a/sites/x6-sites/src/xflow/components/grid/mesh/index.tsx b/sites/x6-sites/src/xflow/components/grid/mesh/index.tsx
new file mode 100644
index 00000000000..6b33861aa62
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/grid/mesh/index.tsx
@@ -0,0 +1,22 @@
+import { XFlow, XFlowGraph, Grid } from '@antv/xflow'
+import React from 'react'
+import './index.less'
+
+const Page = () => {
+ return (
+
+
+
+
+
+
+ )
+}
+
+export default Page
diff --git a/sites/x6-sites/src/xflow/components/grid/size/index.less b/sites/x6-sites/src/xflow/components/grid/size/index.less
new file mode 100644
index 00000000000..10cbb30a617
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/grid/size/index.less
@@ -0,0 +1,4 @@
+.xflow-grid-size {
+ height: 388px;
+ padding: 16px;
+}
diff --git a/sites/x6-sites/src/xflow/components/grid/size/index.tsx b/sites/x6-sites/src/xflow/components/grid/size/index.tsx
new file mode 100644
index 00000000000..e01df7318f2
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/grid/size/index.tsx
@@ -0,0 +1,22 @@
+import { XFlow, XFlowGraph, Grid } from '@antv/xflow'
+import React from 'react'
+import './index.less'
+
+const Page = () => {
+ return (
+
+
+
+
+
+
+ )
+}
+
+export default Page
diff --git a/sites/x6-sites/src/xflow/components/history/index.less b/sites/x6-sites/src/xflow/components/history/index.less
new file mode 100644
index 00000000000..025861d4054
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/history/index.less
@@ -0,0 +1,11 @@
+.xflow-history {
+ height: 388px;
+
+ .xflow-history-header {
+ margin-bottom: 16px;
+ }
+
+ .xflow-history-content {
+ height: 300px !important;
+ }
+}
diff --git a/sites/x6-sites/src/xflow/components/history/index.tsx b/sites/x6-sites/src/xflow/components/history/index.tsx
new file mode 100644
index 00000000000..d481aa4a10c
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/history/index.tsx
@@ -0,0 +1,74 @@
+import {
+ XFlow,
+ XFlowGraph,
+ Background,
+ useGraphStore,
+ useHistory,
+ History,
+} from '@antv/xflow'
+import React, { useEffect, useCallback } from 'react'
+import { Button, Space } from 'antd'
+import './index.less'
+
+const HistoryButton = () => {
+ const { undo, redo, canUndo, canRedo } = useHistory()
+ const initData = useGraphStore((state) => state.initData)
+
+ const setInitData = useCallback(() => {
+ initData({
+ nodes: [
+ {
+ id: '1',
+ shape: 'rect',
+ x: 200,
+ y: 100,
+ width: 100,
+ height: 40,
+ attrs: {
+ body: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ fill: '#fff',
+ rx: 6,
+ ry: 6,
+ },
+ },
+ label: 'move',
+ },
+ ],
+ edges: [],
+ })
+ }, [initData])
+
+ useEffect(() => {
+ setInitData()
+ }, [setInitData])
+
+ return (
+
+
+
+
+
+
+ )
+}
+
+const Page = () => {
+ return (
+
+
+
+
+
+
+
+
+ )
+}
+
+export default Page
diff --git a/sites/x6-sites/src/xflow/components/minimap/index.less b/sites/x6-sites/src/xflow/components/minimap/index.less
new file mode 100644
index 00000000000..da4f5cf1410
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/minimap/index.less
@@ -0,0 +1,10 @@
+.xflow-minimap-content-setting {
+ flex-basis: 200px;
+ width: 200px;
+ height: 388px;
+}
+
+.xflow-minimap-content-graph {
+ width: 600px;
+ height: 388px;
+}
diff --git a/sites/x6-sites/src/xflow/components/minimap/index.tsx b/sites/x6-sites/src/xflow/components/minimap/index.tsx
new file mode 100644
index 00000000000..5c38b2af67c
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/minimap/index.tsx
@@ -0,0 +1,87 @@
+import {
+ XFlow,
+ XFlowGraph,
+ Background,
+ useGraphStore,
+ Minimap,
+} from '@antv/xflow'
+import React, { useEffect, useState, useCallback } from 'react'
+import { Segmented } from 'antd'
+import './index.less'
+
+const SegmentedHeader = ({ setOptions }) => {
+ const initData = useGraphStore((state) => state.initData)
+ const setInitData = useCallback(() => {
+ initData({
+ nodes: [
+ {
+ id: '1',
+ shape: 'rect',
+ x: 200,
+ y: 100,
+ width: 100,
+ height: 40,
+ attrs: {
+ body: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ fill: '#fff',
+ rx: 6,
+ ry: 6,
+ },
+ },
+ label: 'move',
+ },
+ ],
+ edges: [],
+ })
+ }, [initData])
+
+ useEffect(() => {
+ setInitData()
+ }, [setInitData])
+
+ return (
+
+ setOptions((prev) => ({ ...prev, simple: value }))}
+ />
+
+ )
+}
+
+const Page = () => {
+ const [options, setOptions] = useState({
+ simple: true,
+ simpleNodeBackground: 'red',
+ width: 200,
+ height: 160,
+ padding: 10,
+ })
+ return (
+
+
+
+
+ )
+}
+
+export default Page
diff --git a/sites/x6-sites/src/xflow/components/snapline/index.less b/sites/x6-sites/src/xflow/components/snapline/index.less
new file mode 100644
index 00000000000..ecf0a8057f7
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/snapline/index.less
@@ -0,0 +1,10 @@
+.xflow-snapline-content-setting {
+ flex-basis: 300px;
+ width: 300px;
+ height: 388px;
+}
+
+.xflow-snapline-content-graph {
+ flex: 1;
+ height: 388px;
+}
diff --git a/sites/x6-sites/src/xflow/components/snapline/index.tsx b/sites/x6-sites/src/xflow/components/snapline/index.tsx
new file mode 100644
index 00000000000..c08f2c3acbb
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/snapline/index.tsx
@@ -0,0 +1,170 @@
+import {
+ XFlow,
+ XFlowGraph,
+ Background,
+ Snapline,
+ useGraphStore,
+ Transform,
+} from '@antv/xflow'
+import React, { useEffect, useState, useCallback } from 'react'
+import { Card, Row, Col, Slider, Checkbox, Badge } from 'antd'
+import './index.less'
+
+const Page = () => {
+ const [options, setOptions] = useState({ tolerance: 10 })
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default Page
+
+const Setting = ({ setOptions, options }) => {
+ const initData = useGraphStore((state) => state.initData)
+ const setInitData = useCallback(() => {
+ initData({
+ nodes: [
+ {
+ id: '1',
+ x: 32,
+ y: 32,
+ width: 100,
+ height: 40,
+ label: 'Hello',
+ attrs: {
+ body: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ fill: '#fff',
+ rx: 6,
+ ry: 6,
+ },
+ },
+ },
+ {
+ id: '2',
+ shape: 'circle',
+ x: 160,
+ y: 180,
+ width: 60,
+ height: 60,
+ label: 'World',
+ attrs: {
+ body: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ fill: '#fff',
+ },
+ },
+ },
+ {
+ id: '3',
+ x: 200,
+ y: 100,
+ width: 100,
+ height: 40,
+ label: 'Drag Me',
+ attrs: {
+ body: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ fill: '#fff',
+ rx: 6,
+ ry: 6,
+ },
+ },
+ },
+ ],
+ edges: [
+ {
+ source: '1',
+ target: '2',
+ attrs: {
+ line: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ },
+ },
+ },
+ ],
+ })
+ }, [initData])
+ useEffect(() => {
+ setInitData()
+ }, [setInitData])
+
+ return (
+
+
+
+ Tolerance
+
+
+
+
+
+
+
+ setOptions((prev) => ({ ...prev, tolerance: value }))
+ }
+ />
+
+
+
+
+
+ setOptions((prev) => ({ ...prev, sharp: e.target.checked }))
+ }
+ >
+ Sharp Line
+
+
+
+
+
+
+ setOptions((prev) => ({ ...prev, resizing: e.target.checked }))
+ }
+ >
+ Snap on Resizing
+
+
+
+
+
+
+ setOptions((prev) => ({
+ ...prev,
+ filter: e.target.checked ? ['circle'] : undefined,
+ }))
+ }
+ >
+ Add Filter(only circle)
+
+
+
+
+
+ )
+}
diff --git a/sites/x6-sites/src/xflow/components/transform/index.less b/sites/x6-sites/src/xflow/components/transform/index.less
new file mode 100644
index 00000000000..beb57e7cef8
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/transform/index.less
@@ -0,0 +1,10 @@
+.xflow-transform-content-setting {
+ flex-basis: 300px;
+ width: 300px;
+ height: 445px;
+}
+
+.xflow-transform-content-graph {
+ flex: 1;
+ height: 445px;
+}
diff --git a/sites/x6-sites/src/xflow/components/transform/index.tsx b/sites/x6-sites/src/xflow/components/transform/index.tsx
new file mode 100644
index 00000000000..aedffe88127
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/transform/index.tsx
@@ -0,0 +1,253 @@
+import {
+ XFlow,
+ XFlowGraph,
+ Background,
+ useGraphStore,
+ Transform,
+} from '@antv/xflow'
+import React, { useEffect, useState, useCallBack } from 'react'
+import { Card, Row, Col, Slider, Checkbox, Badge } from 'antd'
+import './index.less'
+
+const Page = () => {
+ const [options, setOptions] = useState({})
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default Page
+
+const Setting = ({ setOptions, options }) => {
+ const initData = useGraphStore((state) => state.initData)
+
+ const setInitData = useCallBack(() => {
+ initData({
+ nodes: [
+ {
+ id: '1',
+ x: 32,
+ y: 32,
+ width: 100,
+ height: 40,
+ label: 'Hello',
+ attrs: {
+ body: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ fill: '#fff',
+ rx: 6,
+ ry: 6,
+ },
+ },
+ },
+ {
+ id: '2',
+ shape: 'circle',
+ x: 160,
+ y: 180,
+ width: 60,
+ height: 60,
+ label: 'World',
+ attrs: {
+ body: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ fill: '#fff',
+ },
+ },
+ },
+ ],
+ edges: [
+ {
+ source: '1',
+ target: '2',
+ attrs: {
+ line: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ },
+ },
+ },
+ ],
+ })
+ }, [initData])
+
+ useEffect(() => {
+ setInitData()
+ }, [setInitData])
+
+ return (
+
+
+
+
+
+ setOptions((prev) => ({
+ ...prev,
+ resizing: { ...prev.resizing, enabled: e.target.checked },
+ }))
+ }
+ >
+ resizing
+
+
+
+
+ minWidth
+
+
+ setOptions((prev) => ({
+ ...prev,
+ resizing: { ...prev.resizing, minWidth: value },
+ }))
+ }
+ />
+
+
+
+
+
+
+
+ maxWidth
+
+
+ setOptions((prev) => ({
+ ...prev,
+ resizing: { ...prev.resizing, maxWidth: value },
+ }))
+ }
+ />
+
+
+
+
+
+
+ minHeight
+
+
+ setOptions((prev) => ({
+ ...prev,
+ resizing: { ...prev.resizing, minHeight: value },
+ }))
+ }
+ />
+
+
+
+
+
+
+
+ maxHeight
+
+
+ setOptions((prev) => ({
+ ...prev,
+ resizing: { ...prev.resizing, maxHeight: value },
+ }))
+ }
+ />
+
+
+
+
+
+
+
+
+ setOptions((prev) => ({
+ ...prev,
+ rotating: { ...prev.rotating, enabled: e.target.checked },
+ }))
+ }
+ >
+ rotating
+
+
+
+
+ grid
+
+
+ setOptions((prev) => ({
+ ...prev,
+ rotating: { ...prev.rotating, grid: value },
+ }))
+ }
+ />
+
+
+
+
+
+
+
+ )
+}
diff --git a/sites/x6-sites/src/xflow/components/xflow-graph/basic/index.less b/sites/x6-sites/src/xflow/components/xflow-graph/basic/index.less
new file mode 100644
index 00000000000..1458d49dc82
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/xflow-graph/basic/index.less
@@ -0,0 +1,4 @@
+.xflow-xflow-graph-basic {
+ height: 388px;
+ padding: 16px;
+}
diff --git a/sites/x6-sites/src/xflow/components/xflow-graph/basic/index.tsx b/sites/x6-sites/src/xflow/components/xflow-graph/basic/index.tsx
new file mode 100644
index 00000000000..0f413176126
--- /dev/null
+++ b/sites/x6-sites/src/xflow/components/xflow-graph/basic/index.tsx
@@ -0,0 +1,16 @@
+import { XFlow, XFlowGraph, Background } from '@antv/xflow'
+import React from 'react'
+import './index.less'
+
+const Page = () => {
+ return (
+
+
+
+
+
+
+ )
+}
+
+export default Page
diff --git a/sites/x6-sites/src/xflow/guide/InitNode.tsx b/sites/x6-sites/src/xflow/guide/InitNode.tsx
new file mode 100644
index 00000000000..810567a06bc
--- /dev/null
+++ b/sites/x6-sites/src/xflow/guide/InitNode.tsx
@@ -0,0 +1,81 @@
+import { useGraphStore } from '@antv/xflow'
+import { useEffect, useCallback } from 'react'
+
+export const InitNode = () => {
+ const initData = useGraphStore((state) => state.initData)
+
+ const setInitData = useCallback(() => {
+ initData({
+ nodes: [
+ {
+ id: '1',
+ x: 32,
+ y: 32,
+ width: 100,
+ height: 40,
+ label: 'Hello',
+ attrs: {
+ body: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ fill: '#fff',
+ rx: 6,
+ ry: 6,
+ },
+ },
+ },
+ {
+ id: '2',
+ shape: 'circle',
+ x: 160,
+ y: 180,
+ width: 60,
+ height: 60,
+ label: 'World',
+ attrs: {
+ body: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ fill: '#fff',
+ },
+ },
+ },
+ {
+ id: '3',
+ x: 200,
+ y: 100,
+ width: 100,
+ height: 40,
+ label: 'Drag Me',
+ attrs: {
+ body: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ fill: '#fff',
+ rx: 6,
+ ry: 6,
+ },
+ },
+ },
+ ],
+ edges: [
+ {
+ source: '1',
+ target: '2',
+ attrs: {
+ line: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ },
+ },
+ },
+ ],
+ })
+ }, [initData])
+
+ useEffect(() => {
+ setInitData()
+ }, [setInitData])
+
+ return null
+}
diff --git a/sites/x6-sites/src/xflow/guide/index.less b/sites/x6-sites/src/xflow/guide/index.less
new file mode 100644
index 00000000000..506f237e4e0
--- /dev/null
+++ b/sites/x6-sites/src/xflow/guide/index.less
@@ -0,0 +1,4 @@
+.xflow-guide {
+ height: 388px;
+ padding: 16px;
+}
diff --git a/sites/x6-sites/src/xflow/guide/index.tsx b/sites/x6-sites/src/xflow/guide/index.tsx
new file mode 100644
index 00000000000..1e7cde8231a
--- /dev/null
+++ b/sites/x6-sites/src/xflow/guide/index.tsx
@@ -0,0 +1,20 @@
+import { XFlow, XFlowGraph, Grid, Background, Snapline } from '@antv/xflow'
+import React from 'react'
+import { InitNode } from './InitNode'
+import './index.less'
+
+const Page = () => {
+ return (
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default Page
diff --git a/sites/x6-sites/src/xflow/hooks/use-dnd/dnd.tsx b/sites/x6-sites/src/xflow/hooks/use-dnd/dnd.tsx
new file mode 100644
index 00000000000..8d436ebf916
--- /dev/null
+++ b/sites/x6-sites/src/xflow/hooks/use-dnd/dnd.tsx
@@ -0,0 +1,51 @@
+import { useDnd } from '@antv/xflow'
+
+import './index.less'
+import React from 'react'
+import { Space } from 'antd'
+
+const Dnd = () => {
+ const { startDrag } = useDnd()
+ const list = ['node1', 'node2', 'node3']
+
+ const handleMouseDown = (
+ e: React.MouseEvent,
+ item: string,
+ ) => {
+ startDrag(
+ {
+ id: item,
+ shape: 'rect',
+ width: 150,
+ height: 32,
+ attrs: {
+ body: {
+ stroke: '#D9DADD',
+ strokeWidth: 1,
+ },
+ },
+ label: item,
+ },
+ e,
+ )
+ }
+
+ return (
+
+
+ {list.map((item) => (
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions
+ handleMouseDown(e, item)}
+ >
+ {item}
+
+ ))}
+
+
+ )
+}
+
+export { Dnd }
diff --git a/sites/x6-sites/src/xflow/hooks/use-dnd/index.less b/sites/x6-sites/src/xflow/hooks/use-dnd/index.less
new file mode 100644
index 00000000000..15f0f2bc608
--- /dev/null
+++ b/sites/x6-sites/src/xflow/hooks/use-dnd/index.less
@@ -0,0 +1,28 @@
+.xflow-hooks-use-dnd-page {
+ height: 288px;
+
+ .xflow-hooks-use-dnd-Container {
+ display: flex;
+ height: 288px;
+ border-radius: 5px;
+ box-shadow: 0 12px 5px -10px rgb(0 0 0 / 10%), 0 0 4px 0 rgb(0 0 0 / 10%);
+ }
+
+ .xflow-hooks-use-dnd-dnd {
+ flex-shrink: 0;
+ width: 200px;
+ height: 100%;
+ padding: 24px;
+ border-right: 1px solid #ccc;
+
+ .dnd-item {
+ box-sizing: border-box;
+ width: 150px;
+ height: 32px;
+ text-align: center;
+ background-color: #fff;
+ border: 1px solid #ccc;
+ cursor: move;
+ }
+ }
+}
diff --git a/sites/x6-sites/src/xflow/hooks/use-dnd/index.tsx b/sites/x6-sites/src/xflow/hooks/use-dnd/index.tsx
new file mode 100644
index 00000000000..25c7ba43a32
--- /dev/null
+++ b/sites/x6-sites/src/xflow/hooks/use-dnd/index.tsx
@@ -0,0 +1,20 @@
+import { XFlow, XFlowGraph, Background } from '@antv/xflow'
+import React from 'react'
+import { Dnd } from './dnd'
+import './index.less'
+
+const Page = () => {
+ return (
+
+ )
+}
+
+export default Page
diff --git a/sites/x6-sites/src/xflow/hooks/use-graph-event/index.less b/sites/x6-sites/src/xflow/hooks/use-graph-event/index.less
new file mode 100644
index 00000000000..825250d3680
--- /dev/null
+++ b/sites/x6-sites/src/xflow/hooks/use-graph-event/index.less
@@ -0,0 +1,4 @@
+.xflow-hooks-use-graph-event {
+ height: 388px;
+ padding: 16px;
+}
diff --git a/sites/x6-sites/src/xflow/hooks/use-graph-event/index.tsx b/sites/x6-sites/src/xflow/hooks/use-graph-event/index.tsx
new file mode 100644
index 00000000000..fc1924c3743
--- /dev/null
+++ b/sites/x6-sites/src/xflow/hooks/use-graph-event/index.tsx
@@ -0,0 +1,74 @@
+import {
+ XFlow,
+ XFlowGraph,
+ Background,
+ useGraphStore,
+ useGraphEvent,
+} from '@antv/xflow'
+import React, { useCallback, useEffect } from 'react'
+import './index.less'
+
+const InitNode = () => {
+ const addNodes = useGraphStore((state) => state.addNodes)
+ const updateNode = useGraphStore((state) => state.updateNode)
+
+ useGraphEvent('node:click', ({ node }) => {
+ const { id } = node
+ const randomColor = Math.floor(Math.random() * 16777215).toString(16)
+ const fillColor = `#${randomColor}`
+ updateNode(id, {
+ attrs: {
+ body: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ fill: fillColor,
+ rx: 6,
+ ry: 6,
+ },
+ },
+ })
+ })
+
+ const addNodeInit = useCallback(() => {
+ addNodes([
+ {
+ id: '3',
+ shape: 'rect',
+ x: 200,
+ y: 200,
+ width: 100,
+ height: 40,
+ attrs: {
+ body: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ fill: '#fff',
+ rx: 6,
+ ry: 6,
+ },
+ },
+ label: 'added',
+ },
+ ])
+ }, [addNodes])
+
+ useEffect(() => {
+ addNodeInit()
+ }, [addNodeInit])
+
+ return null
+}
+
+const Page = () => {
+ return (
+
+
+
+
+
+
+
+ )
+}
+
+export default Page
diff --git a/sites/x6-sites/src/xflow/hooks/use-graph-store/index.less b/sites/x6-sites/src/xflow/hooks/use-graph-store/index.less
new file mode 100644
index 00000000000..e9ff129c986
--- /dev/null
+++ b/sites/x6-sites/src/xflow/hooks/use-graph-store/index.less
@@ -0,0 +1,11 @@
+.xflow-hooks-use-graph-store {
+ height: 388px;
+
+ .xflow-hooks-use-graph-store-header {
+ margin-bottom: 16px;
+ }
+
+ .xflow-hooks-use-graph-store-content {
+ height: 300px;
+ }
+}
diff --git a/sites/x6-sites/src/xflow/hooks/use-graph-store/index.tsx b/sites/x6-sites/src/xflow/hooks/use-graph-store/index.tsx
new file mode 100644
index 00000000000..9bf908b4eed
--- /dev/null
+++ b/sites/x6-sites/src/xflow/hooks/use-graph-store/index.tsx
@@ -0,0 +1,61 @@
+import { XFlow, XFlowGraph, Background, useGraphStore } from '@antv/xflow'
+import React from 'react'
+import { Button, Space } from 'antd'
+import './index.less'
+
+const ToolsButton = () => {
+ const addNodes = useGraphStore((state) => state.addNodes)
+ const removeNodes = useGraphStore((state) => state.removeNodes)
+
+ const onAddNodes = () => {
+ addNodes([
+ {
+ id: '3',
+ shape: 'rect',
+ x: 200,
+ y: 100,
+ width: 100,
+ height: 40,
+ attrs: {
+ body: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ fill: '#fff',
+ rx: 6,
+ ry: 6,
+ },
+ },
+ label: 'added',
+ },
+ ])
+ }
+
+ const onRemoveNodes = () => {
+ removeNodes(['3'])
+ }
+
+ return (
+
+
+
+
+
+
+ )
+}
+
+const Page = () => {
+ return (
+
+ )
+}
+
+export default Page
diff --git a/sites/x6-sites/src/xflow/hooks/use-key-board/index.less b/sites/x6-sites/src/xflow/hooks/use-key-board/index.less
new file mode 100644
index 00000000000..8988ad3e0fe
--- /dev/null
+++ b/sites/x6-sites/src/xflow/hooks/use-key-board/index.less
@@ -0,0 +1,3 @@
+.xflow-hooks-use-key-board {
+ height: 388px;
+}
diff --git a/sites/x6-sites/src/xflow/hooks/use-key-board/index.tsx b/sites/x6-sites/src/xflow/hooks/use-key-board/index.tsx
new file mode 100644
index 00000000000..447cfdd55b7
--- /dev/null
+++ b/sites/x6-sites/src/xflow/hooks/use-key-board/index.tsx
@@ -0,0 +1,108 @@
+import {
+ XFlow,
+ XFlowGraph,
+ Background,
+ useGraphStore,
+ useClipboard,
+ useKeyboard,
+ Clipboard,
+} from '@antv/xflow'
+import React, { useEffect, useCallback } from 'react'
+import './index.less'
+
+const Page = () => {
+ return (
+
+
+
+
+
+
+
+
+ )
+}
+
+const Header = () => {
+ const { copy, paste } = useClipboard()
+
+ const addNodes = useGraphStore((state) => state.addNodes)
+ const addEdges = useGraphStore((state) => state.addEdges)
+ const nodes = useGraphStore((state) => state.nodes)
+
+ const setAddNodes = useCallback(() => {
+ addNodes([
+ {
+ id: 'source',
+ x: 32,
+ y: 32,
+ width: 100,
+ height: 40,
+ label: 'Hello',
+ attrs: {
+ body: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ fill: '#fff',
+ rx: 6,
+ ry: 6,
+ },
+ },
+ },
+ {
+ id: 'target',
+ shape: 'circle',
+ x: 160,
+ y: 180,
+ width: 60,
+ height: 60,
+ label: 'World',
+ attrs: {
+ body: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ fill: '#fff',
+ },
+ },
+ },
+ ])
+ }, [addNodes])
+
+ const setAddEdges = useCallback(() => {
+ addEdges([
+ {
+ source: 'source',
+ target: 'target',
+ attrs: {
+ line: {
+ stroke: '#8f8f8f',
+ strokeWidth: 1,
+ },
+ },
+ },
+ ])
+ }, [addEdges])
+
+ const onCopy = () => {
+ const selected = nodes.filter((node) => node.selected)
+ const ids: string[] = selected.map((node) => node.id || '')
+ copy(ids)
+ }
+
+ useKeyboard('ctrl+c', () => {
+ onCopy()
+ })
+
+ useKeyboard('ctrl+v', () => {
+ paste()
+ })
+
+ useEffect(() => {
+ setAddNodes()
+ setAddEdges()
+ }, [setAddEdges, setAddNodes])
+
+ return null
+}
+
+export default Page