From 46f3f81dc43b41a164d3f1ca06db5aa912cd50b7 Mon Sep 17 00:00:00 2001 From: Hayleigh Thompson Date: Thu, 9 Mar 2023 14:01:55 +0000 Subject: [PATCH 1/6] :truck: Move 'MiniMapNodeProps' into shared types module. --- packages/minimap/src/MiniMapNode.tsx | 18 +----------------- packages/minimap/src/types.ts | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/minimap/src/MiniMapNode.tsx b/packages/minimap/src/MiniMapNode.tsx index ae000d02..54072f02 100644 --- a/packages/minimap/src/MiniMapNode.tsx +++ b/packages/minimap/src/MiniMapNode.tsx @@ -1,23 +1,7 @@ import { memo } from 'react'; -import type { CSSProperties, MouseEvent } from 'react'; +import type { MiniMapNodeProps } from './types'; import cc from 'classcat'; -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; -} - const MiniMapNode = ({ id, x, diff --git a/packages/minimap/src/types.ts b/packages/minimap/src/types.ts index 7d006329..ca28981a 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 { CSSProperties, HTMLAttributes, MouseEvent } from 'react'; import type { Node, PanelPosition, XYPosition } from '@reactflow/core'; export type GetMiniMapNodeAttribute = (node: Node) => string; @@ -20,3 +20,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; +} From 1415ed003e2fd4ceb07b88bd3e0303960c03790a Mon Sep 17 00:00:00 2001 From: Hayleigh Thompson Date: Thu, 9 Mar 2023 14:25:23 +0000 Subject: [PATCH 2/6] :sparkles: Add an optional 'MiniMapNode' field to 'MiniMapProps'. This lets users supply a custom SVG node to render in the minimap instead of the default rectangle. --- packages/minimap/src/MiniMap.tsx | 5 ++++- packages/minimap/src/types.ts | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/minimap/src/MiniMap.tsx b/packages/minimap/src/MiniMap.tsx index bb4a2f89..378ee4f6 100644 --- a/packages/minimap/src/MiniMap.tsx +++ b/packages/minimap/src/MiniMap.tsx @@ -64,6 +64,9 @@ function MiniMap({ pannable = false, zoomable = false, ariaLabel = 'React Flow mini map', + // Rename the field to avoid clashes with the default `MiniMapNode` component. + // Fallback to that component if none is provided. + MiniMapNode: CustomMiniMapNode = MiniMapNode, }: MiniMapProps) { const store = useStoreApi(); const svg = useRef(null); @@ -176,7 +179,7 @@ function MiniMap({ const { x, y } = getNodePositionWithOrigin(node, nodeOrigin).positionAbsolute; return ( - = (node: Node) => string; @@ -19,6 +19,7 @@ export type MiniMapProps = Omit, ' pannable?: boolean; zoomable?: boolean; ariaLabel?: string | null; + MiniMapNode?: ComponentType; }; export interface MiniMapNodeProps { From 2acc5b7706de1f52d87323989d27c9e04bd21577 Mon Sep 17 00:00:00 2001 From: Hayleigh Thompson Date: Thu, 9 Mar 2023 14:46:46 +0000 Subject: [PATCH 3/6] :recycle: Rename 'MiniMapNode' prop to 'nodeComponent' for greater consistency. --- packages/minimap/src/MiniMap.tsx | 8 ++++---- packages/minimap/src/types.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/minimap/src/MiniMap.tsx b/packages/minimap/src/MiniMap.tsx index 378ee4f6..b4bc9ee2 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, @@ -64,9 +67,6 @@ function MiniMap({ pannable = false, zoomable = false, ariaLabel = 'React Flow mini map', - // Rename the field to avoid clashes with the default `MiniMapNode` component. - // Fallback to that component if none is provided. - MiniMapNode: CustomMiniMapNode = MiniMapNode, }: MiniMapProps) { const store = useStoreApi(); const svg = useRef(null); @@ -179,7 +179,7 @@ function MiniMap({ const { x, y } = getNodePositionWithOrigin(node, nodeOrigin).positionAbsolute; return ( - = Omit, ' nodeClassName?: string | GetMiniMapNodeAttribute; nodeBorderRadius?: number; nodeStrokeWidth?: number; + nodeComponent?: ComponentType; maskColor?: string; maskStrokeColor?: string; maskStrokeWidth?: number; @@ -19,7 +20,6 @@ export type MiniMapProps = Omit, ' pannable?: boolean; zoomable?: boolean; ariaLabel?: string | null; - MiniMapNode?: ComponentType; }; export interface MiniMapNodeProps { From 8b9f2a8f011f22979c28ecbcf1f55b4a417fa47f Mon Sep 17 00:00:00 2001 From: Hayleigh Thompson Date: Thu, 9 Mar 2023 14:48:03 +0000 Subject: [PATCH 4/6] :sparkles: Add an example flow to demo custom minimap nodes. --- examples/vite-app/src/App/index.tsx | 6 ++ .../src/examples/CustomMiniMapNode/index.tsx | 73 +++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 examples/vite-app/src/examples/CustomMiniMapNode/index.tsx 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..fcb5b5b0 --- /dev/null +++ b/examples/vite-app/src/examples/CustomMiniMapNode/index.tsx @@ -0,0 +1,73 @@ +import { MouseEvent, CSSProperties, useCallback } from 'react'; + +import ReactFlow, { + addEdge, + Background, + BackgroundVariant, + Connection, + Controls, + Edge, + MiniMap, + 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 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} + > + + + + + + + ); +}; + +const CustomMiniMapNode = ({ x, y, width, height }) => ( + +) + +export default CustomMiniMapNodeFlow; From 84ffe34f87156eb1ae6aa2d2ba7f0eef417e57b6 Mon Sep 17 00:00:00 2001 From: moklick Date: Mon, 13 Mar 2023 13:08:29 +0100 Subject: [PATCH 5/6] chore(custom-minimap-node-example): cleanup --- .../vite-app/src/examples/CustomMiniMapNode/index.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/vite-app/src/examples/CustomMiniMapNode/index.tsx b/examples/vite-app/src/examples/CustomMiniMapNode/index.tsx index fcb5b5b0..6a8e5807 100644 --- a/examples/vite-app/src/examples/CustomMiniMapNode/index.tsx +++ b/examples/vite-app/src/examples/CustomMiniMapNode/index.tsx @@ -8,6 +8,7 @@ import ReactFlow, { Controls, Edge, MiniMap, + MiniMapNodeProps, Node, ReactFlowInstance, useEdgesState, @@ -25,6 +26,10 @@ const buttonStyle: CSSProperties = { zIndex: 4, }; +const CustomMiniMapNode = ({ x, y, width, height, color }: MiniMapNodeProps) => ( + +); + const CustomMiniMapNodeFlow = () => { const [nodes, setNodes, onNodesChange] = useNodesState([]); const [edges, setEdges, onEdgesChange] = useEdgesState([]); @@ -66,8 +71,4 @@ const CustomMiniMapNodeFlow = () => { ); }; -const CustomMiniMapNode = ({ x, y, width, height }) => ( - -) - export default CustomMiniMapNodeFlow; From 4a30185a12899691ff61259f0db84bc5494cb573 Mon Sep 17 00:00:00 2001 From: moklick Date: Mon, 13 Mar 2023 13:09:23 +0100 Subject: [PATCH 6/6] chore(changeset): add --- .changeset/green-files-act.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/green-files-act.md 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