diff --git a/example/src/Basic/index.tsx b/example/src/Basic/index.tsx index e8c650e1..0b5c574d 100644 --- a/example/src/Basic/index.tsx +++ b/example/src/Basic/index.tsx @@ -26,38 +26,40 @@ const initialNodes: Node[] = [ { id: '4', data: { label: 'Node 4' }, - position: { x: 0, y: 0 }, + position: { x: 100, y: 200 }, className: 'light', style: { backgroundColor: 'rgba(255, 0, 0, .2)' }, - width: 0, - height: 0, + width: 600, + height: 300, }, { id: '4a', data: { label: 'Node 4a' }, - position: { x: 70, y: 200 }, + position: { x: 115, y: 215 }, className: 'light', parentNode: '4', }, { id: '4b', data: { label: 'Node 4b' }, - position: { x: 0, y: 0 }, + position: { x: 250, y: 250 }, className: 'light', style: { backgroundColor: 'rgba(255, 0, 0, .2)' }, parentNode: '4', + height: 200, + width: 350, }, { id: '4b1', data: { label: 'Node 4b1' }, - position: { x: 150, y: 270 }, + position: { x: 270, y: 270 }, className: 'light', parentNode: '4b', }, { id: '4b2', data: { label: 'Node 4b2' }, - position: { x: 420, y: 370 }, + position: { x: 500, y: 400 }, className: 'light', parentNode: '4b', }, @@ -129,18 +131,9 @@ const BasicFlow = () => { setEdges((es) => applyEdgeChanges(changes, es)); }, []); - const nodesWithLabel = useMemo( - () => - nodes.map((n) => { - n.data = { ...n.data, label: `${n.width}x${n.height}` }; - return n; - }), - [nodes] - ); - return ( ) => { scale, xPos, yPos, + width, + height, isSelected, onClick, onMouseEnter, @@ -49,6 +51,7 @@ export default (NodeComponent: ComponentType) => { resizeObserver, dragHandle, zIndex, + isParentNode, }: WrapNodeProps) => { const { addSelectedElements, @@ -70,6 +73,8 @@ export default (NodeComponent: ComponentType) => { isSelectable || isDraggable || onClick || onMouseEnter || onMouseMove || onMouseLeave ? 'all' : 'none', // prevents jumping of nodes on start // opacity: isInitialized ? 1 : 0, + width: isParentNode && width !== null ? width : 'auto', + height: isParentNode && height !== null ? height : 'auto', ...style, }), [ @@ -84,6 +89,7 @@ export default (NodeComponent: ComponentType) => { onMouseEnter, onMouseMove, onMouseLeave, + isParentNode, ] ); @@ -200,10 +206,10 @@ export default (NodeComponent: ComponentType) => { ); useEffect(() => { - if (nodeElement.current && !isHidden && !isInitialized) { + if (nodeElement.current && !isHidden && (!isInitialized || isParentNode)) { updateNodeDimensions([{ id, nodeElement: nodeElement.current, forceUpdate: true }]); } - }, [id, isHidden, sourcePosition, targetPosition, isInitialized]); + }, [id, isHidden, sourcePosition, targetPosition, isInitialized, isParentNode]); useEffect(() => { if (nodeElement.current) { @@ -225,6 +231,7 @@ export default (NodeComponent: ComponentType) => { { selected: isSelected, selectable: isSelectable, + parent: isParentNode, }, ]); diff --git a/src/container/NodeRenderer/index.tsx b/src/container/NodeRenderer/index.tsx index e3deef5a..477e1658 100644 --- a/src/container/NodeRenderer/index.tsx +++ b/src/container/NodeRenderer/index.tsx @@ -1,9 +1,8 @@ -import React, { memo, useMemo, ComponentType, MouseEvent, Fragment, useEffect } from 'react'; +import React, { memo, useMemo, ComponentType, MouseEvent, Fragment } from 'react'; import shallow from 'zustand/shallow'; import { useStore } from '../../store'; import { Node, NodeTypesType, ReactFlowState, WrapNodeProps, SnapGrid } from '../../types'; -import { getRectOfNodes } from '../../utils/graph'; import useVisibleNodes from '../../hooks/useVisibleNodes'; interface NodeRendererProps { nodeTypes: NodeTypesType; @@ -63,7 +62,7 @@ function Node({ recursionDepth, ...props }: NodeProps) { - const onNodesChange = useStore((s) => s.onNodesChange); + // const onNodesChange = useStore((s) => s.onNodesChange); const NodeComponent = (props.nodeTypes[nodeType] || props.nodeTypes.default) as ComponentType; const isNodeDraggable = typeof isDraggable !== 'undefined' @@ -77,39 +76,13 @@ function Node({ typeof node.width !== 'undefined' && typeof node.height !== 'undefined'; - const { childNodes = [] } = node; - const childRect = useMemo(() => getRectOfNodes(childNodes), [childNodes]); - const isParentNode = !!childNodes.length; - - const style = useMemo(() => { - if (isParentNode) { - return { - ...node.style, - width: Math.floor(childRect.width) + 20, - height: Math.floor(childRect.height) + 20, - boxSizing: 'border-box', - } as React.CSSProperties; - } - return node.style; - }, [childRect.width, childRect.height, isParentNode, node.style]); - - useEffect(() => { - if (onNodesChange && isParentNode) { - onNodesChange([ - { - type: 'position', - id: node.id, - position: { x: Math.floor(childRect.x) - 10, y: Math.floor(childRect.y) - 10 }, - }, - ]); - } - }, [childRect.width, childRect.height, childRect.x, childRect.y, isParentNode]); + const isParentNode = !!node.childNodes?.length; return ( ); } diff --git a/src/store/index.ts b/src/store/index.ts index 977ac643..2c5ab2db 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -25,7 +25,7 @@ import { OnNodesChange, OnEdgesChange, EdgeChange, - NodePositionChange, + NodeDimensionChange, } from '../types'; import { isNode, isEdge, getRectOfNodes, getNodesInside, getConnectedEdges } from '../utils/graph'; import { getHandleBounds } from '../components/Nodes/utils'; @@ -194,9 +194,9 @@ const createStore = () => if (matchingNodes?.length) { onNodesChange( matchingNodes.map((n) => { - const change: NodePositionChange = { + const change: NodeDimensionChange = { id: n.id, - type: 'position', + type: 'dimensions', isDragging: !!isDragging, }; diff --git a/src/style.css b/src/style.css index 327cfbef..8fac54d9 100644 --- a/src/style.css +++ b/src/style.css @@ -95,6 +95,7 @@ user-select: none; pointer-events: all; transform-origin: 0 0; + box-sizing: border-box; } .react-flow__nodesselection { diff --git a/src/types/index.ts b/src/types/index.ts index 3ecfaa10..9a84ae20 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -12,15 +12,12 @@ export type Transform = [number, number, number]; export type NodeDimensionChange = { id: string; type: 'dimensions'; - dimensions: Dimensions; - handleBounds?: NodeHandleBounds; -}; -export type NodePositionChange = { - id: string; - type: 'position'; + dimensions?: Dimensions; position?: XYPosition; + handleBounds?: NodeHandleBounds; isDragging?: boolean; }; + export type NodeSelectionChange = { id: string; type: 'select'; @@ -30,7 +27,7 @@ export type NodeRemoveChange = { id: string; type: 'remove'; }; -export type NodeChange = NodeDimensionChange | NodePositionChange | NodeSelectionChange | NodeRemoveChange; +export type NodeChange = NodeDimensionChange | NodeSelectionChange | NodeRemoveChange; export type EdgeSelectionChange = NodeSelectionChange; export type EdgeRemoveChange = NodeRemoveChange; @@ -277,6 +274,8 @@ export interface WrapNodeProps { scale: number; xPos: number; yPos: number; + width?: number | null; + height?: number | null; isSelectable: boolean; isDraggable: boolean; isConnectable: boolean; @@ -302,6 +301,7 @@ export interface WrapNodeProps { resizeObserver: ResizeObserver | null; dragHandle?: string; zIndex: number; + isParentNode: boolean; } export type FitViewParams = { diff --git a/src/utils/graph.ts b/src/utils/graph.ts index b4cb5edc..179db8f7 100644 --- a/src/utils/graph.ts +++ b/src/utils/graph.ts @@ -301,17 +301,22 @@ function applyChanges(changes: NodeChange[] | EdgeChange[], elements: any[]): an if (currentChange) { switch (currentChange.type) { - case 'dimensions': { - res.push({ ...item, ...currentChange.dimensions, handleBounds: currentChange.handleBounds }); - return res; - } case 'select': { res.push({ ...item, isSelected: currentChange.isSelected }); return res; } - case 'position': { + case 'dimensions': { const updateItem = { ...item }; + if (typeof currentChange.dimensions !== 'undefined') { + updateItem.width = currentChange.dimensions.width; + updateItem.height = currentChange.dimensions.height; + } + + if (typeof currentChange.handleBounds !== 'undefined') { + updateItem.handleBounds = currentChange.handleBounds; + } + if (typeof currentChange.position !== 'undefined') { updateItem.position = currentChange.position; }