Louis Young 3 месяцев назад
Родитель
Сommit
25677258f8

+ 3 - 1
apps/docs/src/en/guide/advanced/plugin/_meta.json

@@ -1,4 +1,6 @@
 [
   "custom-plugin",
-  "panel-manager-plugin"
+  "panel-manager-plugin",
+  "free-auto-layout-plugin",
+  "free-stack-plugin"
 ]

+ 2 - 2
apps/docs/src/en/guide/advanced/plugin/auto-layout-plugin.mdx → apps/docs/src/en/guide/advanced/plugin/free-auto-layout-plugin.mdx

@@ -1,6 +1,6 @@
 import { PackageManagerTabs } from '@theme';
 
-# Auto Layout Plugin
+# @flowgram.ai/free-auto-layout-plugin
 
 An automatic layout plugin based on the Dagre algorithm that provides intelligent node arrangement functionality for free layout canvases.
 
@@ -258,4 +258,4 @@ layoutOptions: {
   // or
   animationDuration: 150, // Reduce animation duration
 }
-```
+```

+ 195 - 0
apps/docs/src/en/guide/advanced/plugin/free-stack-plugin.mdx

@@ -0,0 +1,195 @@
+import { PackageManagerTabs } from '@theme';
+
+# @flowgram.ai/free-stack-plugin
+
+A layer management plugin that provides z-index layer control functionality for nodes and connections in free layout canvas.
+
+## Features
+
+- Intelligently calculates layer relationships between nodes and connections to avoid occlusion issues
+- Supports automatic top-level display for selected nodes
+- Supports highlighting of hovered nodes and connections
+- Customizable node sorting rules to control rendering order of nodes at the same level
+- Automatically handles parent-child node layer relationships
+- Supports intelligent layer management for connections, ensuring connection visibility
+- Real-time response to node selection, hover, and entity change events
+
+## Quick Start
+
+1. Installation
+
+<PackageManagerTabs command="install @flowgram.ai/free-stack-plugin" />
+
+2. Register Plugin
+
+The plugin registration method is basically the same as other flowgram plugins. Just make sure not to create duplicates and finally pass it to the corresponding FreeLayoutEditorProvider.
+
+```tsx
+import { createFreeStackPlugin } from '@flowgram.ai/free-stack-plugin';
+
+const editorProps = useMemo(() => ({
+  plugins: () => [
+    createFreeStackPlugin()
+  ]
+}), []);
+
+return (
+  <FreeLayoutEditorProvider {...editorProps}>
+    <EditorRenderer />
+  </FreeLayoutEditorProvider>
+)
+```
+
+3. Custom Node Sorting
+
+You can customize the sorting rules for nodes at the same level through the `sortNodes` function:
+
+```tsx
+import { createFreeStackPlugin } from '@flowgram.ai/free-stack-plugin';
+import { WorkflowNodeType } from './nodes/constants';
+
+const editorProps = useMemo(() => ({
+  plugins: () => [
+    createFreeStackPlugin({
+      sortNodes: (nodes) => {
+        const commentNodes = [];
+        const otherNodes = [];
+
+        // Separate comment nodes from other nodes
+        nodes.forEach((node) => {
+          if (node.flowNodeType === WorkflowNodeType.Comment) {
+            commentNodes.push(node);
+          } else {
+            otherNodes.push(node);
+          }
+        });
+
+        // Comment nodes render at the bottom layer, other nodes at the top layer
+        return [...commentNodes, ...otherNodes];
+      },
+    })
+  ]
+}), []);
+```
+
+## Configuration Options
+
+### FreeStackPluginOptions
+
+Plugin configuration options:
+
+```typescript
+interface FreeStackPluginOptions {
+  /** Custom node sorting function */
+  sortNodes?: (nodes: WorkflowNodeEntity[]) => WorkflowNodeEntity[];
+}
+```
+
+### sortNodes Function
+
+Used to customize sorting rules for nodes at the same level:
+
+```typescript
+type SortNodesFunction = (nodes: WorkflowNodeEntity[]) => WorkflowNodeEntity[];
+```
+
+**Parameter Description:**
+- `nodes`: Array of nodes to be sorted
+- **Return Value**: Array of sorted nodes
+
+**Use Cases:**
+- Place specific types of nodes (like comments) at the bottom layer
+- Sort nodes by business priority
+- Sort by creation time or other attributes
+
+## Layer Management Algorithm
+
+### Basic Layer Calculation
+
+The plugin uses an intelligent algorithm to calculate the layer for each node and connection:
+
+1. **Base Layer**: Starts calculation from `BASE_Z_INDEX` (default is 8)
+2. **Node Layer**: Calculated based on node nesting relationships and sorting rules
+3. **Connection Layer**: Ensures connections are not occluded by nodes while handling special cases
+
+### Layer Elevation Rules
+
+The following situations will trigger layer elevation:
+
+- **Selected Nodes**: Selected nodes will be elevated to the top layer
+- **Hovered Elements**: Hovered nodes or connections will be highlighted
+- **Drawing Connections**: Connections being drawn will be placed at the top layer
+- **Parent-Child Relationship Connections**: Connections between parent-child nodes will be prioritized for display
+
+### Layer Calculation Process
+
+1. **Initialization**: Clear cache, calculate basic parameters
+2. **Node Indexing**: Establish node index mapping
+3. **Selected Node Processing**: Mark parent relationships of selected nodes
+4. **Layer Assignment**: Recursively process node layers
+5. **Connection Processing**: Calculate connection layers, ensure visibility
+6. **Style Application**: Apply calculation results to DOM elements
+
+## Advanced Usage
+
+### Complex Sorting Rules
+
+You can implement complex node sorting logic:
+
+```typescript
+const sortNodes = (nodes: WorkflowNodeEntity[]) => {
+  return nodes.sort((a, b) => {
+    // 1. Sort by node type priority
+    const typeOrder = {
+      [WorkflowNodeType.Comment]: 0,
+      [WorkflowNodeType.Start]: 1,
+      [WorkflowNodeType.End]: 2,
+      // ... other types
+    };
+
+    const aOrder = typeOrder[a.flowNodeType] ?? 999;
+    const bOrder = typeOrder[b.flowNodeType] ?? 999;
+
+    if (aOrder !== bOrder) {
+      return aOrder - bOrder;
+    }
+
+    // 2. Sort by creation time
+    return a.createTime - b.createTime;
+  });
+};
+```
+
+## FAQ
+
+### Q: How to keep specific types of nodes always at the bottom layer?
+
+A: Place these nodes at the front of the array through the `sortNodes` function:
+
+```typescript
+const sortNodes = (nodes) => {
+  const backgroundNodes = nodes.filter(node =>
+    node.flowNodeType === WorkflowNodeType.Comment
+  );
+  const foregroundNodes = nodes.filter(node =>
+    node.flowNodeType !== WorkflowNodeType.Comment
+  );
+
+  return [...backgroundNodes, ...foregroundNodes];
+};
+```
+
+### Q: How to disable automatic layer management?
+
+A: Currently, the plugin does not provide a disable option. If you need complete custom layer management, it is recommended not to use this plugin and directly set z-index in node components.
+
+### Q: Performance optimization suggestions?
+
+A: The plugin already has built-in performance optimizations:
+- Uses debounce mechanism to reduce calculation frequency
+- Only recalculates layers when necessary
+- Uses Map data structure to improve lookup efficiency
+
+For large canvases (over 1000 nodes), it is recommended to:
+- Simplify the logic of the `sortNodes` function
+- Avoid complex calculations in sorting functions

+ 1 - 1
apps/docs/src/en/guide/advanced/plugin/panel-manager-plugin.mdx

@@ -1,6 +1,6 @@
 import { PackageManagerTabs } from '@theme';
 
-# Panel Manager Plugin
+# @flowgram.ai/panel-manager-plugin
 
 A plugin for managing different types of panels.
 

+ 2 - 1
apps/docs/src/zh/guide/advanced/plugin/_meta.json

@@ -1,5 +1,6 @@
 [
   "custom-plugin",
   "panel-manager-plugin",
-  "free-auto-layout-plugin"
+  "free-auto-layout-plugin",
+  "free-stack-plugin"
 ]

+ 195 - 0
apps/docs/src/zh/guide/advanced/plugin/free-stack-plugin.mdx

@@ -0,0 +1,195 @@
+import { PackageManagerTabs } from '@theme';
+
+# @flowgram.ai/free-stack-plugin
+
+层级管理插件,为自由布局画布提供节点和连线的 z-index 层级控制功能。
+
+## 功能
+
+- 智能计算节点和连线的层级关系,避免遮挡问题
+- 支持选中节点自动置顶显示
+- 支持悬停节点和连线的高亮显示
+- 可自定义节点排序规则,控制同层级节点的渲染顺序
+- 自动处理父子节点的层级关系
+- 支持连线的智能层级管理,确保连线可见性
+- 实时响应节点选择、悬停和实体变化事件
+
+## 快速开始
+
+1. 安装
+
+<PackageManagerTabs command="install @flowgram.ai/free-stack-plugin" />
+
+2. 注册插件
+
+插件的注册方法和 flowgram 的其他插件基本相同,只需要保证不要重复创建以及最终传入到对应的 FreeLayoutEditorProvider 即可
+
+```tsx
+import { createFreeStackPlugin } from '@flowgram.ai/free-stack-plugin';
+
+const editorProps = useMemo(() => ({
+  plugins: () => [
+    createFreeStackPlugin()
+  ]
+}), []);
+
+return (
+  <FreeLayoutEditorProvider {...editorProps}>
+    <EditorRenderer />
+  </FreeLayoutEditorProvider>
+)
+```
+
+3. 自定义节点排序
+
+可以通过 `sortNodes` 函数自定义同层级节点的排序规则:
+
+```tsx
+import { createFreeStackPlugin } from '@flowgram.ai/free-stack-plugin';
+import { WorkflowNodeType } from './nodes/constants';
+
+const editorProps = useMemo(() => ({
+  plugins: () => [
+    createFreeStackPlugin({
+      sortNodes: (nodes) => {
+        const commentNodes = [];
+        const otherNodes = [];
+
+        // 将注释节点和其他节点分开
+        nodes.forEach((node) => {
+          if (node.flowNodeType === WorkflowNodeType.Comment) {
+            commentNodes.push(node);
+          } else {
+            otherNodes.push(node);
+          }
+        });
+
+        // 注释节点渲染在底层,其他节点在上层
+        return [...commentNodes, ...otherNodes];
+      },
+    })
+  ]
+}), []);
+```
+
+## 配置选项
+
+### FreeStackPluginOptions
+
+插件的配置选项:
+
+```typescript
+interface FreeStackPluginOptions {
+  /** 自定义节点排序函数 */
+  sortNodes?: (nodes: WorkflowNodeEntity[]) => WorkflowNodeEntity[];
+}
+```
+
+### sortNodes 函数
+
+用于自定义同层级节点的排序规则:
+
+```typescript
+type SortNodesFunction = (nodes: WorkflowNodeEntity[]) => WorkflowNodeEntity[];
+```
+
+**参数说明:**
+- `nodes`: 需要排序的节点数组
+- **返回值**: 排序后的节点数组
+
+**使用场景:**
+- 将特定类型的节点(如注释)放在底层
+- 按照业务优先级排序节点
+- 按照创建时间或其他属性排序
+
+## 层级管理算法
+
+### 基础层级计算
+
+插件使用智能算法计算每个节点和连线的层级:
+
+1. **基础层级**:从 `BASE_Z_INDEX`(默认为 8)开始计算
+2. **节点层级**:根据节点的嵌套关系和排序规则计算
+3. **连线层级**:确保连线不被节点遮挡,同时处理特殊情况
+
+### 层级提升规则
+
+以下情况会触发层级提升:
+
+- **选中节点**:选中的节点会被提升到顶层
+- **悬停元素**:悬停的节点或连线会被高亮显示
+- **正在绘制的连线**:绘制中的连线会置于顶层
+- **父子关系连线**:父子节点间的连线会优先显示
+
+### 层级计算流程
+
+1. **初始化**:清除缓存,计算基础参数
+2. **节点索引**:建立节点索引映射
+3. **选中节点处理**:标记选中节点的父级关系
+4. **层级分配**:递归处理节点层级
+5. **连线处理**:计算连线层级,确保可见性
+6. **样式应用**:将计算结果应用到 DOM 元素
+
+## 高级用法
+
+### 复杂排序规则
+
+可以实现复杂的节点排序逻辑:
+
+```typescript
+const sortNodes = (nodes: WorkflowNodeEntity[]) => {
+  return nodes.sort((a, b) => {
+    // 1. 按节点类型优先级排序
+    const typeOrder = {
+      [WorkflowNodeType.Comment]: 0,
+      [WorkflowNodeType.Start]: 1,
+      [WorkflowNodeType.End]: 2,
+      // ... 其他类型
+    };
+
+    const aOrder = typeOrder[a.flowNodeType] ?? 999;
+    const bOrder = typeOrder[b.flowNodeType] ?? 999;
+
+    if (aOrder !== bOrder) {
+      return aOrder - bOrder;
+    }
+
+    // 2. 按创建时间排序
+    return a.createTime - b.createTime;
+  });
+};
+```
+
+## 常见问题
+
+### Q: 如何让特定类型的节点始终在底层?
+
+A: 通过 `sortNodes` 函数将这些节点排在数组前面:
+
+```typescript
+const sortNodes = (nodes) => {
+  const backgroundNodes = nodes.filter(node =>
+    node.flowNodeType === WorkflowNodeType.Comment
+  );
+  const foregroundNodes = nodes.filter(node =>
+    node.flowNodeType !== WorkflowNodeType.Comment
+  );
+
+  return [...backgroundNodes, ...foregroundNodes];
+};
+```
+
+### Q: 如何禁用自动层级管理?
+
+A: 目前插件没有提供禁用选项,如果需要完全自定义层级管理,建议不使用此插件,直接在节点组件中设置 z-index。
+
+### Q: 性能优化建议?
+
+A: 插件已经内置了性能优化:
+- 使用防抖机制减少计算频率
+- 只在必要时重新计算层级
+- 使用 Map 数据结构提高查找效率
+
+对于大型画布(超过 1000 个节点),建议:
+- 简化 `sortNodes` 函数的逻辑
+- 避免在排序函数中进行复杂计算