refactor(components): show displayName in react profiler correctly
This commit is contained in:
@@ -19,38 +19,43 @@ const defaultColors = {
|
|||||||
[BackgroundVariant.Lines]: '#eee',
|
[BackgroundVariant.Lines]: '#eee',
|
||||||
};
|
};
|
||||||
|
|
||||||
const Background = memo(
|
const Background = ({
|
||||||
({ variant = BackgroundVariant.Dots, gap = 24, size = 0.5, color, style, className }: BackgroundProps) => {
|
variant = BackgroundVariant.Dots,
|
||||||
const [x, y, scale] = useStoreState((s) => s.transform);
|
gap = 24,
|
||||||
|
size = 0.5,
|
||||||
|
color,
|
||||||
|
style,
|
||||||
|
className,
|
||||||
|
}: BackgroundProps) => {
|
||||||
|
const [x, y, scale] = useStoreState((s) => s.transform);
|
||||||
|
|
||||||
const bgClasses = cc(['react-flow__background', className]);
|
const bgClasses = cc(['react-flow__background', className]);
|
||||||
const scaledGap = gap * scale;
|
const scaledGap = gap * scale;
|
||||||
const xOffset = x % scaledGap;
|
const xOffset = x % scaledGap;
|
||||||
const yOffset = y % scaledGap;
|
const yOffset = y % scaledGap;
|
||||||
|
|
||||||
const bgSvgTile = useMemo(() => {
|
const bgSvgTile = useMemo(() => {
|
||||||
const isLines = variant === BackgroundVariant.Lines;
|
const isLines = variant === BackgroundVariant.Lines;
|
||||||
const bgColor = color ? color : defaultColors[variant];
|
const bgColor = color ? color : defaultColors[variant];
|
||||||
const path = isLines ? createGridLinesPath(scaledGap, size, bgColor) : createGridDotsPath(size, bgColor);
|
const path = isLines ? createGridLinesPath(scaledGap, size, bgColor) : createGridDotsPath(size, bgColor);
|
||||||
|
|
||||||
return encodeURIComponent(
|
return encodeURIComponent(
|
||||||
`<svg width="${scaledGap}" height="${scaledGap}" xmlns='http://www.w3.org/2000/svg'>${path}</svg>`
|
`<svg width="${scaledGap}" height="${scaledGap}" xmlns='http://www.w3.org/2000/svg'>${path}</svg>`
|
||||||
);
|
|
||||||
}, [variant, scaledGap, size, color]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={bgClasses}
|
|
||||||
style={{
|
|
||||||
...style,
|
|
||||||
backgroundImage: `url("data:image/svg+xml;utf8,${bgSvgTile}")`,
|
|
||||||
backgroundPosition: `${xOffset}px ${yOffset}px`,
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
);
|
);
|
||||||
}
|
}, [variant, scaledGap, size, color]);
|
||||||
);
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={bgClasses}
|
||||||
|
style={{
|
||||||
|
...style,
|
||||||
|
backgroundImage: `url("data:image/svg+xml;utf8,${bgSvgTile}")`,
|
||||||
|
backgroundPosition: `${xOffset}px ${yOffset}px`,
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
Background.displayName = 'Background';
|
Background.displayName = 'Background';
|
||||||
|
|
||||||
export default Background;
|
export default memo(Background);
|
||||||
|
|||||||
@@ -17,49 +17,47 @@ interface ControlProps extends React.HTMLAttributes<HTMLDivElement> {
|
|||||||
showInteractive?: boolean;
|
showInteractive?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Controls = memo(
|
const Controls = ({ style, showZoom = true, showFitView = true, showInteractive = true, className }: ControlProps) => {
|
||||||
({ style, showZoom = true, showFitView = true, showInteractive = true, className }: ControlProps) => {
|
const setInteractive = useStoreActions((actions) => actions.setInteractive);
|
||||||
const setInteractive = useStoreActions((actions) => actions.setInteractive);
|
const fitView = useStoreActions((actions) => actions.fitView);
|
||||||
const fitView = useStoreActions((actions) => actions.fitView);
|
const zoomIn = useStoreActions((actions) => actions.zoomIn);
|
||||||
const zoomIn = useStoreActions((actions) => actions.zoomIn);
|
const zoomOut = useStoreActions((actions) => actions.zoomOut);
|
||||||
const zoomOut = useStoreActions((actions) => actions.zoomOut);
|
|
||||||
|
|
||||||
const isInteractive = useStoreState((s) => s.nodesDraggable && s.nodesConnectable && s.elementsSelectable);
|
const isInteractive = useStoreState((s) => s.nodesDraggable && s.nodesConnectable && s.elementsSelectable);
|
||||||
const mapClasses = cc(['react-flow__controls', className]);
|
const mapClasses = cc(['react-flow__controls', className]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={mapClasses} style={style}>
|
<div className={mapClasses} style={style}>
|
||||||
{showZoom && (
|
{showZoom && (
|
||||||
<>
|
<>
|
||||||
<div className="react-flow__controls-button react-flow__controls-zoomin" onClick={() => zoomIn()}>
|
<div className="react-flow__controls-button react-flow__controls-zoomin" onClick={() => zoomIn()}>
|
||||||
<PlusIcon />
|
<PlusIcon />
|
||||||
</div>
|
|
||||||
<div className="react-flow__controls-button react-flow__controls-zoomout" onClick={() => zoomOut()}>
|
|
||||||
<MinusIcon />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{showFitView && (
|
|
||||||
<div
|
|
||||||
className="react-flow__controls-button react-flow__controls-fitview"
|
|
||||||
onClick={() => fitView({ padding: 0.1 })}
|
|
||||||
>
|
|
||||||
<FitviewIcon />
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
<div className="react-flow__controls-button react-flow__controls-zoomout" onClick={() => zoomOut()}>
|
||||||
{showInteractive && (
|
<MinusIcon />
|
||||||
<div
|
|
||||||
className="react-flow__controls-button react-flow__controls-interactive"
|
|
||||||
onClick={() => setInteractive(!isInteractive)}
|
|
||||||
>
|
|
||||||
{isInteractive ? <UnlockIcon /> : <LockIcon />}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
</>
|
||||||
</div>
|
)}
|
||||||
);
|
{showFitView && (
|
||||||
}
|
<div
|
||||||
);
|
className="react-flow__controls-button react-flow__controls-fitview"
|
||||||
|
onClick={() => fitView({ padding: 0.1 })}
|
||||||
|
>
|
||||||
|
<FitviewIcon />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{showInteractive && (
|
||||||
|
<div
|
||||||
|
className="react-flow__controls-button react-flow__controls-interactive"
|
||||||
|
onClick={() => setInteractive(!isInteractive)}
|
||||||
|
>
|
||||||
|
{isInteractive ? <UnlockIcon /> : <LockIcon />}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
Controls.displayName = 'Controls';
|
Controls.displayName = 'Controls';
|
||||||
|
|
||||||
export default Controls;
|
export default memo(Controls);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ interface MiniMapNodeProps {
|
|||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MiniMapNode = memo(({ x, y, width, height, style, color, borderRadius }: MiniMapNodeProps) => {
|
const MiniMapNode = ({ x, y, width, height, style, color, borderRadius }: MiniMapNodeProps) => {
|
||||||
const { background, backgroundColor } = style || {};
|
const { background, backgroundColor } = style || {};
|
||||||
const fill = (color || background || backgroundColor) as string;
|
const fill = (color || background || backgroundColor) as string;
|
||||||
|
|
||||||
@@ -26,8 +26,8 @@ const MiniMapNode = memo(({ x, y, width, height, style, color, borderRadius }: M
|
|||||||
fill={fill}
|
fill={fill}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
};
|
||||||
|
|
||||||
MiniMapNode.displayName = 'MiniMapNode';
|
MiniMapNode.displayName = 'MiniMapNode';
|
||||||
|
|
||||||
export default MiniMapNode;
|
export default memo(MiniMapNode);
|
||||||
|
|||||||
@@ -19,77 +19,75 @@ interface MiniMapProps extends React.HTMLAttributes<SVGSVGElement> {
|
|||||||
const defaultWidth = 200;
|
const defaultWidth = 200;
|
||||||
const defaultHeight = 150;
|
const defaultHeight = 150;
|
||||||
|
|
||||||
const MiniMap = memo(
|
const MiniMap = ({
|
||||||
({
|
style = { backgroundColor: '#f8f8f8' },
|
||||||
style = { backgroundColor: '#f8f8f8' },
|
className,
|
||||||
className,
|
nodeColor = '#ddd',
|
||||||
nodeColor = '#ddd',
|
nodeBorderRadius = 5,
|
||||||
nodeBorderRadius = 5,
|
maskColor = 'rgba(10, 10, 10, .25)',
|
||||||
maskColor = 'rgba(10, 10, 10, .25)',
|
}: MiniMapProps) => {
|
||||||
}: MiniMapProps) => {
|
const containerWidth = useStoreState((s) => s.width);
|
||||||
const containerWidth = useStoreState((s) => s.width);
|
const containerHeight = useStoreState((s) => s.height);
|
||||||
const containerHeight = useStoreState((s) => s.height);
|
const [tX, tY, tScale] = useStoreState((s) => s.transform);
|
||||||
const [tX, tY, tScale] = useStoreState((s) => s.transform);
|
const nodes = useStoreState((s) => s.nodes);
|
||||||
const nodes = useStoreState((s) => s.nodes);
|
|
||||||
|
|
||||||
const mapClasses = cc(['react-flow__minimap', className]);
|
const mapClasses = cc(['react-flow__minimap', className]);
|
||||||
const elementWidth = (style.width || defaultWidth)! as number;
|
const elementWidth = (style.width || defaultWidth)! as number;
|
||||||
const elementHeight = (style.height || defaultHeight)! as number;
|
const elementHeight = (style.height || defaultHeight)! as number;
|
||||||
const nodeColorFunc = (nodeColor instanceof Function ? nodeColor : () => nodeColor) as StringFunc;
|
const nodeColorFunc = (nodeColor instanceof Function ? nodeColor : () => nodeColor) as StringFunc;
|
||||||
const hasNodes = nodes && nodes.length;
|
const hasNodes = nodes && nodes.length;
|
||||||
const bb = getRectOfNodes(nodes);
|
const bb = getRectOfNodes(nodes);
|
||||||
const viewBB: Rect = {
|
const viewBB: Rect = {
|
||||||
x: -tX / tScale,
|
x: -tX / tScale,
|
||||||
y: -tY / tScale,
|
y: -tY / tScale,
|
||||||
width: containerWidth / tScale,
|
width: containerWidth / tScale,
|
||||||
height: containerHeight / tScale,
|
height: containerHeight / tScale,
|
||||||
};
|
};
|
||||||
const boundingRect = hasNodes ? getBoundsofRects(bb, viewBB) : viewBB;
|
const boundingRect = hasNodes ? getBoundsofRects(bb, viewBB) : viewBB;
|
||||||
const scaledWidth = boundingRect.width / elementWidth;
|
const scaledWidth = boundingRect.width / elementWidth;
|
||||||
const scaledHeight = boundingRect.height / elementHeight;
|
const scaledHeight = boundingRect.height / elementHeight;
|
||||||
const viewScale = Math.max(scaledWidth, scaledHeight);
|
const viewScale = Math.max(scaledWidth, scaledHeight);
|
||||||
const viewWidth = viewScale * elementWidth;
|
const viewWidth = viewScale * elementWidth;
|
||||||
const viewHeight = viewScale * elementHeight;
|
const viewHeight = viewScale * elementHeight;
|
||||||
const offset = 5 * viewScale;
|
const offset = 5 * viewScale;
|
||||||
const x = boundingRect.x - (viewWidth - boundingRect.width) / 2 - offset;
|
const x = boundingRect.x - (viewWidth - boundingRect.width) / 2 - offset;
|
||||||
const y = boundingRect.y - (viewHeight - boundingRect.height) / 2 - offset;
|
const y = boundingRect.y - (viewHeight - boundingRect.height) / 2 - offset;
|
||||||
const width = viewWidth + offset * 2;
|
const width = viewWidth + offset * 2;
|
||||||
const height = viewHeight + offset * 2;
|
const height = viewHeight + offset * 2;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
width={elementWidth}
|
width={elementWidth}
|
||||||
height={elementHeight}
|
height={elementHeight}
|
||||||
viewBox={`${x} ${y} ${width} ${height}`}
|
viewBox={`${x} ${y} ${width} ${height}`}
|
||||||
style={style}
|
style={style}
|
||||||
className={mapClasses}
|
className={mapClasses}
|
||||||
>
|
>
|
||||||
{nodes
|
{nodes
|
||||||
.filter((node) => !node.isHidden)
|
.filter((node) => !node.isHidden)
|
||||||
.map((node) => (
|
.map((node) => (
|
||||||
<MiniMapNode
|
<MiniMapNode
|
||||||
key={node.id}
|
key={node.id}
|
||||||
x={node.__rf.position.x}
|
x={node.__rf.position.x}
|
||||||
y={node.__rf.position.y}
|
y={node.__rf.position.y}
|
||||||
width={node.__rf.width}
|
width={node.__rf.width}
|
||||||
height={node.__rf.height}
|
height={node.__rf.height}
|
||||||
style={node.style}
|
style={node.style}
|
||||||
color={nodeColorFunc(node)}
|
color={nodeColorFunc(node)}
|
||||||
borderRadius={nodeBorderRadius}
|
borderRadius={nodeBorderRadius}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<path
|
<path
|
||||||
className="react-flow__minimap-mask"
|
className="react-flow__minimap-mask"
|
||||||
d={`M${x - offset},${y - offset}h${width + offset * 2}v${height + offset * 2}h${-width - offset * 2}z
|
d={`M${x - offset},${y - offset}h${width + offset * 2}v${height + offset * 2}h${-width - offset * 2}z
|
||||||
M${viewBB.x},${viewBB.y}h${viewBB.width}v${viewBB.height}h${-viewBB.width}z`}
|
M${viewBB.x},${viewBB.y}h${viewBB.width}v${viewBB.height}h${-viewBB.width}z`}
|
||||||
fill={maskColor}
|
fill={maskColor}
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
);
|
|
||||||
|
|
||||||
MiniMap.displayName = 'MiniMap';
|
MiniMap.displayName = 'MiniMap';
|
||||||
|
|
||||||
export default MiniMap;
|
export default memo(MiniMap);
|
||||||
|
|||||||
@@ -23,70 +23,68 @@ interface EdgeWrapperProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default (EdgeComponent: ComponentType<EdgeCompProps>) => {
|
export default (EdgeComponent: ComponentType<EdgeCompProps>) => {
|
||||||
const EdgeWrapper = memo(
|
const EdgeWrapper = ({
|
||||||
({
|
id,
|
||||||
id,
|
source,
|
||||||
source,
|
target,
|
||||||
target,
|
type,
|
||||||
type,
|
animated,
|
||||||
animated,
|
selected,
|
||||||
selected,
|
onClick,
|
||||||
onClick,
|
elementsSelectable,
|
||||||
elementsSelectable,
|
label,
|
||||||
label,
|
labelStyle,
|
||||||
labelStyle,
|
labelShowBg,
|
||||||
labelShowBg,
|
labelBgStyle,
|
||||||
labelBgStyle,
|
className,
|
||||||
className,
|
isHidden,
|
||||||
isHidden,
|
data,
|
||||||
data,
|
...rest
|
||||||
...rest
|
}: EdgeWrapperProps) => {
|
||||||
}: EdgeWrapperProps) => {
|
const setSelectedElements = useStoreActions((a) => a.setSelectedElements);
|
||||||
const setSelectedElements = useStoreActions((a) => a.setSelectedElements);
|
|
||||||
|
|
||||||
if (isHidden) {
|
if (isHidden) {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const edgeClasses = cc(['react-flow__edge', `react-flow__edge-${type}`, className, { selected, animated }]);
|
||||||
|
const edgeGroupStyle: CSSProperties = {
|
||||||
|
pointerEvents: elementsSelectable ? 'all' : 'none',
|
||||||
|
};
|
||||||
|
const onEdgeClick = (): void => {
|
||||||
|
if (!elementsSelectable) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const edgeClasses = cc(['react-flow__edge', `react-flow__edge-${type}`, className, { selected, animated }]);
|
setSelectedElements({ id, source, target });
|
||||||
const edgeGroupStyle: CSSProperties = {
|
|
||||||
pointerEvents: elementsSelectable ? 'all' : 'none',
|
|
||||||
};
|
|
||||||
const onEdgeClick = (): void => {
|
|
||||||
if (!elementsSelectable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setSelectedElements({ id, source, target });
|
if (onClick) {
|
||||||
|
onClick({ id, source, target, type });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (onClick) {
|
return (
|
||||||
onClick({ id, source, target, type });
|
<g className={edgeClasses} onClick={onEdgeClick} style={edgeGroupStyle}>
|
||||||
}
|
<EdgeComponent
|
||||||
};
|
id={id}
|
||||||
|
source={source}
|
||||||
return (
|
target={target}
|
||||||
<g className={edgeClasses} onClick={onEdgeClick} style={edgeGroupStyle}>
|
type={type}
|
||||||
<EdgeComponent
|
animated={animated}
|
||||||
id={id}
|
selected={selected}
|
||||||
source={source}
|
onClick={onClick}
|
||||||
target={target}
|
label={label}
|
||||||
type={type}
|
labelStyle={labelStyle}
|
||||||
animated={animated}
|
labelShowBg={labelShowBg}
|
||||||
selected={selected}
|
labelBgStyle={labelBgStyle}
|
||||||
onClick={onClick}
|
data={data}
|
||||||
label={label}
|
{...rest}
|
||||||
labelStyle={labelStyle}
|
/>
|
||||||
labelShowBg={labelShowBg}
|
</g>
|
||||||
labelBgStyle={labelBgStyle}
|
);
|
||||||
data={data}
|
};
|
||||||
{...rest}
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
EdgeWrapper.displayName = 'EdgeWrapper';
|
EdgeWrapper.displayName = 'EdgeWrapper';
|
||||||
|
|
||||||
return EdgeWrapper;
|
return memo(EdgeWrapper);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -130,47 +130,45 @@ function onMouseDown(
|
|||||||
document.addEventListener('mouseup', onMouseUp);
|
document.addEventListener('mouseup', onMouseUp);
|
||||||
}
|
}
|
||||||
|
|
||||||
const BaseHandle = memo(
|
const BaseHandle = ({
|
||||||
({
|
type,
|
||||||
type,
|
nodeId,
|
||||||
nodeId,
|
onConnect,
|
||||||
onConnect,
|
position,
|
||||||
position,
|
setConnectionNodeId,
|
||||||
setConnectionNodeId,
|
setPosition,
|
||||||
setPosition,
|
className,
|
||||||
|
id = false,
|
||||||
|
isValidConnection,
|
||||||
|
...rest
|
||||||
|
}: BaseHandleProps) => {
|
||||||
|
const isTarget = type === 'target';
|
||||||
|
const handleClasses = cc([
|
||||||
|
'react-flow__handle',
|
||||||
|
`react-flow__handle-${position}`,
|
||||||
|
'nodrag',
|
||||||
className,
|
className,
|
||||||
id = false,
|
{
|
||||||
isValidConnection,
|
source: !isTarget,
|
||||||
...rest
|
target: isTarget,
|
||||||
}: BaseHandleProps) => {
|
},
|
||||||
const isTarget = type === 'target';
|
]);
|
||||||
const handleClasses = cc([
|
|
||||||
'react-flow__handle',
|
|
||||||
`react-flow__handle-${position}`,
|
|
||||||
'nodrag',
|
|
||||||
className,
|
|
||||||
{
|
|
||||||
source: !isTarget,
|
|
||||||
target: isTarget,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const nodeIdWithHandleId = id ? `${nodeId}__${id}` : nodeId;
|
const nodeIdWithHandleId = id ? `${nodeId}__${id}` : nodeId;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-nodeid={nodeIdWithHandleId}
|
data-nodeid={nodeIdWithHandleId}
|
||||||
data-handlepos={position}
|
data-handlepos={position}
|
||||||
className={handleClasses}
|
className={handleClasses}
|
||||||
onMouseDown={(evt) =>
|
onMouseDown={(evt) =>
|
||||||
onMouseDown(evt, nodeIdWithHandleId, setConnectionNodeId, setPosition, onConnect, isTarget, isValidConnection)
|
onMouseDown(evt, nodeIdWithHandleId, setConnectionNodeId, setPosition, onConnect, isTarget, isValidConnection)
|
||||||
}
|
}
|
||||||
{...rest}
|
{...rest}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
);
|
|
||||||
|
|
||||||
BaseHandle.displayName = 'BaseHandle';
|
BaseHandle.displayName = 'BaseHandle';
|
||||||
|
|
||||||
export default BaseHandle;
|
export default memo(BaseHandle);
|
||||||
|
|||||||
@@ -7,44 +7,42 @@ import NodeIdContext from '../../contexts/NodeIdContext';
|
|||||||
|
|
||||||
import { HandleProps, ElementId, Position, Connection } from '../../types';
|
import { HandleProps, ElementId, Position, Connection } from '../../types';
|
||||||
|
|
||||||
const Handle = memo(
|
const Handle = ({
|
||||||
({
|
type = 'source',
|
||||||
type = 'source',
|
position = Position.Top,
|
||||||
position = Position.Top,
|
onConnect = () => {},
|
||||||
onConnect = () => {},
|
isValidConnection = () => true,
|
||||||
isValidConnection = () => true,
|
isConnectable = true,
|
||||||
isConnectable = true,
|
style,
|
||||||
style,
|
className,
|
||||||
className,
|
id,
|
||||||
id,
|
}: HandleProps) => {
|
||||||
}: HandleProps) => {
|
const nodeId = useContext(NodeIdContext) as ElementId;
|
||||||
const nodeId = useContext(NodeIdContext) as ElementId;
|
const setPosition = useStoreActions((a) => a.setConnectionPosition);
|
||||||
const setPosition = useStoreActions((a) => a.setConnectionPosition);
|
const setConnectionNodeId = useStoreActions((a) => a.setConnectionNodeId);
|
||||||
const setConnectionNodeId = useStoreActions((a) => a.setConnectionNodeId);
|
const onConnectAction = useStoreState((s) => s.onConnect);
|
||||||
const onConnectAction = useStoreState((s) => s.onConnect);
|
const onConnectExtended = (params: Connection) => {
|
||||||
const onConnectExtended = (params: Connection) => {
|
onConnectAction(params);
|
||||||
onConnectAction(params);
|
onConnect(params);
|
||||||
onConnect(params);
|
};
|
||||||
};
|
const handleClasses = cc([className, { connectable: isConnectable }]);
|
||||||
const handleClasses = cc([className, { connectable: isConnectable }]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseHandle
|
<BaseHandle
|
||||||
className={handleClasses}
|
className={handleClasses}
|
||||||
id={id}
|
id={id}
|
||||||
nodeId={nodeId}
|
nodeId={nodeId}
|
||||||
setPosition={setPosition}
|
setPosition={setPosition}
|
||||||
setConnectionNodeId={setConnectionNodeId}
|
setConnectionNodeId={setConnectionNodeId}
|
||||||
onConnect={onConnectExtended}
|
onConnect={onConnectExtended}
|
||||||
type={type}
|
type={type}
|
||||||
position={position}
|
position={position}
|
||||||
isValidConnection={isValidConnection}
|
isValidConnection={isValidConnection}
|
||||||
style={style}
|
style={style}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
);
|
|
||||||
|
|
||||||
Handle.displayName = 'Handle';
|
Handle.displayName = 'Handle';
|
||||||
|
|
||||||
export default Handle;
|
export default memo(Handle);
|
||||||
|
|||||||
@@ -3,16 +3,19 @@ import React, { memo } from 'react';
|
|||||||
import Handle from '../../components/Handle';
|
import Handle from '../../components/Handle';
|
||||||
import { NodeProps, Position } from '../../types';
|
import { NodeProps, Position } from '../../types';
|
||||||
|
|
||||||
const DefaultNode = memo(
|
const DefaultNode = ({
|
||||||
({ data, isConnectable, targetPosition = Position.Top, sourcePosition = Position.Bottom }: NodeProps) => (
|
data,
|
||||||
<>
|
isConnectable,
|
||||||
<Handle type="target" position={targetPosition} isConnectable={isConnectable} />
|
targetPosition = Position.Top,
|
||||||
{data.label}
|
sourcePosition = Position.Bottom,
|
||||||
<Handle type="source" position={sourcePosition} isConnectable={isConnectable} />
|
}: NodeProps) => (
|
||||||
</>
|
<>
|
||||||
)
|
<Handle type="target" position={targetPosition} isConnectable={isConnectable} />
|
||||||
|
{data.label}
|
||||||
|
<Handle type="source" position={sourcePosition} isConnectable={isConnectable} />
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
DefaultNode.displayName = 'DefaultNode';
|
DefaultNode.displayName = 'DefaultNode';
|
||||||
|
|
||||||
export default DefaultNode;
|
export default memo(DefaultNode);
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import React, { memo } from 'react';
|
|||||||
import Handle from '../../components/Handle';
|
import Handle from '../../components/Handle';
|
||||||
import { NodeProps, Position } from '../../types';
|
import { NodeProps, Position } from '../../types';
|
||||||
|
|
||||||
const InputNode = memo(({ data, isConnectable, sourcePosition = Position.Bottom }: NodeProps) => (
|
const InputNode = ({ data, isConnectable, sourcePosition = Position.Bottom }: NodeProps) => (
|
||||||
<>
|
<>
|
||||||
{data.label}
|
{data.label}
|
||||||
<Handle type="source" position={sourcePosition} isConnectable={isConnectable} />
|
<Handle type="source" position={sourcePosition} isConnectable={isConnectable} />
|
||||||
</>
|
</>
|
||||||
));
|
);
|
||||||
|
|
||||||
InputNode.displayName = 'InputNode';
|
InputNode.displayName = 'InputNode';
|
||||||
|
|
||||||
export default InputNode;
|
export default memo(InputNode);
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import React, { memo } from 'react';
|
|||||||
import Handle from '../../components/Handle';
|
import Handle from '../../components/Handle';
|
||||||
import { NodeProps, Position } from '../../types';
|
import { NodeProps, Position } from '../../types';
|
||||||
|
|
||||||
const OutputNode = memo(({ data, isConnectable, targetPosition = Position.Top }: NodeProps) => (
|
const OutputNode = ({ data, isConnectable, targetPosition = Position.Top }: NodeProps) => (
|
||||||
<>
|
<>
|
||||||
<Handle type="target" position={targetPosition} isConnectable={isConnectable} />
|
<Handle type="target" position={targetPosition} isConnectable={isConnectable} />
|
||||||
{data.label}
|
{data.label}
|
||||||
</>
|
</>
|
||||||
));
|
);
|
||||||
|
|
||||||
OutputNode.displayName = 'OutputNode';
|
OutputNode.displayName = 'OutputNode';
|
||||||
|
|
||||||
export default OutputNode;
|
export default memo(OutputNode);
|
||||||
|
|||||||
+175
-177
@@ -160,192 +160,190 @@ const onStop = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
||||||
const NodeWrapper = memo(
|
const NodeWrapper = ({
|
||||||
({
|
id,
|
||||||
id,
|
type,
|
||||||
type,
|
data,
|
||||||
data,
|
transform,
|
||||||
transform,
|
xPos,
|
||||||
xPos,
|
yPos,
|
||||||
yPos,
|
selected,
|
||||||
selected,
|
onClick,
|
||||||
onClick,
|
onMouseEnter,
|
||||||
onMouseEnter,
|
onMouseMove,
|
||||||
onMouseMove,
|
onMouseLeave,
|
||||||
onMouseLeave,
|
onContextMenu,
|
||||||
onContextMenu,
|
onNodeDragStart,
|
||||||
onNodeDragStart,
|
onNodeDragStop,
|
||||||
onNodeDragStop,
|
style,
|
||||||
style,
|
className,
|
||||||
|
isDraggable,
|
||||||
|
isSelectable,
|
||||||
|
isConnectable,
|
||||||
|
selectNodesOnDrag,
|
||||||
|
sourcePosition,
|
||||||
|
targetPosition,
|
||||||
|
isHidden,
|
||||||
|
}: WrapNodeProps) => {
|
||||||
|
const updateNodeDimensions = useStoreActions((a) => a.updateNodeDimensions);
|
||||||
|
const setSelectedElements = useStoreActions((a) => a.setSelectedElements);
|
||||||
|
const updateNodePos = useStoreActions((a) => a.updateNodePos);
|
||||||
|
|
||||||
|
const nodeElement = useRef<HTMLDivElement>(null);
|
||||||
|
const [offset, setOffset] = useState({ x: 0, y: 0 });
|
||||||
|
const [isDragging, setDragging] = useState(false);
|
||||||
|
const position = { x: xPos, y: yPos };
|
||||||
|
const nodeClasses = cc([
|
||||||
|
'react-flow__node',
|
||||||
|
`react-flow__node-${type}`,
|
||||||
className,
|
className,
|
||||||
isDraggable,
|
{
|
||||||
isSelectable,
|
selected,
|
||||||
isConnectable,
|
selectable: isSelectable,
|
||||||
selectNodesOnDrag,
|
},
|
||||||
sourcePosition,
|
]);
|
||||||
targetPosition,
|
const node = { id, type, position, data };
|
||||||
isHidden,
|
const onMouseEnterHandler = useMemo(() => {
|
||||||
}: WrapNodeProps) => {
|
if (!onMouseEnter || isDragging) {
|
||||||
const updateNodeDimensions = useStoreActions((a) => a.updateNodeDimensions);
|
|
||||||
const setSelectedElements = useStoreActions((a) => a.setSelectedElements);
|
|
||||||
const updateNodePos = useStoreActions((a) => a.updateNodePos);
|
|
||||||
|
|
||||||
const nodeElement = useRef<HTMLDivElement>(null);
|
|
||||||
const [offset, setOffset] = useState({ x: 0, y: 0 });
|
|
||||||
const [isDragging, setDragging] = useState(false);
|
|
||||||
const position = { x: xPos, y: yPos };
|
|
||||||
const nodeClasses = cc([
|
|
||||||
'react-flow__node',
|
|
||||||
`react-flow__node-${type}`,
|
|
||||||
className,
|
|
||||||
{
|
|
||||||
selected,
|
|
||||||
selectable: isSelectable,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
const node = { id, type, position, data };
|
|
||||||
const onMouseEnterHandler = useMemo(() => {
|
|
||||||
if (!onMouseEnter || isDragging) {
|
|
||||||
return noop;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (evt: MouseEvent) => onMouseEnter(evt, node);
|
|
||||||
}, [onMouseEnter, isDragging]);
|
|
||||||
|
|
||||||
const onMouseMoveHandler = useMemo(() => {
|
|
||||||
if (!onMouseMove || isDragging) {
|
|
||||||
return noop;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (evt: MouseEvent) => onMouseMove(evt, node);
|
|
||||||
}, [onMouseMove, isDragging]);
|
|
||||||
|
|
||||||
const onMouseLeaveHandler = useMemo(() => {
|
|
||||||
if (!onMouseLeave || isDragging) {
|
|
||||||
return noop;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (evt: MouseEvent) => onMouseLeave(evt, node);
|
|
||||||
}, [onMouseLeave, isDragging]);
|
|
||||||
|
|
||||||
const onContextMenuHandler = useMemo(() => {
|
|
||||||
if (!onContextMenu) {
|
|
||||||
return noop;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (evt: MouseEvent) => onContextMenu(evt, node);
|
|
||||||
}, [onContextMenu]);
|
|
||||||
|
|
||||||
const onSelectNodeHandler = useCallback(() => {
|
|
||||||
if (!isDraggable && isSelectable) {
|
|
||||||
setSelectedElements({ id: node.id, type: node.type } as Node);
|
|
||||||
|
|
||||||
if (onClick) {
|
|
||||||
onClick(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return noop;
|
return noop;
|
||||||
}, [isSelectable, isDraggable, node]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (nodeElement.current) {
|
|
||||||
updateNodeDimensions({ id, nodeElement: nodeElement.current });
|
|
||||||
|
|
||||||
const resizeObserver = new ResizeObserver((entries) => {
|
|
||||||
for (let _ of entries) {
|
|
||||||
updateNodeDimensions({ id, nodeElement: nodeElement.current! });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
resizeObserver.observe(nodeElement.current);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
if (resizeObserver && nodeElement.current) {
|
|
||||||
resizeObserver.unobserve(nodeElement.current);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}, [id]);
|
|
||||||
|
|
||||||
if (isHidden) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodeStyle: CSSProperties = {
|
return (evt: MouseEvent) => onMouseEnter(evt, node);
|
||||||
zIndex: selected ? 10 : 3,
|
}, [onMouseEnter, isDragging]);
|
||||||
transform: `translate(${xPos}px,${yPos}px)`,
|
|
||||||
pointerEvents: isSelectable || isDraggable ? 'all' : 'none',
|
|
||||||
...style,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
const onMouseMoveHandler = useMemo(() => {
|
||||||
<DraggableCore
|
if (!onMouseMove || isDragging) {
|
||||||
onStart={(evt) =>
|
return noop;
|
||||||
onStart({
|
}
|
||||||
evt: evt as MouseEvent,
|
|
||||||
selectNodesOnDrag,
|
return (evt: MouseEvent) => onMouseMove(evt, node);
|
||||||
isSelectable,
|
}, [onMouseMove, isDragging]);
|
||||||
onNodeDragStart,
|
|
||||||
id,
|
const onMouseLeaveHandler = useMemo(() => {
|
||||||
type,
|
if (!onMouseLeave || isDragging) {
|
||||||
data,
|
return noop;
|
||||||
setOffset,
|
}
|
||||||
transform,
|
|
||||||
position,
|
return (evt: MouseEvent) => onMouseLeave(evt, node);
|
||||||
setSelectedElements,
|
}, [onMouseLeave, isDragging]);
|
||||||
})
|
|
||||||
|
const onContextMenuHandler = useMemo(() => {
|
||||||
|
if (!onContextMenu) {
|
||||||
|
return noop;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (evt: MouseEvent) => onContextMenu(evt, node);
|
||||||
|
}, [onContextMenu]);
|
||||||
|
|
||||||
|
const onSelectNodeHandler = useCallback(() => {
|
||||||
|
if (!isDraggable && isSelectable) {
|
||||||
|
setSelectedElements({ id: node.id, type: node.type } as Node);
|
||||||
|
|
||||||
|
if (onClick) {
|
||||||
|
onClick(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return noop;
|
||||||
|
}, [isSelectable, isDraggable, node]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (nodeElement.current) {
|
||||||
|
updateNodeDimensions({ id, nodeElement: nodeElement.current });
|
||||||
|
|
||||||
|
const resizeObserver = new ResizeObserver((entries) => {
|
||||||
|
for (let _ of entries) {
|
||||||
|
updateNodeDimensions({ id, nodeElement: nodeElement.current! });
|
||||||
}
|
}
|
||||||
onDrag={(evt) => onDrag({ evt: evt as MouseEvent, setDragging, id, offset, transform, updateNodePos })}
|
});
|
||||||
onStop={() =>
|
|
||||||
onStop({
|
resizeObserver.observe(nodeElement.current);
|
||||||
onNodeDragStop,
|
|
||||||
selectNodesOnDrag,
|
return () => {
|
||||||
isSelectable,
|
if (resizeObserver && nodeElement.current) {
|
||||||
onClick,
|
resizeObserver.unobserve(nodeElement.current);
|
||||||
isDragging,
|
|
||||||
setDragging,
|
|
||||||
id,
|
|
||||||
type,
|
|
||||||
position,
|
|
||||||
data,
|
|
||||||
setSelectedElements,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
scale={transform[2]}
|
};
|
||||||
disabled={!isDraggable}
|
}
|
||||||
cancel=".nodrag"
|
|
||||||
>
|
return;
|
||||||
<div
|
}, [id]);
|
||||||
className={nodeClasses}
|
|
||||||
ref={nodeElement}
|
if (isHidden) {
|
||||||
style={nodeStyle}
|
return null;
|
||||||
onMouseEnter={onMouseEnterHandler}
|
|
||||||
onMouseMove={onMouseMoveHandler}
|
|
||||||
onMouseLeave={onMouseLeaveHandler}
|
|
||||||
onContextMenu={onContextMenuHandler}
|
|
||||||
onClick={onSelectNodeHandler}
|
|
||||||
>
|
|
||||||
<Provider value={id}>
|
|
||||||
<NodeComponent
|
|
||||||
id={id}
|
|
||||||
data={data}
|
|
||||||
type={type}
|
|
||||||
selected={selected}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
sourcePosition={sourcePosition}
|
|
||||||
targetPosition={targetPosition}
|
|
||||||
/>
|
|
||||||
</Provider>
|
|
||||||
</div>
|
|
||||||
</DraggableCore>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
const nodeStyle: CSSProperties = {
|
||||||
|
zIndex: selected ? 10 : 3,
|
||||||
|
transform: `translate(${xPos}px,${yPos}px)`,
|
||||||
|
pointerEvents: isSelectable || isDraggable ? 'all' : 'none',
|
||||||
|
...style,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DraggableCore
|
||||||
|
onStart={(evt) =>
|
||||||
|
onStart({
|
||||||
|
evt: evt as MouseEvent,
|
||||||
|
selectNodesOnDrag,
|
||||||
|
isSelectable,
|
||||||
|
onNodeDragStart,
|
||||||
|
id,
|
||||||
|
type,
|
||||||
|
data,
|
||||||
|
setOffset,
|
||||||
|
transform,
|
||||||
|
position,
|
||||||
|
setSelectedElements,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onDrag={(evt) => onDrag({ evt: evt as MouseEvent, setDragging, id, offset, transform, updateNodePos })}
|
||||||
|
onStop={() =>
|
||||||
|
onStop({
|
||||||
|
onNodeDragStop,
|
||||||
|
selectNodesOnDrag,
|
||||||
|
isSelectable,
|
||||||
|
onClick,
|
||||||
|
isDragging,
|
||||||
|
setDragging,
|
||||||
|
id,
|
||||||
|
type,
|
||||||
|
position,
|
||||||
|
data,
|
||||||
|
setSelectedElements,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
scale={transform[2]}
|
||||||
|
disabled={!isDraggable}
|
||||||
|
cancel=".nodrag"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={nodeClasses}
|
||||||
|
ref={nodeElement}
|
||||||
|
style={nodeStyle}
|
||||||
|
onMouseEnter={onMouseEnterHandler}
|
||||||
|
onMouseMove={onMouseMoveHandler}
|
||||||
|
onMouseLeave={onMouseLeaveHandler}
|
||||||
|
onContextMenu={onContextMenuHandler}
|
||||||
|
onClick={onSelectNodeHandler}
|
||||||
|
>
|
||||||
|
<Provider value={id}>
|
||||||
|
<NodeComponent
|
||||||
|
id={id}
|
||||||
|
data={data}
|
||||||
|
type={type}
|
||||||
|
selected={selected}
|
||||||
|
isConnectable={isConnectable}
|
||||||
|
sourcePosition={sourcePosition}
|
||||||
|
targetPosition={targetPosition}
|
||||||
|
/>
|
||||||
|
</Provider>
|
||||||
|
</div>
|
||||||
|
</DraggableCore>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
NodeWrapper.displayName = 'NodeWrapper';
|
NodeWrapper.displayName = 'NodeWrapper';
|
||||||
|
|
||||||
return NodeWrapper;
|
return memo(NodeWrapper);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* made a selectio with on or several nodes
|
* made a selectio with on or several nodes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, memo } from 'react';
|
import React, { useState } from 'react';
|
||||||
import ReactDraggable from 'react-draggable';
|
import ReactDraggable from 'react-draggable';
|
||||||
|
|
||||||
import { useStoreState, useStoreActions } from '../../store/hooks';
|
import { useStoreState, useStoreActions } from '../../store/hooks';
|
||||||
@@ -27,7 +27,7 @@ function getStartPositions(nodes: Node[]): StartPositions {
|
|||||||
}, startPositions);
|
}, startPositions);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default memo(() => {
|
export default () => {
|
||||||
const [offset, setOffset] = useState<XYPosition>({ x: 0, y: 0 });
|
const [offset, setOffset] = useState<XYPosition>({ x: 0, y: 0 });
|
||||||
const [startPositions, setStartPositions] = useState<StartPositions>({});
|
const [startPositions, setStartPositions] = useState<StartPositions>({});
|
||||||
const [tX, tY, tScale] = useStoreState((s) => s.transform);
|
const [tX, tY, tScale] = useStoreState((s) => s.transform);
|
||||||
@@ -110,4 +110,4 @@ export default memo(() => {
|
|||||||
</ReactDraggable>
|
</ReactDraggable>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
};
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ function renderEdge(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const EdgeRenderer = memo((props: EdgeRendererProps) => {
|
const EdgeRenderer = (props: EdgeRendererProps) => {
|
||||||
const [tX, tY, tScale] = useStoreState((s) => s.transform);
|
const [tX, tY, tScale] = useStoreState((s) => s.transform);
|
||||||
const edges = useStoreState((s) => s.edges);
|
const edges = useStoreState((s) => s.edges);
|
||||||
const nodes = useStoreState((s) => s.nodes);
|
const nodes = useStoreState((s) => s.nodes);
|
||||||
@@ -234,8 +234,8 @@ const EdgeRenderer = memo((props: EdgeRendererProps) => {
|
|||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
});
|
};
|
||||||
|
|
||||||
EdgeRenderer.displayName = 'EdgeRenderer';
|
EdgeRenderer.displayName = 'EdgeRenderer';
|
||||||
|
|
||||||
export default EdgeRenderer;
|
export default memo(EdgeRenderer);
|
||||||
|
|||||||
+175
-177
@@ -62,200 +62,198 @@ export interface GraphViewProps {
|
|||||||
zoomOnDoubleClick: boolean;
|
zoomOnDoubleClick: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const GraphView = memo(
|
const GraphView = ({
|
||||||
({
|
nodeTypes,
|
||||||
nodeTypes,
|
edgeTypes,
|
||||||
edgeTypes,
|
onMove,
|
||||||
|
onMoveStart,
|
||||||
|
onMoveEnd,
|
||||||
|
onLoad,
|
||||||
|
onElementClick,
|
||||||
|
onNodeMouseEnter,
|
||||||
|
onNodeMouseMove,
|
||||||
|
onNodeMouseLeave,
|
||||||
|
onNodeContextMenu,
|
||||||
|
onNodeDragStart,
|
||||||
|
onNodeDragStop,
|
||||||
|
connectionLineType,
|
||||||
|
connectionLineStyle,
|
||||||
|
selectionKeyCode,
|
||||||
|
onElementsRemove,
|
||||||
|
deleteKeyCode,
|
||||||
|
elements,
|
||||||
|
onConnect,
|
||||||
|
snapToGrid,
|
||||||
|
snapGrid,
|
||||||
|
onlyRenderVisibleNodes,
|
||||||
|
nodesDraggable,
|
||||||
|
nodesConnectable,
|
||||||
|
elementsSelectable,
|
||||||
|
selectNodesOnDrag,
|
||||||
|
minZoom,
|
||||||
|
maxZoom,
|
||||||
|
defaultZoom,
|
||||||
|
defaultPosition,
|
||||||
|
arrowHeadColor,
|
||||||
|
markerEndId,
|
||||||
|
zoomOnScroll,
|
||||||
|
zoomOnDoubleClick,
|
||||||
|
}: GraphViewProps) => {
|
||||||
|
const zoomPane = useRef<HTMLDivElement>(null);
|
||||||
|
const rendererNode = useRef<HTMLDivElement>(null);
|
||||||
|
const width = useStoreState((s) => s.width);
|
||||||
|
const height = useStoreState((s) => s.height);
|
||||||
|
const d3Initialised = useStoreState((s) => s.d3Initialised);
|
||||||
|
const nodesSelectionActive = useStoreState((s) => s.nodesSelectionActive);
|
||||||
|
const updateSize = useStoreActions((actions) => actions.updateSize);
|
||||||
|
const setNodesSelection = useStoreActions((actions) => actions.setNodesSelection);
|
||||||
|
const setOnConnect = useStoreActions((a) => a.setOnConnect);
|
||||||
|
const setSnapGrid = useStoreActions((actions) => actions.setSnapGrid);
|
||||||
|
const setNodesDraggable = useStoreActions((actions) => actions.setNodesDraggable);
|
||||||
|
const setNodesConnectable = useStoreActions((actions) => actions.setNodesConnectable);
|
||||||
|
const setElementsSelectable = useStoreActions((actions) => actions.setElementsSelectable);
|
||||||
|
const setInitTransform = useStoreActions((actions) => actions.setInitTransform);
|
||||||
|
const setMinMaxZoom = useStoreActions((actions) => actions.setMinMaxZoom);
|
||||||
|
const fitView = useStoreActions((actions) => actions.fitView);
|
||||||
|
const zoom = useStoreActions((actions) => actions.zoom);
|
||||||
|
|
||||||
|
const selectionKeyPressed = useKeyPress(selectionKeyCode);
|
||||||
|
|
||||||
|
const onZoomPaneClick = () => setNodesSelection({ isActive: false });
|
||||||
|
|
||||||
|
const updateDimensions = () => {
|
||||||
|
if (!rendererNode.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size = getDimensions(rendererNode.current);
|
||||||
|
|
||||||
|
if (size.height === 0 || size.width === 0) {
|
||||||
|
throw new Error('The React Flow parent container needs a width and a height to render the graph.');
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSize(size);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let resizeObserver: ResizeObserver;
|
||||||
|
|
||||||
|
updateDimensions();
|
||||||
|
window.onresize = updateDimensions;
|
||||||
|
|
||||||
|
if (onConnect) {
|
||||||
|
setOnConnect(onConnect);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rendererNode.current) {
|
||||||
|
resizeObserver = new ResizeObserver((entries) => {
|
||||||
|
for (let _ of entries) {
|
||||||
|
updateDimensions();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
resizeObserver.observe(rendererNode.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.onresize = null;
|
||||||
|
|
||||||
|
if (resizeObserver && rendererNode.current) {
|
||||||
|
resizeObserver.unobserve(rendererNode.current!);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useD3Zoom({
|
||||||
|
zoomPane,
|
||||||
onMove,
|
onMove,
|
||||||
onMoveStart,
|
onMoveStart,
|
||||||
onMoveEnd,
|
onMoveEnd,
|
||||||
onLoad,
|
selectionKeyPressed,
|
||||||
onElementClick,
|
|
||||||
onNodeMouseEnter,
|
|
||||||
onNodeMouseMove,
|
|
||||||
onNodeMouseLeave,
|
|
||||||
onNodeContextMenu,
|
|
||||||
onNodeDragStart,
|
|
||||||
onNodeDragStop,
|
|
||||||
connectionLineType,
|
|
||||||
connectionLineStyle,
|
|
||||||
selectionKeyCode,
|
|
||||||
onElementsRemove,
|
|
||||||
deleteKeyCode,
|
|
||||||
elements,
|
|
||||||
onConnect,
|
|
||||||
snapToGrid,
|
|
||||||
snapGrid,
|
|
||||||
onlyRenderVisibleNodes,
|
|
||||||
nodesDraggable,
|
|
||||||
nodesConnectable,
|
|
||||||
elementsSelectable,
|
|
||||||
selectNodesOnDrag,
|
|
||||||
minZoom,
|
|
||||||
maxZoom,
|
|
||||||
defaultZoom,
|
|
||||||
defaultPosition,
|
|
||||||
arrowHeadColor,
|
|
||||||
markerEndId,
|
|
||||||
zoomOnScroll,
|
zoomOnScroll,
|
||||||
zoomOnDoubleClick,
|
zoomOnDoubleClick,
|
||||||
}: GraphViewProps) => {
|
});
|
||||||
const zoomPane = useRef<HTMLDivElement>(null);
|
|
||||||
const rendererNode = useRef<HTMLDivElement>(null);
|
|
||||||
const width = useStoreState((s) => s.width);
|
|
||||||
const height = useStoreState((s) => s.height);
|
|
||||||
const d3Initialised = useStoreState((s) => s.d3Initialised);
|
|
||||||
const nodesSelectionActive = useStoreState((s) => s.nodesSelectionActive);
|
|
||||||
const updateSize = useStoreActions((actions) => actions.updateSize);
|
|
||||||
const setNodesSelection = useStoreActions((actions) => actions.setNodesSelection);
|
|
||||||
const setOnConnect = useStoreActions((a) => a.setOnConnect);
|
|
||||||
const setSnapGrid = useStoreActions((actions) => actions.setSnapGrid);
|
|
||||||
const setNodesDraggable = useStoreActions((actions) => actions.setNodesDraggable);
|
|
||||||
const setNodesConnectable = useStoreActions((actions) => actions.setNodesConnectable);
|
|
||||||
const setElementsSelectable = useStoreActions((actions) => actions.setElementsSelectable);
|
|
||||||
const setInitTransform = useStoreActions((actions) => actions.setInitTransform);
|
|
||||||
const setMinMaxZoom = useStoreActions((actions) => actions.setMinMaxZoom);
|
|
||||||
const fitView = useStoreActions((actions) => actions.fitView);
|
|
||||||
const zoom = useStoreActions((actions) => actions.zoom);
|
|
||||||
|
|
||||||
const selectionKeyPressed = useKeyPress(selectionKeyCode);
|
useEffect(() => {
|
||||||
|
if (d3Initialised && onLoad) {
|
||||||
|
onLoad({
|
||||||
|
fitView: (params = { padding: 0.1 }) => fitView(params),
|
||||||
|
zoomIn: () => zoom(0.2),
|
||||||
|
zoomOut: () => zoom(-0.2),
|
||||||
|
project,
|
||||||
|
getElements,
|
||||||
|
setTransform: (transform: FlowTransform) =>
|
||||||
|
setInitTransform({ x: transform.x, y: transform.y, k: transform.zoom }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const onZoomPaneClick = () => setNodesSelection({ isActive: false });
|
if (d3Initialised) {
|
||||||
|
const initialTransform = {
|
||||||
const updateDimensions = () => {
|
x: defaultPosition[0],
|
||||||
if (!rendererNode.current) {
|
y: defaultPosition[1],
|
||||||
return;
|
k: defaultZoom,
|
||||||
}
|
|
||||||
|
|
||||||
const size = getDimensions(rendererNode.current);
|
|
||||||
|
|
||||||
if (size.height === 0 || size.width === 0) {
|
|
||||||
throw new Error('The React Flow parent container needs a width and a height to render the graph.');
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSize(size);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
let resizeObserver: ResizeObserver;
|
|
||||||
|
|
||||||
updateDimensions();
|
|
||||||
window.onresize = updateDimensions;
|
|
||||||
|
|
||||||
if (onConnect) {
|
|
||||||
setOnConnect(onConnect);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rendererNode.current) {
|
|
||||||
resizeObserver = new ResizeObserver((entries) => {
|
|
||||||
for (let _ of entries) {
|
|
||||||
updateDimensions();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
resizeObserver.observe(rendererNode.current);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.onresize = null;
|
|
||||||
|
|
||||||
if (resizeObserver && rendererNode.current) {
|
|
||||||
resizeObserver.unobserve(rendererNode.current!);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}, []);
|
|
||||||
|
|
||||||
useD3Zoom({
|
if (initialTransform.x !== 0 || initialTransform.y !== 0 || initialTransform.k !== 1) {
|
||||||
zoomPane,
|
setInitTransform(initialTransform);
|
||||||
onMove,
|
|
||||||
onMoveStart,
|
|
||||||
onMoveEnd,
|
|
||||||
selectionKeyPressed,
|
|
||||||
zoomOnScroll,
|
|
||||||
zoomOnDoubleClick,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (d3Initialised && onLoad) {
|
|
||||||
onLoad({
|
|
||||||
fitView: (params = { padding: 0.1 }) => fitView(params),
|
|
||||||
zoomIn: () => zoom(0.2),
|
|
||||||
zoomOut: () => zoom(-0.2),
|
|
||||||
project,
|
|
||||||
getElements,
|
|
||||||
setTransform: (transform: FlowTransform) =>
|
|
||||||
setInitTransform({ x: transform.x, y: transform.y, k: transform.zoom }),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}, [d3Initialised, onLoad]);
|
||||||
|
|
||||||
if (d3Initialised) {
|
useEffect(() => {
|
||||||
const initialTransform = {
|
setSnapGrid({ snapToGrid, snapGrid });
|
||||||
x: defaultPosition[0],
|
}, [snapToGrid]);
|
||||||
y: defaultPosition[1],
|
|
||||||
k: defaultZoom,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (initialTransform.x !== 0 || initialTransform.y !== 0 || initialTransform.k !== 1) {
|
useEffect(() => {
|
||||||
setInitTransform(initialTransform);
|
setNodesDraggable(nodesDraggable);
|
||||||
}
|
}, [nodesDraggable]);
|
||||||
}
|
|
||||||
}, [d3Initialised, onLoad]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSnapGrid({ snapToGrid, snapGrid });
|
setNodesConnectable(nodesConnectable);
|
||||||
}, [snapToGrid]);
|
}, [nodesConnectable]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setNodesDraggable(nodesDraggable);
|
setElementsSelectable(elementsSelectable);
|
||||||
}, [nodesDraggable]);
|
}, [elementsSelectable]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setNodesConnectable(nodesConnectable);
|
setMinMaxZoom({ minZoom, maxZoom });
|
||||||
}, [nodesConnectable]);
|
}, [minZoom, maxZoom]);
|
||||||
|
|
||||||
useEffect(() => {
|
useGlobalKeyHandler({ onElementsRemove, deleteKeyCode });
|
||||||
setElementsSelectable(elementsSelectable);
|
useElementUpdater(elements);
|
||||||
}, [elementsSelectable]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
return (
|
||||||
setMinMaxZoom({ minZoom, maxZoom });
|
<div className="react-flow__renderer" ref={rendererNode}>
|
||||||
}, [minZoom, maxZoom]);
|
<NodeRenderer
|
||||||
|
nodeTypes={nodeTypes}
|
||||||
useGlobalKeyHandler({ onElementsRemove, deleteKeyCode });
|
onElementClick={onElementClick}
|
||||||
useElementUpdater(elements);
|
onNodeMouseEnter={onNodeMouseEnter}
|
||||||
|
onNodeMouseMove={onNodeMouseMove}
|
||||||
return (
|
onNodeMouseLeave={onNodeMouseLeave}
|
||||||
<div className="react-flow__renderer" ref={rendererNode}>
|
onNodeContextMenu={onNodeContextMenu}
|
||||||
<NodeRenderer
|
onNodeDragStop={onNodeDragStop}
|
||||||
nodeTypes={nodeTypes}
|
onNodeDragStart={onNodeDragStart}
|
||||||
onElementClick={onElementClick}
|
onlyRenderVisibleNodes={onlyRenderVisibleNodes}
|
||||||
onNodeMouseEnter={onNodeMouseEnter}
|
selectNodesOnDrag={selectNodesOnDrag}
|
||||||
onNodeMouseMove={onNodeMouseMove}
|
/>
|
||||||
onNodeMouseLeave={onNodeMouseLeave}
|
<EdgeRenderer
|
||||||
onNodeContextMenu={onNodeContextMenu}
|
width={width}
|
||||||
onNodeDragStop={onNodeDragStop}
|
height={height}
|
||||||
onNodeDragStart={onNodeDragStart}
|
edgeTypes={edgeTypes}
|
||||||
onlyRenderVisibleNodes={onlyRenderVisibleNodes}
|
onElementClick={onElementClick}
|
||||||
selectNodesOnDrag={selectNodesOnDrag}
|
connectionLineType={connectionLineType}
|
||||||
/>
|
connectionLineStyle={connectionLineStyle}
|
||||||
<EdgeRenderer
|
arrowHeadColor={arrowHeadColor}
|
||||||
width={width}
|
markerEndId={markerEndId}
|
||||||
height={height}
|
/>
|
||||||
edgeTypes={edgeTypes}
|
<UserSelection selectionKeyPressed={selectionKeyPressed} />
|
||||||
onElementClick={onElementClick}
|
{nodesSelectionActive && <NodesSelection />}
|
||||||
connectionLineType={connectionLineType}
|
<div className="react-flow__zoompane" onClick={onZoomPaneClick} ref={zoomPane} />
|
||||||
connectionLineStyle={connectionLineStyle}
|
</div>
|
||||||
arrowHeadColor={arrowHeadColor}
|
);
|
||||||
markerEndId={markerEndId}
|
};
|
||||||
/>
|
|
||||||
<UserSelection selectionKeyPressed={selectionKeyPressed} />
|
|
||||||
{nodesSelectionActive && <NodesSelection />}
|
|
||||||
<div className="react-flow__zoompane" onClick={onZoomPaneClick} ref={zoomPane} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
GraphView.displayName = 'GraphView';
|
GraphView.displayName = 'GraphView';
|
||||||
|
|
||||||
export default GraphView;
|
export default memo(GraphView);
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ function renderNode(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const NodeRenderer = memo(({ onlyRenderVisibleNodes = true, ...props }: NodeRendererProps) => {
|
const NodeRenderer = ({ onlyRenderVisibleNodes = true, ...props }: NodeRendererProps) => {
|
||||||
const nodes = useStoreState((s) => s.nodes);
|
const nodes = useStoreState((s) => s.nodes);
|
||||||
const transform = useStoreState((s) => s.transform);
|
const transform = useStoreState((s) => s.transform);
|
||||||
const selectedElements = useStoreState((s) => s.selectedElements);
|
const selectedElements = useStoreState((s) => s.selectedElements);
|
||||||
@@ -94,8 +94,8 @@ const NodeRenderer = memo(({ onlyRenderVisibleNodes = true, ...props }: NodeRend
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
};
|
||||||
|
|
||||||
NodeRenderer.displayName = 'NodeRenderer';
|
NodeRenderer.displayName = 'NodeRenderer';
|
||||||
|
|
||||||
export default NodeRenderer;
|
export default memo(NodeRenderer);
|
||||||
|
|||||||
Reference in New Issue
Block a user