From 2420cf368922d157f51b0d32bf44c147b9e7b123 Mon Sep 17 00:00:00 2001 From: moklick Date: Sat, 14 Nov 2020 16:46:14 +0100 Subject: [PATCH] refactor(onlyRenderVisible): change from nodes to elements #677 --- src/container/EdgeRenderer/index.tsx | 98 +++++----------------------- src/container/EdgeRenderer/utils.ts | 74 ++++++++++++++++++++- src/container/FlowRenderer/index.tsx | 2 +- src/container/GraphView/index.tsx | 12 +++- src/container/NodeRenderer/index.tsx | 9 +-- src/container/ReactFlow/index.tsx | 6 +- src/store/index.ts | 18 +++++ 7 files changed, 121 insertions(+), 98 deletions(-) diff --git a/src/container/EdgeRenderer/index.tsx b/src/container/EdgeRenderer/index.tsx index f37fb718..68978d54 100644 --- a/src/container/EdgeRenderer/index.tsx +++ b/src/container/EdgeRenderer/index.tsx @@ -4,12 +4,11 @@ import { useStoreState } from '../../store/hooks'; import ConnectionLine from '../../components/ConnectionLine/index'; import { isEdge } from '../../utils/graph'; import MarkerDefinitions from './MarkerDefinitions'; +import { getHandlePosition, getHandle } from './utils'; import { - XYPosition, Position, Edge, Node, - ElementId, HandleElement, Elements, ConnectionLineType, @@ -33,78 +32,6 @@ interface EdgePositions { targetY: number; } -function getHandlePosition(position: Position, node: Node, handle: any | null = null): XYPosition { - if (!handle) { - switch (position) { - case Position.Top: - return { - x: node.__rf.width / 2, - y: 0, - }; - case Position.Right: - return { - x: node.__rf.width, - y: node.__rf.height / 2, - }; - case Position.Bottom: - return { - x: node.__rf.width / 2, - y: node.__rf.height, - }; - case Position.Left: - return { - x: 0, - y: node.__rf.height / 2, - }; - } - } - - switch (position) { - case Position.Top: - return { - x: handle.x + handle.width / 2, - y: handle.y, - }; - case Position.Right: - return { - x: handle.x + handle.width, - y: handle.y + handle.height / 2, - }; - case Position.Bottom: - return { - x: handle.x + handle.width / 2, - y: handle.y + handle.height, - }; - case Position.Left: - return { - x: handle.x, - y: handle.y + handle.height / 2, - }; - } -} - -function getHandle(bounds: HandleElement[], handleId: ElementId | null): HandleElement | null | undefined { - let handle = null; - - if (!bounds) { - return null; - } - - // there is no handleId when there are no multiple handles/ handles with ids - // so we just pick the first one - if (bounds.length === 1 || !handleId) { - handle = bounds[0]; - } else if (handleId) { - handle = bounds.find((d) => d.id === handleId); - } - - if (typeof handle === 'undefined') { - return null; - } - - return handle; -} - function getEdgePositions( sourceNode: Node, sourceHandle: HandleElement | unknown, @@ -132,25 +59,29 @@ function getEdgePositions( function renderEdge( edge: Edge, props: EdgeRendererProps, + visibleNodes: Node[], nodes: Node[], selectedElements: Elements | null, elementsSelectable: boolean ) { - const sourceId = edge.source; const sourceHandleId = edge.sourceHandle || null; - const targetId = edge.target; const targetHandleId = edge.targetHandle || null; - const sourceNode = nodes.find((n) => n.id === sourceId); - const targetNode = nodes.find((n) => n.id === targetId); + const sourceNode = nodes.find((n) => n.id === edge.source); + const targetNode = nodes.find((n) => n.id === edge.target); + const renderEdge = visibleNodes.some((n) => n.id === edge.source || n.id == edge.target); + + if (!renderEdge) { + return null; + } if (!sourceNode) { - console.warn(`couldn't create edge for source id: ${sourceId}`); + console.warn(`couldn't create edge for source id: ${edge.source}`); return null; } if (!targetNode) { - console.warn(`couldn't create edge for target id: ${targetId}`); + console.warn(`couldn't create edge for target id: ${edge.target}`); return null; } @@ -224,7 +155,6 @@ function renderEdge( const EdgeRenderer = (props: EdgeRendererProps) => { const [tX, tY, tScale] = useStoreState((state) => state.transform); const edges = useStoreState((state) => state.edges); - const nodes = useStoreState((state) => state.nodes); const connectionNodeId = useStoreState((state) => state.connectionNodeId); const connectionHandleId = useStoreState((state) => state.connectionHandleId); const connectionHandleType = useStoreState((state) => state.connectionHandleType); @@ -234,6 +164,8 @@ const EdgeRenderer = (props: EdgeRendererProps) => { const elementsSelectable = useStoreState((state) => state.elementsSelectable); const width = useStoreState((state) => state.width); const height = useStoreState((state) => state.height); + const visibleNodes = useStoreState((state) => state.visibleNodes); + const nodes = useStoreState((state) => state.nodes); const { connectionLineType, arrowHeadColor, connectionLineStyle, connectionLineComponent } = props; @@ -248,10 +180,10 @@ const EdgeRenderer = (props: EdgeRendererProps) => { - {edges.map((edge: Edge) => renderEdge(edge, props, nodes, selectedElements, elementsSelectable))} + {edges.map((edge: Edge) => renderEdge(edge, props, visibleNodes, nodes, selectedElements, elementsSelectable))} {renderConnectionLine && ( d.id === handleId); + } + + if (typeof handle === 'undefined') { + return null; + } + + return handle; +} diff --git a/src/container/FlowRenderer/index.tsx b/src/container/FlowRenderer/index.tsx index d5edd351..cae1998a 100644 --- a/src/container/FlowRenderer/index.tsx +++ b/src/container/FlowRenderer/index.tsx @@ -19,7 +19,7 @@ interface FlowRendererProps | 'snapGrid' | 'connectionLineType' | 'arrowHeadColor' - | 'onlyRenderVisibleNodes' + | 'onlyRenderVisibleElements' | 'selectNodesOnDrag' > { children: ReactNode; diff --git a/src/container/GraphView/index.tsx b/src/container/GraphView/index.tsx index b5f9c15e..2e25c628 100644 --- a/src/container/GraphView/index.tsx +++ b/src/container/GraphView/index.tsx @@ -20,7 +20,7 @@ export interface GraphViewProps extends Omit actions.setMinZoom); const setMaxZoom = useStoreActions((actions) => actions.setMaxZoom); const setTranslateExtent = useStoreActions((actions) => actions.setTranslateExtent); + const setOnlyRenderVisibleElements = useStoreActions((actions) => actions.setOnlyRenderVisibleElements); const currentStore = useStore(); const { zoomIn, zoomOut, zoomTo, transform, fitView, initialized } = useZoomPanHelper(); @@ -187,6 +188,12 @@ const GraphView = ({ } }, [translateExtent]); + useEffect(() => { + if (typeof onlyRenderVisibleElements !== 'undefined') { + setOnlyRenderVisibleElements(onlyRenderVisibleElements); + } + }, [translateExtent]); + return ( void; onNodeDragStart?: (event: MouseEvent, node: Node) => void; onNodeDragStop?: (event: MouseEvent, node: Node) => void; - onlyRenderVisibleNodes: boolean; snapToGrid: boolean; snapGrid: [number, number]; } const NodeRenderer = (props: NodeRendererProps) => { - const nodes = useStoreState((state) => state.nodes); const transform = useStoreState((state) => state.transform); const selectedElements = useStoreState((state) => state.selectedElements); - const viewportBox = useStoreState((state) => state.viewportBox); const nodesDraggable = useStoreState((state) => state.nodesDraggable); const nodesConnectable = useStoreState((state) => state.nodesConnectable); const elementsSelectable = useStoreState((state) => state.elementsSelectable); + const visibleNodes = useStoreState((state) => state.visibleNodes); const transformStyle = useMemo( () => ({ @@ -35,11 +32,9 @@ const NodeRenderer = (props: NodeRendererProps) => { [transform[0], transform[1], transform[2]] ); - const nodesToRender = props.onlyRenderVisibleNodes ? getNodesInside(nodes, viewportBox, transform, true) : nodes; - return (
- {nodesToRender.map((node) => { + {visibleNodes.map((node) => { const nodeType = node.type || 'default'; const NodeComponent = (props.nodeTypes[nodeType] || props.nodeTypes.default) as ComponentType; diff --git a/src/container/ReactFlow/index.tsx b/src/container/ReactFlow/index.tsx index e59a2bb2..1e529ff6 100644 --- a/src/container/ReactFlow/index.tsx +++ b/src/container/ReactFlow/index.tsx @@ -88,7 +88,7 @@ export interface ReactFlowProps extends Omit, 'on multiSelectionKeyCode?: KeyCode; snapToGrid?: boolean; snapGrid?: [number, number]; - onlyRenderVisibleNodes?: boolean; + onlyRenderVisibleElements?: boolean; nodesDraggable?: boolean; nodesConnectable?: boolean; elementsSelectable?: boolean; @@ -142,7 +142,7 @@ const ReactFlow = ({ multiSelectionKeyCode = 'Meta', snapToGrid = false, snapGrid = [15, 15], - onlyRenderVisibleNodes = true, + onlyRenderVisibleElements = true, selectNodesOnDrag = true, nodesDraggable, nodesConnectable, @@ -200,7 +200,7 @@ const ReactFlow = ({ onConnectEnd={onConnectEnd} snapToGrid={snapToGrid} snapGrid={snapGrid} - onlyRenderVisibleNodes={onlyRenderVisibleNodes} + onlyRenderVisibleElements={onlyRenderVisibleElements} nodesDraggable={nodesDraggable} nodesConnectable={nodesConnectable} elementsSelectable={elementsSelectable} diff --git a/src/store/index.ts b/src/store/index.ts index 406ac647..41870cd3 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -45,10 +45,12 @@ export interface StoreModel { viewportBox: Computed; transform: Transform; elements: Elements; + visibleNodes: Computed; nodes: Computed; edges: Computed; selectedElements: Elements | null; selectedNodesBbox: Rect; + onlyRenderVisibleElements: boolean; d3Zoom: ZoomBehavior | null; d3Selection: D3Selection | null; @@ -131,6 +133,8 @@ export interface StoreModel { unsetUserSelection: Action; setMultiSelectionActive: Action; + + setOnlyRenderVisibleElements: Action; } export const storeModel: StoreModel = { @@ -140,9 +144,19 @@ export const storeModel: StoreModel = { transform: [0, 0, 1], elements: [], nodes: computed((state) => state.elements.filter(isNode)), + visibleNodes: computed((state) => { + if (!state.onlyRenderVisibleElements) { + return state.nodes; + } + + const viewportBox = { x: 0, y: 0, width: state.width, height: state.height }; + + return getNodesInside(state.nodes, viewportBox, state.transform, true); + }), edges: computed((state) => state.elements.filter(isEdge)), selectedElements: null, selectedNodesBbox: { x: 0, y: 0, width: 0, height: 0 }, + onlyRenderVisibleElements: true, d3Zoom: null, d3Selection: null, @@ -440,6 +454,10 @@ export const storeModel: StoreModel = { setMultiSelectionActive: action((state, isActive) => { state.multiSelectionActive = isActive; }), + + setOnlyRenderVisibleElements: action((state, onlyRenderVisible) => { + state.onlyRenderVisibleElements = onlyRenderVisible; + }), }; const nodeEnv: string = (typeof __ENV__ !== 'undefined' && __ENV__) as string;