mirror of
https://gitee.com/ByteDance/flowgram.ai.git
synced 2025-07-07 17:43:29 +08:00
feat(demo): sidebar support openning by node id
This commit is contained in:
parent
d96f97dbc5
commit
22915805ad
@ -44,7 +44,7 @@ export const BaseNode = ({ node }: { node: FlowNodeEntity }) => {
|
|||||||
if (nodeRender.dragging) {
|
if (nodeRender.dragging) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sidebar.setNodeRender(nodeRender);
|
sidebar.setNodeId(nodeRender.node.id);
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,13 +1,11 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { NodeRenderReturnType } from '@flowgram.ai/fixed-layout-editor';
|
|
||||||
|
|
||||||
import { SidebarContext } from '../../context';
|
import { SidebarContext } from '../../context';
|
||||||
|
|
||||||
export function SidebarProvider({ children }: { children: React.ReactNode }) {
|
export function SidebarProvider({ children }: { children: React.ReactNode }) {
|
||||||
const [nodeRender, setNodeRender] = useState<NodeRenderReturnType | undefined>();
|
const [nodeId, setNodeId] = useState<string | undefined>();
|
||||||
return (
|
return (
|
||||||
<SidebarContext.Provider value={{ visible: !!nodeRender, nodeRender, setNodeRender }}>
|
<SidebarContext.Provider value={{ visible: !!nodeId, nodeId, setNodeId }}>
|
||||||
{children}
|
{children}
|
||||||
</SidebarContext.Provider>
|
</SidebarContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useContext, useEffect } from 'react';
|
import { useCallback, useContext, useEffect, useMemo } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
PlaygroundEntityContext,
|
PlaygroundEntityContext,
|
||||||
@ -7,20 +7,26 @@ import {
|
|||||||
} from '@flowgram.ai/fixed-layout-editor';
|
} from '@flowgram.ai/fixed-layout-editor';
|
||||||
import { SideSheet } from '@douyinfe/semi-ui';
|
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 = () => {
|
export const SidebarRenderer = () => {
|
||||||
const { nodeRender, setNodeRender } = useContext(SidebarContext);
|
const { nodeId, setNodeId } = useContext(SidebarContext);
|
||||||
const { selection, playground } = useClientContext();
|
const { selection, playground, document } = useClientContext();
|
||||||
const refresh = useRefresh();
|
const refresh = useRefresh();
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
setNodeRender(undefined);
|
setNodeId(undefined);
|
||||||
}, []);
|
}, []);
|
||||||
|
const node = nodeId ? document.getNode(nodeId) : undefined;
|
||||||
/**
|
/**
|
||||||
* Listen readonly
|
* Listen readonly
|
||||||
*/
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const disposable = playground.config.onReadonlyOrDisabledChange(() => refresh());
|
const disposable = playground.config.onReadonlyOrDisabledChange(() => {
|
||||||
|
handleClose();
|
||||||
|
refresh();
|
||||||
|
});
|
||||||
return () => disposable.dispose();
|
return () => disposable.dispose();
|
||||||
}, [playground]);
|
}, [playground]);
|
||||||
/**
|
/**
|
||||||
@ -34,41 +40,47 @@ export const SidebarRenderer = () => {
|
|||||||
*/
|
*/
|
||||||
if (selection.selection.length === 0) {
|
if (selection.selection.length === 0) {
|
||||||
handleClose();
|
handleClose();
|
||||||
} else if (selection.selection.length === 1 && selection.selection[0] !== nodeRender?.node) {
|
} else if (selection.selection.length === 1 && selection.selection[0] !== node) {
|
||||||
handleClose();
|
handleClose();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return () => toDispose.dispose();
|
return () => toDispose.dispose();
|
||||||
}, [selection, handleClose]);
|
}, [selection, handleClose, node]);
|
||||||
/**
|
/**
|
||||||
* Close when node disposed
|
* Close when node disposed
|
||||||
*/
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (nodeRender) {
|
if (node) {
|
||||||
const toDispose = nodeRender.node.onDispose(() => {
|
const toDispose = node.onDispose(() => {
|
||||||
setNodeRender(undefined);
|
setNodeId(undefined);
|
||||||
});
|
});
|
||||||
return () => toDispose.dispose();
|
return () => toDispose.dispose();
|
||||||
}
|
}
|
||||||
return () => {};
|
return () => {};
|
||||||
}, [nodeRender]);
|
}, [node]);
|
||||||
|
|
||||||
|
const visible = useMemo(() => {
|
||||||
|
if (!node) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const { disableSideBar = false } = node.getNodeMeta<FlowNodeMeta>();
|
||||||
|
return !disableSideBar;
|
||||||
|
}, [node]);
|
||||||
|
|
||||||
if (playground.config.readonly) {
|
if (playground.config.readonly) {
|
||||||
return null;
|
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 ? (
|
const content = node ? (
|
||||||
<PlaygroundEntityContext.Provider key={nodeRender.node.id} value={nodeRender.node}>
|
<PlaygroundEntityContext.Provider key={node.id} value={node}>
|
||||||
<NodeRenderContext.Provider value={nodeRender}>
|
<SidebarNodeRenderer node={node} />
|
||||||
{nodeRender.form?.render()}
|
|
||||||
</NodeRenderContext.Provider>
|
|
||||||
</PlaygroundEntityContext.Provider>
|
</PlaygroundEntityContext.Provider>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SideSheet mask={false} visible={!!nodeRender} onCancel={handleClose}>
|
<SideSheet mask={false} visible={visible} onCancel={handleClose}>
|
||||||
<IsSidebarContext.Provider value={true}>{content}</IsSidebarContext.Provider>
|
<IsSidebarContext.Provider value={true}>{content}</IsSidebarContext.Provider>
|
||||||
</SideSheet>
|
</SideSheet>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,11 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { NodeRenderReturnType } from '@flowgram.ai/fixed-layout-editor';
|
|
||||||
|
|
||||||
export const SidebarContext = React.createContext<{
|
export const SidebarContext = React.createContext<{
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
nodeRender?: NodeRenderReturnType;
|
nodeId?: string;
|
||||||
setNodeRender: (node: NodeRenderReturnType | undefined) => void;
|
setNodeId: (node: string | undefined) => void;
|
||||||
}>({ visible: false, setNodeRender: () => {} });
|
}>({ visible: false, setNodeId: () => {} });
|
||||||
|
|
||||||
export const IsSidebarContext = React.createContext<boolean>(false);
|
export const IsSidebarContext = React.createContext<boolean>(false);
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import {
|
|||||||
FlowNodeRegistry as FlowNodeRegistryDefault,
|
FlowNodeRegistry as FlowNodeRegistryDefault,
|
||||||
FixedLayoutPluginContext,
|
FixedLayoutPluginContext,
|
||||||
FlowNodeEntity,
|
FlowNodeEntity,
|
||||||
|
FlowNodeMeta as FlowNodeMetaDefault,
|
||||||
} from '@flowgram.ai/fixed-layout-editor';
|
} from '@flowgram.ai/fixed-layout-editor';
|
||||||
|
|
||||||
import { type JsonSchema } from './json-schema';
|
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
|
* You can customize your own node registry
|
||||||
* 你可以自定义节点的注册器
|
* 你可以自定义节点的注册器
|
||||||
*/
|
*/
|
||||||
export interface FlowNodeRegistry extends FlowNodeRegistryDefault {
|
export interface FlowNodeRegistry extends FlowNodeRegistryDefault {
|
||||||
|
meta: FlowNodeMeta;
|
||||||
info: {
|
info: {
|
||||||
icon: string;
|
icon: string;
|
||||||
description: string;
|
description: string;
|
||||||
|
|||||||
@ -41,7 +41,7 @@ export const NodeWrapper: React.FC<NodeWrapperProps> = (props) => {
|
|||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
selectNode(e);
|
selectNode(e);
|
||||||
if (!isDragging) {
|
if (!isDragging) {
|
||||||
sidebar.setNodeRender(nodeRender);
|
sidebar.setNodeId(nodeRender.node.id);
|
||||||
// 可选:将 isScrollToView 设为 true,可以让节点选中后滚动到画布中间
|
// 可选:将 isScrollToView 设为 true,可以让节点选中后滚动到画布中间
|
||||||
// Optional: Set isScrollToView to true to scroll the node to the center of the canvas after it is selected.
|
// Optional: Set isScrollToView to true to scroll the node to the center of the canvas after it is selected.
|
||||||
if (isScrollToView) {
|
if (isScrollToView) {
|
||||||
|
|||||||
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,13 +1,11 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { NodeRenderReturnType } from '@flowgram.ai/free-layout-editor';
|
|
||||||
|
|
||||||
import { SidebarContext } from '../../context';
|
import { SidebarContext } from '../../context';
|
||||||
|
|
||||||
export function SidebarProvider({ children }: { children: React.ReactNode }) {
|
export function SidebarProvider({ children }: { children: React.ReactNode }) {
|
||||||
const [nodeRender, setNodeRender] = useState<NodeRenderReturnType | undefined>();
|
const [nodeId, setNodeId] = useState<string | undefined>();
|
||||||
return (
|
return (
|
||||||
<SidebarContext.Provider value={{ visible: !!nodeRender, nodeRender, setNodeRender }}>
|
<SidebarContext.Provider value={{ visible: !!nodeId, nodeId, setNodeId }}>
|
||||||
{children}
|
{children}
|
||||||
</SidebarContext.Provider>
|
</SidebarContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -8,20 +8,25 @@ import {
|
|||||||
import { SideSheet } from '@douyinfe/semi-ui';
|
import { SideSheet } from '@douyinfe/semi-ui';
|
||||||
|
|
||||||
import { FlowNodeMeta } from '../../typings';
|
import { FlowNodeMeta } from '../../typings';
|
||||||
import { SidebarContext, IsSidebarContext, NodeRenderContext } from '../../context';
|
import { SidebarContext, IsSidebarContext } from '../../context';
|
||||||
|
import { SidebarNodeRenderer } from './sidebar-node-renderer';
|
||||||
|
|
||||||
export const SidebarRenderer = () => {
|
export const SidebarRenderer = () => {
|
||||||
const { nodeRender, setNodeRender } = useContext(SidebarContext);
|
const { nodeId, setNodeId } = useContext(SidebarContext);
|
||||||
const { selection, playground } = useClientContext();
|
const { selection, playground, document } = useClientContext();
|
||||||
const refresh = useRefresh();
|
const refresh = useRefresh();
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
setNodeRender(undefined);
|
setNodeId(undefined);
|
||||||
}, []);
|
}, []);
|
||||||
|
const node = nodeId ? document.getNode(nodeId) : undefined;
|
||||||
/**
|
/**
|
||||||
* Listen readonly
|
* Listen readonly
|
||||||
*/
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const disposable = playground.config.onReadonlyOrDisabledChange(() => refresh());
|
const disposable = playground.config.onReadonlyOrDisabledChange(() => {
|
||||||
|
handleClose();
|
||||||
|
refresh();
|
||||||
|
});
|
||||||
return () => disposable.dispose();
|
return () => disposable.dispose();
|
||||||
}, [playground]);
|
}, [playground]);
|
||||||
/**
|
/**
|
||||||
@ -35,44 +40,42 @@ export const SidebarRenderer = () => {
|
|||||||
*/
|
*/
|
||||||
if (selection.selection.length === 0) {
|
if (selection.selection.length === 0) {
|
||||||
handleClose();
|
handleClose();
|
||||||
} else if (selection.selection.length === 1 && selection.selection[0] !== nodeRender?.node) {
|
} else if (selection.selection.length === 1 && selection.selection[0] !== node) {
|
||||||
handleClose();
|
handleClose();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return () => toDispose.dispose();
|
return () => toDispose.dispose();
|
||||||
}, [selection, handleClose]);
|
}, [selection, handleClose, node]);
|
||||||
/**
|
/**
|
||||||
* Close when node disposed
|
* Close when node disposed
|
||||||
*/
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (nodeRender) {
|
if (node) {
|
||||||
const toDispose = nodeRender.node.onDispose(() => {
|
const toDispose = node.onDispose(() => {
|
||||||
setNodeRender(undefined);
|
setNodeId(undefined);
|
||||||
});
|
});
|
||||||
return () => toDispose.dispose();
|
return () => toDispose.dispose();
|
||||||
}
|
}
|
||||||
return () => {};
|
return () => {};
|
||||||
}, [nodeRender]);
|
}, [node]);
|
||||||
|
|
||||||
const visible = useMemo(() => {
|
const visible = useMemo(() => {
|
||||||
if (!nodeRender) {
|
if (!node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const { disableSideBar = false } = nodeRender.node.getNodeMeta<FlowNodeMeta>();
|
const { disableSideBar = false } = node.getNodeMeta<FlowNodeMeta>();
|
||||||
return !disableSideBar;
|
return !disableSideBar;
|
||||||
}, [nodeRender]);
|
}, [node]);
|
||||||
|
|
||||||
if (playground.config.readonly) {
|
if (playground.config.readonly) {
|
||||||
return null;
|
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 ? (
|
const content = node ? (
|
||||||
<PlaygroundEntityContext.Provider key={nodeRender.node.id} value={nodeRender.node}>
|
<PlaygroundEntityContext.Provider key={node.id} value={node}>
|
||||||
<NodeRenderContext.Provider value={nodeRender}>
|
<SidebarNodeRenderer node={node} />
|
||||||
{nodeRender.form?.render()}
|
|
||||||
</NodeRenderContext.Provider>
|
|
||||||
</PlaygroundEntityContext.Provider>
|
</PlaygroundEntityContext.Provider>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { NodeRenderReturnType } from '@flowgram.ai/free-layout-editor';
|
|
||||||
|
|
||||||
export const SidebarContext = React.createContext<{
|
export const SidebarContext = React.createContext<{
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
nodeRender?: NodeRenderReturnType;
|
nodeId?: string;
|
||||||
setNodeRender: (node: NodeRenderReturnType | undefined) => void;
|
setNodeId: (node: string | undefined) => void;
|
||||||
}>({ visible: false, setNodeRender: () => {} });
|
}>({ visible: false, setNodeId: () => {} });
|
||||||
|
|
||||||
export const IsSidebarContext = React.createContext<boolean>(false);
|
export const IsSidebarContext = React.createContext<boolean>(false);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user