Kaynağa Gözat

feat(auto-layout): support custom node filtering (#897)

* feat(auto-layout): support filter node

* docs(auto-layout): filter node
Louis Young 3 ay önce
ebeveyn
işleme
3bbdf8fee2

+ 1 - 1
apps/demo-free-layout/src/hooks/use-editor-props.tsx

@@ -277,7 +277,7 @@ export function useEditorProps(
       },
       plugins: () => [
         createFreeStackPlugin({
-          sortNodes: (nodes) => {
+          sortNodes: (nodes: WorkflowNodeEntity[]) => {
             const commentNodes: WorkflowNodeEntity[] = [];
             const otherNodes: WorkflowNodeEntity[] = [];
             nodes.forEach((node) => {

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

@@ -140,6 +140,8 @@ interface LayoutOptions {
   enableAnimation?: boolean;
   /** Animation duration (milliseconds) */
   animationDuration?: number;
+  /** Node filter function to control which nodes participate in layout calculation */
+  filterNode?: (params: { node: WorkflowNodeEntity; parent?: WorkflowNodeEntity }) => boolean;
 }
 ```
 
@@ -163,7 +165,7 @@ The plugin is implemented based on the Dagre library, which is a JavaScript libr
 
 ## Advanced Usage
 
-### Custom Follow Nodes
+### Custom Follow Node
 
 You can customize node following relationships through the `getFollowNode` function:
 
@@ -171,12 +173,38 @@ You can customize node following relationships through the `getFollowNode` funct
 const layoutOptions: LayoutOptions = {
   getFollowNode: (node: LayoutNode) => {
     // Return the node ID that should follow the current node
-    if (node.flowNodeType.type === 'comment') {
+    if (node.flowNodeType === 'comment') {
       return getNearestNode(node);
     }
     return undefined;
   }
 };
+await tools.autoLayout(layoutOptions);
+```
+
+### Node Filtering
+
+You can control which nodes participate in layout calculation through the `filterNode` function:
+
+```typescript
+const layoutOptions: LayoutOptions = {
+  filterNode: ({ node, parent }) => {
+    // Filter out specific types of nodes
+    if (node.flowNodeType === 'comment') {
+      return false;
+    }
+
+    // Filter based on parent node conditions
+    if (parent && parent.flowNodeType.type === 'group') {
+      return false;
+    }
+
+    return true; // Include all nodes by default
+  }
+};
+
+// Execute layout with filter options
+await tools.autoLayout(layoutOptions);
 ```
 
 ### Nested Container Layout

+ 29 - 1
apps/docs/src/zh/guide/advanced/plugin/free-auto-layout-plugin.mdx

@@ -141,6 +141,8 @@ interface LayoutOptions {
   enableAnimation?: boolean;
   /** 动画持续时间(毫秒) */
   animationDuration?: number;
+  /** 节点过滤函数,用于控制哪些节点参与布局计算 */
+  filterNode?: (params: { node: WorkflowNodeEntity; parent?: WorkflowNodeEntity }) => boolean;
 }
 ```
 
@@ -172,12 +174,38 @@ interface LayoutOptions {
 const layoutOptions: LayoutOptions = {
   getFollowNode: (node: LayoutNode) => {
     // 返回应该跟随当前节点的节点ID
-    if (node.flowNodeType.type === 'comment') {
+    if (node.flowNodeType === 'comment') {
       return getNearestNode(node);
     }
     return undefined;
   }
 };
+await tools.autoLayout(layoutOptions);
+```
+
+### 节点过滤
+
+可以通过 `filterNode` 函数控制哪些节点参与布局计算:
+
+```typescript
+const layoutOptions: LayoutOptions = {
+  filterNode: ({ node, parent }) => {
+    // 过滤掉特定类型的节点
+    if (node.flowNodeType === 'comment') {
+      return false;
+    }
+
+    // 根据父节点条件过滤
+    if (parent && parent.flowNodeType.type === 'group') {
+      return false;
+    }
+
+    return true; // 默认包含所有节点
+  }
+};
+
+// 使用过滤选项执行布局
+await tools.autoLayout(layoutOptions);
 ```
 
 ### 嵌套容器布局

+ 1 - 0
packages/plugins/free-auto-layout-plugin/src/layout/constant.ts

@@ -18,6 +18,7 @@ export const DefaultLayoutConfig: LayoutConfig = {
 };
 
 export const DefaultLayoutOptions: LayoutOptions = {
+  filterNode: undefined,
   getFollowNode: undefined,
   disableFitView: false,
   enableAnimation: false,

+ 1 - 0
packages/plugins/free-auto-layout-plugin/src/layout/type.ts

@@ -107,6 +107,7 @@ export interface LayoutOptions {
   enableAnimation?: boolean;
   animationDuration?: number;
   disableFitView?: boolean;
+  filterNode?: (params: { node: WorkflowNodeEntity; parent?: WorkflowNodeEntity }) => boolean;
 }
 
 export interface LayoutConfig {

+ 3 - 1
packages/plugins/free-auto-layout-plugin/src/services.ts

@@ -82,7 +82,9 @@ export class AutoLayoutService {
 
   /** 创建节点布局数据 */
   private createLayoutNode(node: WorkflowNodeEntity, options: LayoutOptions): LayoutNode {
-    const { blocks } = node;
+    const blocks = node.blocks.filter((blockNode) =>
+      options.filterNode ? options.filterNode?.({ node: blockNode, parent: node.parent }) : true
+    );
     const edges = this.getNodesAllLines(blocks);
 
     // 创建子布局节点