Просмотр исходного кода

feat(demo): sidebar support openning by node id (#324)

xiamidaxia 7 месяцев назад
Родитель
Сommit
fdb663c74c

+ 1 - 1
apps/demo-fixed-layout/src/components/base-node/index.tsx

@@ -44,7 +44,7 @@ export const BaseNode = ({ node }: { node: FlowNodeEntity }) => {
           if (nodeRender.dragging) {
             return;
           }
-          sidebar.setNodeRender(nodeRender);
+          sidebar.setNodeId(nodeRender.node.id);
         }}
         style={{
           /**

+ 14 - 0
apps/demo-fixed-layout/src/components/sidebar/sidebar-node-renderer.tsx

@@ -0,0 +1,14 @@
+import { useNodeRender, FlowNodeEntity } from '@flowgram.ai/fixed-layout-editor';
+
+import { NodeRenderContext } from '../../context';
+
+export function SidebarNodeRenderer(props: { node: FlowNodeEntity }) {
+  const { node } = props;
+  const nodeRender = useNodeRender(node);
+
+  return (
+    <NodeRenderContext.Provider value={nodeRender}>
+      {nodeRender.form?.render()}
+    </NodeRenderContext.Provider>
+  );
+}

+ 2 - 4
apps/demo-fixed-layout/src/components/sidebar/sidebar-provider.tsx

@@ -1,13 +1,11 @@
 import { useState } from 'react';
 
-import { NodeRenderReturnType } from '@flowgram.ai/fixed-layout-editor';
-
 import { SidebarContext } from '../../context';
 
 export function SidebarProvider({ children }: { children: React.ReactNode }) {
-  const [nodeRender, setNodeRender] = useState<NodeRenderReturnType | undefined>();
+  const [nodeId, setNodeId] = useState<string | undefined>();
   return (
-    <SidebarContext.Provider value={{ visible: !!nodeRender, nodeRender, setNodeRender }}>
+    <SidebarContext.Provider value={{ visible: !!nodeId, nodeId, setNodeId }}>
       {children}
     </SidebarContext.Provider>
   );

+ 31 - 19
apps/demo-fixed-layout/src/components/sidebar/sidebar-renderer.tsx

@@ -1,4 +1,4 @@
-import { useCallback, useContext, useEffect } from 'react';
+import { useCallback, useContext, useEffect, useMemo } from 'react';
 
 import {
   PlaygroundEntityContext,
@@ -7,20 +7,26 @@ import {
 } from '@flowgram.ai/fixed-layout-editor';
 import { SideSheet } from '@douyinfe/semi-ui';
 
-import { SidebarContext, IsSidebarContext, NodeRenderContext } from '../../context';
+import { FlowNodeMeta } from '../../typings';
+import { SidebarContext, IsSidebarContext } from '../../context';
+import { SidebarNodeRenderer } from './sidebar-node-renderer';
 
 export const SidebarRenderer = () => {
-  const { nodeRender, setNodeRender } = useContext(SidebarContext);
-  const { selection, playground } = useClientContext();
+  const { nodeId, setNodeId } = useContext(SidebarContext);
+  const { selection, playground, document } = useClientContext();
   const refresh = useRefresh();
   const handleClose = useCallback(() => {
-    setNodeRender(undefined);
+    setNodeId(undefined);
   }, []);
+  const node = nodeId ? document.getNode(nodeId) : undefined;
   /**
    * Listen readonly
    */
   useEffect(() => {
-    const disposable = playground.config.onReadonlyOrDisabledChange(() => refresh());
+    const disposable = playground.config.onReadonlyOrDisabledChange(() => {
+      handleClose();
+      refresh();
+    });
     return () => disposable.dispose();
   }, [playground]);
   /**
@@ -34,41 +40,47 @@ export const SidebarRenderer = () => {
        */
       if (selection.selection.length === 0) {
         handleClose();
-      } else if (selection.selection.length === 1 && selection.selection[0] !== nodeRender?.node) {
+      } else if (selection.selection.length === 1 && selection.selection[0] !== node) {
         handleClose();
       }
     });
     return () => toDispose.dispose();
-  }, [selection, handleClose]);
+  }, [selection, handleClose, node]);
   /**
    * Close when node disposed
    */
   useEffect(() => {
-    if (nodeRender) {
-      const toDispose = nodeRender.node.onDispose(() => {
-        setNodeRender(undefined);
+    if (node) {
+      const toDispose = node.onDispose(() => {
+        setNodeId(undefined);
       });
       return () => toDispose.dispose();
     }
     return () => {};
-  }, [nodeRender]);
+  }, [node]);
+
+  const visible = useMemo(() => {
+    if (!node) {
+      return false;
+    }
+    const { disableSideBar = false } = node.getNodeMeta<FlowNodeMeta>();
+    return !disableSideBar;
+  }, [node]);
 
   if (playground.config.readonly) {
     return null;
   }
   /**
-   * Add key to rerender the sidebar when the node changes
+   * Add "key" to rerender the sidebar when the node changes
    */
-  const content = nodeRender ? (
-    <PlaygroundEntityContext.Provider key={nodeRender.node.id} value={nodeRender.node}>
-      <NodeRenderContext.Provider value={nodeRender}>
-        {nodeRender.form?.render()}
-      </NodeRenderContext.Provider>
+  const content = node ? (
+    <PlaygroundEntityContext.Provider key={node.id} value={node}>
+      <SidebarNodeRenderer node={node} />
     </PlaygroundEntityContext.Provider>
   ) : null;
 
   return (
-    <SideSheet mask={false} visible={!!nodeRender} onCancel={handleClose}>
+    <SideSheet mask={false} visible={visible} onCancel={handleClose}>
       <IsSidebarContext.Provider value={true}>{content}</IsSidebarContext.Provider>
     </SideSheet>
   );

+ 3 - 5
apps/demo-fixed-layout/src/context/sidebar-context.ts

@@ -1,11 +1,9 @@
 import React from 'react';
 
-import { NodeRenderReturnType } from '@flowgram.ai/fixed-layout-editor';
-
 export const SidebarContext = React.createContext<{
   visible: boolean;
-  nodeRender?: NodeRenderReturnType;
-  setNodeRender: (node: NodeRenderReturnType | undefined) => void;
-}>({ visible: false, setNodeRender: () => {} });
+  nodeId?: string;
+  setNodeId: (node: string | undefined) => void;
+}>({ visible: false, setNodeId: () => {} });
 
 export const IsSidebarContext = React.createContext<boolean>(false);

+ 9 - 0
apps/demo-fixed-layout/src/typings/node.ts

@@ -4,6 +4,7 @@ import {
   FlowNodeRegistry as FlowNodeRegistryDefault,
   FixedLayoutPluginContext,
   FlowNodeEntity,
+  FlowNodeMeta as FlowNodeMetaDefault,
 } from '@flowgram.ai/fixed-layout-editor';
 
 import { type JsonSchema } from './json-schema';
@@ -37,11 +38,19 @@ export interface FlowNodeJSON extends FlowNodeJSONDefault {
   };
 }
 
+/**
+ * You can customize your own node meta
+ * 你可以自定义节点的meta
+ */
+export interface FlowNodeMeta extends FlowNodeMetaDefault {
+  disableSideBar?: boolean;
+}
 /**
  * You can customize your own node registry
  * 你可以自定义节点的注册器
  */
 export interface FlowNodeRegistry extends FlowNodeRegistryDefault {
+  meta: FlowNodeMeta;
   info: {
     icon: string;
     description: string;

+ 1 - 1
apps/demo-free-layout/src/components/base-node/node-wrapper.tsx

@@ -41,7 +41,7 @@ export const NodeWrapper: React.FC<NodeWrapperProps> = (props) => {
         onClick={(e) => {
           selectNode(e);
           if (!isDragging) {
-            sidebar.setNodeRender(nodeRender);
+            sidebar.setNodeId(nodeRender.node.id);
             // 可选:将 isScrollToView 设为 true,可以让节点选中后滚动到画布中间
             // Optional: Set isScrollToView to true to scroll the node to the center of the canvas after it is selected.
             if (isScrollToView) {

+ 14 - 0
apps/demo-free-layout/src/components/sidebar/sidebar-node-renderer.tsx

@@ -0,0 +1,14 @@
+import { useNodeRender, FlowNodeEntity } from '@flowgram.ai/free-layout-editor';
+
+import { NodeRenderContext } from '../../context';
+
+export function SidebarNodeRenderer(props: { node: FlowNodeEntity }) {
+  const { node } = props;
+  const nodeRender = useNodeRender(node);
+
+  return (
+    <NodeRenderContext.Provider value={nodeRender}>
+      {nodeRender.form?.render()}
+    </NodeRenderContext.Provider>
+  );
+}

+ 2 - 4
apps/demo-free-layout/src/components/sidebar/sidebar-provider.tsx

@@ -1,13 +1,11 @@
 import { useState } from 'react';
 
-import { NodeRenderReturnType } from '@flowgram.ai/free-layout-editor';
-
 import { SidebarContext } from '../../context';
 
 export function SidebarProvider({ children }: { children: React.ReactNode }) {
-  const [nodeRender, setNodeRender] = useState<NodeRenderReturnType | undefined>();
+  const [nodeId, setNodeId] = useState<string | undefined>();
   return (
-    <SidebarContext.Provider value={{ visible: !!nodeRender, nodeRender, setNodeRender }}>
+    <SidebarContext.Provider value={{ visible: !!nodeId, nodeId, setNodeId }}>
       {children}
     </SidebarContext.Provider>
   );

+ 23 - 20
apps/demo-free-layout/src/components/sidebar/sidebar-renderer.tsx

@@ -8,20 +8,25 @@ import {
 import { SideSheet } from '@douyinfe/semi-ui';
 
 import { FlowNodeMeta } from '../../typings';
-import { SidebarContext, IsSidebarContext, NodeRenderContext } from '../../context';
+import { SidebarContext, IsSidebarContext } from '../../context';
+import { SidebarNodeRenderer } from './sidebar-node-renderer';
 
 export const SidebarRenderer = () => {
-  const { nodeRender, setNodeRender } = useContext(SidebarContext);
-  const { selection, playground } = useClientContext();
+  const { nodeId, setNodeId } = useContext(SidebarContext);
+  const { selection, playground, document } = useClientContext();
   const refresh = useRefresh();
   const handleClose = useCallback(() => {
-    setNodeRender(undefined);
+    setNodeId(undefined);
   }, []);
+  const node = nodeId ? document.getNode(nodeId) : undefined;
   /**
    * Listen readonly
    */
   useEffect(() => {
-    const disposable = playground.config.onReadonlyOrDisabledChange(() => refresh());
+    const disposable = playground.config.onReadonlyOrDisabledChange(() => {
+      handleClose();
+      refresh();
+    });
     return () => disposable.dispose();
   }, [playground]);
   /**
@@ -35,44 +40,42 @@ export const SidebarRenderer = () => {
        */
       if (selection.selection.length === 0) {
         handleClose();
-      } else if (selection.selection.length === 1 && selection.selection[0] !== nodeRender?.node) {
+      } else if (selection.selection.length === 1 && selection.selection[0] !== node) {
         handleClose();
       }
     });
     return () => toDispose.dispose();
-  }, [selection, handleClose]);
+  }, [selection, handleClose, node]);
   /**
    * Close when node disposed
    */
   useEffect(() => {
-    if (nodeRender) {
-      const toDispose = nodeRender.node.onDispose(() => {
-        setNodeRender(undefined);
+    if (node) {
+      const toDispose = node.onDispose(() => {
+        setNodeId(undefined);
       });
       return () => toDispose.dispose();
     }
     return () => {};
-  }, [nodeRender]);
+  }, [node]);
 
   const visible = useMemo(() => {
-    if (!nodeRender) {
+    if (!node) {
       return false;
     }
-    const { disableSideBar = false } = nodeRender.node.getNodeMeta<FlowNodeMeta>();
+    const { disableSideBar = false } = node.getNodeMeta<FlowNodeMeta>();
     return !disableSideBar;
-  }, [nodeRender]);
+  }, [node]);
 
   if (playground.config.readonly) {
     return null;
   }
   /**
-   * Add key to rerender the sidebar when the node changes
+   * Add "key" to rerender the sidebar when the node changes
    */
-  const content = nodeRender ? (
-    <PlaygroundEntityContext.Provider key={nodeRender.node.id} value={nodeRender.node}>
-      <NodeRenderContext.Provider value={nodeRender}>
-        {nodeRender.form?.render()}
-      </NodeRenderContext.Provider>
+  const content = node ? (
+    <PlaygroundEntityContext.Provider key={node.id} value={node}>
+      <SidebarNodeRenderer node={node} />
     </PlaygroundEntityContext.Provider>
   ) : null;
 

+ 3 - 5
apps/demo-free-layout/src/context/sidebar-context.ts

@@ -1,11 +1,9 @@
 import React from 'react';
 
-import { NodeRenderReturnType } from '@flowgram.ai/free-layout-editor';
-
 export const SidebarContext = React.createContext<{
   visible: boolean;
-  nodeRender?: NodeRenderReturnType;
-  setNodeRender: (node: NodeRenderReturnType | undefined) => void;
-}>({ visible: false, setNodeRender: () => {} });
+  nodeId?: string;
+  setNodeId: (node: string | undefined) => void;
+}>({ visible: false, setNodeId: () => {} });
 
 export const IsSidebarContext = React.createContext<boolean>(false);