diff --git a/.changeset/green-files-act.md b/.changeset/green-files-act.md new file mode 100644 index 00000000..0ff03536 --- /dev/null +++ b/.changeset/green-files-act.md @@ -0,0 +1,5 @@ +--- +'@reactflow/minimap': minor +--- + +feat: add nodeComponent prop for passing custom component diff --git a/examples/vite-app/src/App/index.tsx b/examples/vite-app/src/App/index.tsx index 02eaac1e..8ecdd8b8 100644 --- a/examples/vite-app/src/App/index.tsx +++ b/examples/vite-app/src/App/index.tsx @@ -5,6 +5,7 @@ import Basic from '../examples/Basic'; import Backgrounds from '../examples/Backgrounds'; import ControlledUncontrolled from '../examples/ControlledUncontrolled'; import CustomConnectionLine from '../examples/CustomConnectionLine'; +import CustomMiniMapNode from '../examples/CustomMiniMapNode'; import CustomNode from '../examples/CustomNode'; import DefaultNodes from '../examples/DefaultNodes'; import DragHandle from '../examples/DragHandle'; @@ -77,6 +78,11 @@ const routes: IRoute[] = [ path: '/custom-connectionline', component: CustomConnectionLine, }, + { + name: 'Custom Minimap Node', + path: '/custom-minimap-node', + component: CustomMiniMapNode, + }, { name: 'Custom Node', path: '/custom-node', diff --git a/examples/vite-app/src/examples/CustomMiniMapNode/index.tsx b/examples/vite-app/src/examples/CustomMiniMapNode/index.tsx new file mode 100644 index 00000000..6a8e5807 --- /dev/null +++ b/examples/vite-app/src/examples/CustomMiniMapNode/index.tsx @@ -0,0 +1,74 @@ +import { MouseEvent, CSSProperties, useCallback } from 'react'; + +import ReactFlow, { + addEdge, + Background, + BackgroundVariant, + Connection, + Controls, + Edge, + MiniMap, + MiniMapNodeProps, + Node, + ReactFlowInstance, + useEdgesState, + useNodesState, +} from 'reactflow'; + +const onInit = (reactFlowInstance: ReactFlowInstance) => console.log('flow loaded:', reactFlowInstance); +const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node); +const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node); + +const buttonStyle: CSSProperties = { + position: 'absolute', + left: 10, + top: 10, + zIndex: 4, +}; + +const CustomMiniMapNode = ({ x, y, width, height, color }: MiniMapNodeProps) => ( + +); + +const CustomMiniMapNodeFlow = () => { + const [nodes, setNodes, onNodesChange] = useNodesState([]); + const [edges, setEdges, onEdgesChange] = useEdgesState([]); + + const onConnect = useCallback((params: Connection | Edge) => setEdges((els) => addEdge(params, els)), [setEdges]); + const addRandomNode = () => { + const nodeId = (nodes.length + 1).toString(); + const newNode: Node = { + id: nodeId, + data: { label: `Node: ${nodeId}` }, + position: { + x: Math.random() * window.innerWidth, + y: Math.random() * window.innerHeight, + }, + }; + setNodes((nds) => nds.concat(newNode)); + }; + + return ( + onConnect(p)} + onNodeDragStop={onNodeDragStop} + onlyRenderVisibleElements={false} + > + + + + + + + ); +}; + +export default CustomMiniMapNodeFlow; diff --git a/packages/minimap/src/MiniMap.tsx b/packages/minimap/src/MiniMap.tsx index 6fb1745b..59b3f34d 100644 --- a/packages/minimap/src/MiniMap.tsx +++ b/packages/minimap/src/MiniMap.tsx @@ -55,6 +55,9 @@ function MiniMap({ nodeClassName = '', nodeBorderRadius = 5, nodeStrokeWidth = 2, + // We need to rename the prop to be `CapitalCase` so that JSX will render it as + // a component properly. + nodeComponent: NodeComponent = MiniMapNode, maskColor = 'rgb(240, 240, 240, 0.6)', maskStrokeColor = 'none', maskStrokeWidth = 1, @@ -181,7 +184,7 @@ function MiniMap({ const { x, y } = getNodePositionWithOrigin(node, nodeOrigin).positionAbsolute; return ( - void; -} - const MiniMapNode = ({ id, x, diff --git a/packages/minimap/src/types.ts b/packages/minimap/src/types.ts index 7d006329..394e466e 100644 --- a/packages/minimap/src/types.ts +++ b/packages/minimap/src/types.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import type { HTMLAttributes, MouseEvent } from 'react'; +import type { ComponentType, CSSProperties, HTMLAttributes, MouseEvent } from 'react'; import type { Node, PanelPosition, XYPosition } from '@reactflow/core'; export type GetMiniMapNodeAttribute = (node: Node) => string; @@ -10,6 +10,7 @@ export type MiniMapProps = Omit, ' nodeClassName?: string | GetMiniMapNodeAttribute; nodeBorderRadius?: number; nodeStrokeWidth?: number; + nodeComponent?: ComponentType; maskColor?: string; maskStrokeColor?: string; maskStrokeWidth?: number; @@ -20,3 +21,19 @@ export type MiniMapProps = Omit, ' zoomable?: boolean; ariaLabel?: string | null; }; + +export interface MiniMapNodeProps { + id: string; + x: number; + y: number; + width: number; + height: number; + borderRadius: number; + className: string; + color: string; + shapeRendering: string; + strokeColor: string; + strokeWidth: number; + style?: CSSProperties; + onClick?: (event: MouseEvent, id: string) => void; +}