diff --git a/examples/vite-app/package.json b/examples/vite-app/package.json index 23bbc7fc..677f7289 100644 --- a/examples/vite-app/package.json +++ b/examples/vite-app/package.json @@ -1,7 +1,7 @@ { "name": "@reactflow/examples", "private": true, - "version": "0.0.2", + "version": "0.0.0", "type": "module", "scripts": { "dev": "vite --port 3000 --open", diff --git a/examples/vite-app/src/App/index.tsx b/examples/vite-app/src/App/index.tsx index 90339935..f0e32fb8 100644 --- a/examples/vite-app/src/App/index.tsx +++ b/examples/vite-app/src/App/index.tsx @@ -10,11 +10,13 @@ import DefaultNodes from '../examples/DefaultNodes'; import DragHandle from '../examples/DragHandle'; import DragNDrop from '../examples/DragNDrop'; import Edges from '../examples/Edges'; +import EdgeRenderer from '../examples/EdgeRenderer'; import EdgeTypes from '../examples/EdgeTypes'; import Empty from '../examples/Empty'; import FloatingEdges from '../examples/FloatingEdges'; import Hidden from '../examples/Hidden'; import Interaction from '../examples/Interaction'; +import Intersection from '../examples/Intersection'; import Layouting from '../examples/Layouting'; import MultiFlows from '../examples/MultiFlows'; import NestedNodes from '../examples/NestedNodes'; @@ -36,6 +38,7 @@ import Validation from '../examples/Validation'; import UseKeyPress from '../examples/UseKeyPress'; import EdgeRouting from '../examples/EdgeRouting'; import CancelConnection from '../examples/CancelConnection'; +import InteractiveMinimap from '../examples/InteractiveMinimap'; import UseOnSelectionChange from '../examples/UseOnSelectionChange'; interface IRoute { @@ -95,6 +98,11 @@ const routes: IRoute[] = [ path: '/edges', component: Edges, }, + { + name: 'Edge Renderer', + path: '/edge-renderer', + component: EdgeRenderer, + }, { name: 'Edge Types', path: '/edge-types', @@ -125,6 +133,16 @@ const routes: IRoute[] = [ path: '/interaction', component: Interaction, }, + { + name: 'Intersection', + path: '/intersection', + component: Intersection, + }, + { + name: 'Interactive Minimap', + path: '/interactive-minimap', + component: InteractiveMinimap, + }, { name: 'Layouting', path: '/layouting', diff --git a/examples/vite-app/src/examples/Basic/index.tsx b/examples/vite-app/src/examples/Basic/index.tsx index b59e21b3..d6f3950f 100644 --- a/examples/vite-app/src/examples/Basic/index.tsx +++ b/examples/vite-app/src/examples/Basic/index.tsx @@ -106,7 +106,7 @@ const BasicFlow = () => { toggle classnames - toObject + toObject ); diff --git a/examples/vite-app/src/examples/EdgeRenderer/CustomEdge.tsx b/examples/vite-app/src/examples/EdgeRenderer/CustomEdge.tsx new file mode 100644 index 00000000..f7a42270 --- /dev/null +++ b/examples/vite-app/src/examples/EdgeRenderer/CustomEdge.tsx @@ -0,0 +1,61 @@ +import { FC, MouseEvent } from 'react'; +import { EdgeProps, getBezierPath, EdgeLabelRenderer, useStore, ReactFlowStore } from 'reactflow'; + +const CustomEdge: FC = ({ + id, + source, + target, + sourceX, + sourceY, + targetX, + targetY, + sourcePosition, + targetPosition, + data, +}) => { + const isConnectedNodeDragging = useStore((s) => + Array.from(s.nodeInternals.values()).find((n) => n.dragging && (target === n.id || source === n.id)) + ); + + const [edgePath, labelX, labelY] = getBezierPath({ + sourceX, + sourceY, + sourcePosition, + targetX, + targetY, + targetPosition, + }); + + const onClick = (event: MouseEvent) => { + event.preventDefault(); + event.stopPropagation(); + + console.log('click', data.text); + }; + + return ( + <> + + + + + {data.text} + + send + + + > + ); +}; + +export default CustomEdge; diff --git a/examples/vite-app/src/examples/EdgeRenderer/CustomEdge2.tsx b/examples/vite-app/src/examples/EdgeRenderer/CustomEdge2.tsx new file mode 100644 index 00000000..e0e8e794 --- /dev/null +++ b/examples/vite-app/src/examples/EdgeRenderer/CustomEdge2.tsx @@ -0,0 +1,51 @@ +import { FC } from 'react'; +import { EdgeProps, getBezierPath, EdgeLabelRenderer, useStore } from 'reactflow'; + +const CustomEdge: FC = ({ + id, + source, + target, + sourceX, + sourceY, + targetX, + targetY, + sourcePosition, + targetPosition, + data, +}) => { + const isConnectedNodeDragging = useStore((s) => + Array.from(s.nodeInternals.values()).find((n) => n.dragging && (target === n.id || source === n.id)) + ); + + const [edgePath, labelX, labelY] = getBezierPath({ + sourceX, + sourceY, + sourcePosition, + targetX, + targetY, + targetPosition, + }); + + return ( + <> + + + + + {data.text} + + + > + ); +}; + +export default CustomEdge; diff --git a/examples/vite-app/src/examples/EdgeRenderer/index.tsx b/examples/vite-app/src/examples/EdgeRenderer/index.tsx new file mode 100644 index 00000000..f4315360 --- /dev/null +++ b/examples/vite-app/src/examples/EdgeRenderer/index.tsx @@ -0,0 +1,206 @@ +import React, { MouseEvent, useCallback } from 'react'; +import ReactFlow, { + Controls, + Background, + MiniMap, + addEdge, + Connection, + Edge, + EdgeTypes, + MarkerType, + Node, + useEdgesState, + useNodesState, +} from 'reactflow'; + +import CustomEdge from './CustomEdge'; +import CustomEdge2 from './CustomEdge2'; + +const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node); +const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node); +const onEdgeClick = (_: MouseEvent, edge: Edge) => console.log('click', edge); +const onEdgeDoubleClick = (_: MouseEvent, edge: Edge) => console.log('dblclick', edge); +const onEdgeMouseEnter = (_: MouseEvent, edge: Edge) => console.log('enter', edge); +const onEdgeMouseMove = (_: MouseEvent, edge: Edge) => console.log('move', edge); +const onEdgeMouseLeave = (_: MouseEvent, edge: Edge) => console.log('leave', edge); + +const initialNodes: Node[] = [ + { + id: '1', + type: 'input', + data: { label: 'Input 1' }, + position: { x: 250, y: 0 }, + }, + { id: '2', data: { label: 'Node 2' }, position: { x: 150, y: 100 } }, + { id: '2a', data: { label: 'Node 2a' }, position: { x: 0, y: 180 } }, + { id: '2b', data: { label: 'Node 2b' }, position: { x: -40, y: 300 } }, + { id: '3', data: { label: 'Node 3' }, position: { x: 250, y: 200 } }, + { id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 300 } }, + { id: '3a', data: { label: 'Node 3a' }, position: { x: 150, y: 300 } }, + { id: '5', data: { label: 'Node 5' }, position: { x: 250, y: 400 } }, + { + id: '6', + type: 'output', + data: { label: 'Output 6' }, + position: { x: 50, y: 550 }, + }, + { + id: '7', + type: 'output', + data: { label: 'Output 7' }, + position: { x: 250, y: 550 }, + }, + { + id: '8', + type: 'output', + data: { label: 'Output 8' }, + position: { x: 525, y: 600 }, + }, + { + id: '9', + type: 'output', + data: { label: 'Output 9' }, + position: { x: 675, y: 500 }, + }, +]; + +const initialEdges: Edge[] = [ + { + id: 'e1-2', + source: '1', + target: '2', + label: 'bezier edge (default)', + className: 'normal-edge', + }, + { + id: 'e2-2a', + source: '2', + target: '2a', + type: 'smoothstep', + label: 'smoothstep edge', + }, + { + id: 'e2a-2b', + source: '2a', + target: '2b', + type: 'simplebezier', + label: 'simple bezier edge', + }, + { id: 'e2-3', source: '2', target: '3', type: 'step', label: 'step edge' }, + { + id: 'e3-4', + source: '3', + target: '4', + type: 'straight', + label: 'straight edge', + }, + { + id: 'e3-3a', + source: '3', + target: '3a', + type: 'straight', + label: 'label only edge', + style: { stroke: 'none' }, + }, + { + id: 'e3-5', + source: '4', + target: '5', + animated: true, + label: 'animated styled edge', + style: { stroke: 'red' }, + }, + { + id: 'e5-7', + source: '5', + target: '7', + label: 'label with styled bg', + labelBgPadding: [8, 4], + labelBgBorderRadius: 4, + labelBgStyle: { fill: '#FFCC00', color: '#fff', fillOpacity: 0.7 }, + markerEnd: { + type: MarkerType.ArrowClosed, + }, + }, + { + id: 'e5-8', + source: '5', + target: '8', + type: 'custom', + data: { text: 'custom edge' }, + }, + { + id: 'e5-9', + source: '5', + target: '9', + type: 'custom2', + data: { text: 'custom edge 2' }, + }, + { + id: 'e5-6', + source: '5', + target: '6', + label: ( + <> + i am using + + {''} + + > + ), + labelStyle: { fill: 'red', fontWeight: 700 }, + style: { stroke: '#ffcc00' }, + markerEnd: { + type: MarkerType.Arrow, + color: '#FFCC00', + markerUnits: 'userSpaceOnUse', + width: 20, + height: 20, + strokeWidth: 2, + }, + markerStart: { + type: MarkerType.ArrowClosed, + color: '#FFCC00', + orient: 'auto-start-reverse', + markerUnits: 'userSpaceOnUse', + width: 20, + height: 20, + }, + }, +]; + +const edgeTypes: EdgeTypes = { + custom: CustomEdge, + custom2: CustomEdge2, +}; + +const EdgesFlow = () => { + const [nodes, , onNodesChange] = useNodesState(initialNodes); + const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); + const onConnect = useCallback((params: Connection | Edge) => setEdges((eds) => addEdge(params, eds)), [setEdges]); + + return ( + + + + + + ); +}; + +export default EdgesFlow; diff --git a/examples/vite-app/src/examples/InteractiveMinimap/index.tsx b/examples/vite-app/src/examples/InteractiveMinimap/index.tsx new file mode 100644 index 00000000..18fa854a --- /dev/null +++ b/examples/vite-app/src/examples/InteractiveMinimap/index.tsx @@ -0,0 +1,165 @@ +import { MouseEvent, useCallback } from 'react'; +import ReactFlow, { + MiniMap, + Background, + BackgroundVariant, + Controls, + ReactFlowProvider, + Node, + Edge, + useReactFlow, + XYPosition, +} from 'reactflow'; + +const onNodeDrag = (_: MouseEvent, node: Node) => console.log('drag', node); +const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node); +const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node); + +const initialNodes: Node[] = [ + { + id: '1', + data: { label: 'Node 1' }, + position: { x: 0, y: 0 }, + }, + { + id: '2', + data: { label: 'Node 2' }, + position: { x: 0, y: 200 }, + }, + { + id: '3', + data: { label: 'Node 3' }, + position: { x: 200, y: 0 }, + }, + + { + id: '4', + data: { label: 'Node 4' }, + position: { x: 1000, y: 0 }, + }, + { + id: '5', + data: { label: 'Node 5' }, + position: { x: 1000, y: 200 }, + }, + { + id: '6', + data: { label: 'Node 6' }, + position: { x: 800, y: 0 }, + }, + + { + id: '7', + data: { label: 'Node 4' }, + position: { x: 0, y: 1000 }, + }, + { + id: '8', + data: { label: 'Node 5' }, + position: { x: 0, y: 800 }, + }, + { + id: '9', + data: { label: 'Node 6' }, + position: { x: 200, y: 1000 }, + }, + + { + id: '10', + data: { label: 'Node 4' }, + position: { x: 1000, y: 1000 }, + }, + { + id: '11', + data: { label: 'Node 5' }, + position: { x: 800, y: 1000 }, + }, + { + id: '12', + data: { label: 'Node 6' }, + position: { x: 1000, y: 800 }, + }, +]; + +const initialEdges: Edge[] = []; + +const defaultEdgeOptions = { zIndex: 0 }; + +const BasicFlow = () => { + const instance = useReactFlow(); + + const updatePos = () => { + instance.setNodes((nodes) => + nodes.map((node) => { + node.position = { + x: Math.random() * 400, + y: Math.random() * 400, + }; + + return node; + }) + ); + }; + + const logToObject = () => console.log(instance.toObject()); + const resetTransform = () => instance.setViewport({ x: 0, y: 0, zoom: 1 }); + + const toggleClassnames = () => { + instance.setNodes((nodes) => + nodes.map((node) => { + node.className = node.className === 'light' ? 'dark' : 'light'; + + return node; + }) + ); + }; + + const onMiniMapClick = useCallback((event: MouseEvent, pos: XYPosition) => { + console.log(pos); + }, []); + + const onMiniMapNodeClick = useCallback((event: MouseEvent, node: Node) => { + console.log(node); + }, []); + + return ( + + + + + + + + reset transform + + + change pos + + + toggle classnames + + toObject + + + ); +}; + +export default function App() { + return ( + + + + ); +} diff --git a/examples/vite-app/src/examples/Intersection/index.tsx b/examples/vite-app/src/examples/Intersection/index.tsx new file mode 100644 index 00000000..1f82b308 --- /dev/null +++ b/examples/vite-app/src/examples/Intersection/index.tsx @@ -0,0 +1,104 @@ +import { useCallback, MouseEvent } from 'react'; +import ReactFlow, { + MiniMap, + Background, + Controls, + ReactFlowProvider, + Node, + Edge, + useReactFlow, + useNodesState, +} from 'reactflow'; + +import './style.css'; + +const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node); +const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node); + +const initialNodes: Node[] = [ + { + id: '1', + type: 'input', + data: { label: 'Node 1' }, + position: { x: 0, y: 0 }, + className: 'light', + style: { + width: 200, + height: 100, + }, + }, + { + id: '2', + data: { label: 'Node 2' }, + position: { x: 0, y: 150 }, + className: 'light', + }, + { + id: '3', + data: { label: 'Node 3' }, + position: { x: 250, y: 0 }, + className: 'light', + }, + { + id: '4', + data: { label: 'Node' }, + position: { x: 350, y: 150 }, + style: { + width: 50, + height: 50, + }, + className: 'light', + }, +]; + +const initialEdges: Edge[] = []; + +const defaultEdgeOptions = { zIndex: 0 }; + +const BasicFlow = () => { + const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); + const { getIntersectingNodes, isNodeIntersecting } = useReactFlow(); + + const onNodeDrag = useCallback((_: MouseEvent, node: Node) => { + const intersections = getIntersectingNodes(node).map((n) => n.id); + const isIntersecting = isNodeIntersecting(node, { x: 0, y: 0, width: 100, height: 100 }); + + console.log(isIntersecting); + + setNodes((ns) => + ns.map((n) => ({ + ...n, + className: intersections.includes(n.id) ? 'highlight' : '', + })) + ); + }, []); + + return ( + + + + + + ); +}; + +export default function App() { + return ( + + + + ); +} diff --git a/examples/vite-app/src/examples/Intersection/style.css b/examples/vite-app/src/examples/Intersection/style.css new file mode 100644 index 00000000..16b4cc4c --- /dev/null +++ b/examples/vite-app/src/examples/Intersection/style.css @@ -0,0 +1,4 @@ +.react-flow__node.highlight { + background-color: #ff5050; + color: white; +} diff --git a/examples/vite-app/src/examples/NestedNodes/index.tsx b/examples/vite-app/src/examples/NestedNodes/index.tsx index 5e9c80f1..a6c083ce 100644 --- a/examples/vite-app/src/examples/NestedNodes/index.tsx +++ b/examples/vite-app/src/examples/NestedNodes/index.tsx @@ -160,7 +160,7 @@ const NestedFlow = () => { maxZoom={4} onlyRenderVisibleElements={false} > - + diff --git a/examples/vite-app/src/examples/UseReactFlow/index.tsx b/examples/vite-app/src/examples/UseReactFlow/index.tsx index 7133cec9..6fca9627 100644 --- a/examples/vite-app/src/examples/UseReactFlow/index.tsx +++ b/examples/vite-app/src/examples/UseReactFlow/index.tsx @@ -43,6 +43,7 @@ const initialNodes: Node[] = [ const initialEdges: Edge[] = [ { id: 'e1-2', source: '1', target: '2', animated: true }, { id: 'e1-3', source: '1', target: '3' }, + // { id: 'e3-4', source: '3', target: '4' } ]; let id = 5; @@ -64,6 +65,7 @@ const UseZoomPanHelperFlow = () => { addEdges, getNodes, getEdges, + deleteElements } = useReactFlow(); const onPaneClick = useCallback( @@ -111,6 +113,16 @@ const UseZoomPanHelperFlow = () => { console.log('edges', getEdges()); }, [getNodes, getEdges]); + const deleteSelectedElements = useCallback(() => { + const selectedNodes = nodes.filter(node => node.selected); + const selectedEdges = edges.filter(edge => edge.selected); + deleteElements({ nodes: selectedNodes, edges: selectedEdges }); + }, [deleteElements, nodes, edges]) + + const deleteSomeElements = useCallback(() => { + deleteElements({ nodes: [{ id: '2' }], edges: [{ id: 'e1-3' }] }) + }, []) + useEffect(() => { addEdges({ id: 'e3-4', source: '3', target: '4' }); }, [addEdges]); @@ -137,6 +149,8 @@ const UseZoomPanHelperFlow = () => { add node reset nodes useNodes + deleteSelectedElements + deleteSomeElements diff --git a/packages/background/CHANGELOG.md b/packages/background/CHANGELOG.md index 55bb8b95..7d2cfbff 100644 --- a/packages/background/CHANGELOG.md +++ b/packages/background/CHANGELOG.md @@ -1,5 +1,12 @@ # @reactflow/background +## 11.0.4 + +### Patch Changes + +- Updated dependencies [[`740659c0`](https://github.com/wbkd/react-flow/commit/740659c0e788c7572d4a1e64e1d33d60712233fc), [`7902a3ce`](https://github.com/wbkd/react-flow/commit/7902a3ce3188426d5cd07cf0943a68f679e67948), [`b25d499e`](https://github.com/wbkd/react-flow/commit/b25d499ec05b5c6f21ac552d03650eb37433552e), [`4fc1253e`](https://github.com/wbkd/react-flow/commit/4fc1253eadf9b7dd392d8dc2348f44fa8d08f931), [`8ba4dd5d`](https://github.com/wbkd/react-flow/commit/8ba4dd5d1d4b2e6f107c148de62aec0b688d8b21)]: + - @reactflow/core@11.2.0 + ## 11.0.3 ### Patch Changes diff --git a/packages/background/package.json b/packages/background/package.json index 12031de8..b3575939 100644 --- a/packages/background/package.json +++ b/packages/background/package.json @@ -1,6 +1,6 @@ { "name": "@reactflow/background", - "version": "11.0.3", + "version": "11.0.4", "description": "Background component with different variants for React Flow", "keywords": [ "react", diff --git a/packages/controls/CHANGELOG.md b/packages/controls/CHANGELOG.md index 1c2d37b1..92d15c50 100644 --- a/packages/controls/CHANGELOG.md +++ b/packages/controls/CHANGELOG.md @@ -1,5 +1,12 @@ # @reactflow/controls +## 11.0.4 + +### Patch Changes + +- Updated dependencies [[`740659c0`](https://github.com/wbkd/react-flow/commit/740659c0e788c7572d4a1e64e1d33d60712233fc), [`7902a3ce`](https://github.com/wbkd/react-flow/commit/7902a3ce3188426d5cd07cf0943a68f679e67948), [`b25d499e`](https://github.com/wbkd/react-flow/commit/b25d499ec05b5c6f21ac552d03650eb37433552e), [`4fc1253e`](https://github.com/wbkd/react-flow/commit/4fc1253eadf9b7dd392d8dc2348f44fa8d08f931), [`8ba4dd5d`](https://github.com/wbkd/react-flow/commit/8ba4dd5d1d4b2e6f107c148de62aec0b688d8b21)]: + - @reactflow/core@11.2.0 + ## 11.0.3 ### Patch Changes diff --git a/packages/controls/package.json b/packages/controls/package.json index e998486e..882da3b0 100644 --- a/packages/controls/package.json +++ b/packages/controls/package.json @@ -1,6 +1,6 @@ { "name": "@reactflow/controls", - "version": "11.0.3", + "version": "11.0.4", "description": "Component to control the viewport of a React Flow instance", "keywords": [ "react", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 7fb7463f..3c8382a4 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,21 @@ # @reactflow/core +## 11.2.0 + +### Minor Changes + +- [#2535](https://github.com/wbkd/react-flow/pull/2535) [`7902a3ce`](https://github.com/wbkd/react-flow/commit/7902a3ce3188426d5cd07cf0943a68f679e67948) Thanks [@moklick](https://github.com/moklick)! - Feat: Add edge label renderer + +- [#2536](https://github.com/wbkd/react-flow/pull/2536) [`b25d499e`](https://github.com/wbkd/react-flow/commit/b25d499ec05b5c6f21ac552d03650eb37433552e) Thanks [@pengfu](https://github.com/pengfu)! - Feat: add deleteElements helper function + +- [#2539](https://github.com/wbkd/react-flow/pull/2539) [`4fc1253e`](https://github.com/wbkd/react-flow/commit/4fc1253eadf9b7dd392d8dc2348f44fa8d08f931) Thanks [@moklick](https://github.com/moklick)! - Feat: add intersection helpers + +- [#2530](https://github.com/wbkd/react-flow/pull/2530) [`8ba4dd5d`](https://github.com/wbkd/react-flow/commit/8ba4dd5d1d4b2e6f107c148de62aec0b688d8b21) Thanks [@moklick](https://github.com/moklick)! - Feat: Add pan and zoom to mini map + +### Patch Changes + +- [#2538](https://github.com/wbkd/react-flow/pull/2538) [`740659c0`](https://github.com/wbkd/react-flow/commit/740659c0e788c7572d4a1e64e1d33d60712233fc) Thanks [@neo](https://github.com/neo)! - Refactor: put React Flow in isolated stacking context + ## 11.1.2 ### Patch Changes diff --git a/packages/core/package.json b/packages/core/package.json index ea025613..aef400ec 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@reactflow/core", - "version": "11.1.2", + "version": "11.2.0", "description": "Core components and util functions of React Flow.", "keywords": [ "react", @@ -58,6 +58,7 @@ "@reactflow/tsconfig": "workspace:*", "@types/node": "^18.7.16", "@types/react": "^18.0.17", + "@types/react-dom": "^18.0.6", "react": "^18.2.0", "typescript": "^4.8.3" }, diff --git a/packages/core/src/components/EdgeLabelRenderer/index.tsx b/packages/core/src/components/EdgeLabelRenderer/index.tsx new file mode 100644 index 00000000..0647364a --- /dev/null +++ b/packages/core/src/components/EdgeLabelRenderer/index.tsx @@ -0,0 +1,15 @@ +import { useRef } from 'react'; +import type { ReactNode } from 'react'; +import { createPortal } from 'react-dom'; + +function EdgeLabelRenderer({ children }: { children: ReactNode }) { + const wrapperRef = useRef(document.getElementById('edgelabel-portal')); + + if (!wrapperRef.current) { + return null; + } + + return createPortal(children, wrapperRef.current); +} + +export default EdgeLabelRenderer; diff --git a/packages/core/src/container/GraphView/index.tsx b/packages/core/src/container/GraphView/index.tsx index 690b66f6..4190bed0 100644 --- a/packages/core/src/container/GraphView/index.tsx +++ b/packages/core/src/container/GraphView/index.tsx @@ -158,6 +158,8 @@ const GraphView = ({ disableKeyboardA11y={disableKeyboardA11y} rfId={rfId} /> + + ( diff --git a/packages/core/src/container/ZoomPane/index.tsx b/packages/core/src/container/ZoomPane/index.tsx index cde4ec47..a5783a7a 100644 --- a/packages/core/src/container/ZoomPane/index.tsx +++ b/packages/core/src/container/ZoomPane/index.tsx @@ -173,9 +173,12 @@ const ZoomPane = ({ useEffect(() => { if (d3Zoom) { d3Zoom.on('start', (event: D3ZoomEvent) => { + if (!event.sourceEvent) { + return null; + } + const { onViewportChangeStart } = store.getState(); isZoomingOrPanning.current = true; - if (event.sourceEvent?.type === 'mousedown') { store.setState({ paneDragging: true }); } @@ -194,6 +197,9 @@ const ZoomPane = ({ useEffect(() => { if (d3Zoom) { d3Zoom.on('end', (event: D3ZoomEvent) => { + if (!event.sourceEvent) { + return null; + } const { onViewportChangeEnd } = store.getState(); isZoomingOrPanning.current = false; diff --git a/packages/core/src/hooks/useGlobalKeyHandler.ts b/packages/core/src/hooks/useGlobalKeyHandler.ts index 23cb2a7d..f2b206e7 100644 --- a/packages/core/src/hooks/useGlobalKeyHandler.ts +++ b/packages/core/src/hooks/useGlobalKeyHandler.ts @@ -2,9 +2,8 @@ import { useEffect } from 'react'; import { useStoreApi } from '../hooks/useStore'; import useKeyPress from './useKeyPress'; -import { getConnectedEdges } from '../utils/graph'; -import type { KeyCode, NodeChange, Node } from '../types'; - +import type { KeyCode } from '../types'; +import useReactFlow from './useReactFlow'; interface HookParams { deleteKeyCode: KeyCode | null; multiSelectionKeyCode: KeyCode | null; @@ -12,87 +11,18 @@ interface HookParams { export default ({ deleteKeyCode, multiSelectionKeyCode }: HookParams): void => { const store = useStoreApi(); + const { deleteElements } = useReactFlow(); + const deleteKeyPressed = useKeyPress(deleteKeyCode); const multiSelectionKeyPressed = useKeyPress(multiSelectionKeyCode); useEffect(() => { - if (!deleteKeyPressed) { - return; - } - - const { - nodeInternals, - edges, - hasDefaultNodes, - hasDefaultEdges, - onNodesDelete, - onEdgesDelete, - onNodesChange, - onEdgesChange, - } = store.getState(); - const nodes = Array.from(nodeInternals.values()); - const nodesToRemove = nodes.reduce((res, node) => { - const parentSelected = !node.selected && node.parentNode && res.find((n) => n.id === node.parentNode); - const deletable = typeof node.deletable === 'boolean' ? node.deletable : true; - if (deletable && (node.selected || parentSelected)) { - res.push(node); - } - - return res; - }, []); - const deletableEdges = edges.filter((e) => (typeof e.deletable === 'boolean' ? e.deletable : true)); - const selectedEdges = deletableEdges.filter((e) => e.selected); - - if (nodesToRemove || selectedEdges) { - const connectedEdges = getConnectedEdges(nodesToRemove, deletableEdges); - const edgesToRemove = [...selectedEdges, ...connectedEdges]; - const edgeIdsToRemove = edgesToRemove.reduce((res, edge) => { - if (!res.includes(edge.id)) { - res.push(edge.id); - } - return res; - }, []); - - if (hasDefaultEdges || hasDefaultNodes) { - if (hasDefaultEdges) { - store.setState({ - edges: edges.filter((e) => !edgeIdsToRemove.includes(e.id)), - }); - } - - if (hasDefaultNodes) { - nodesToRemove.forEach((node) => { - nodeInternals.delete(node.id); - }); - - store.setState({ - nodeInternals: new Map(nodeInternals), - }); - } - } - - if (edgeIdsToRemove.length > 0) { - onEdgesDelete?.(edgesToRemove); - - if (onEdgesChange) { - onEdgesChange( - edgeIdsToRemove.map((id) => ({ - id, - type: 'remove', - })) - ); - } - } - - if (nodesToRemove.length > 0) { - onNodesDelete?.(nodesToRemove); - - if (onNodesChange) { - const nodeChanges: NodeChange[] = nodesToRemove.map((n) => ({ id: n.id, type: 'remove' })); - onNodesChange(nodeChanges); - } - } - + if (deleteKeyPressed) { + const { nodeInternals, edges } = store.getState(); + const nodes = Array.from(nodeInternals.values()); + const selectedNodes = nodes.filter((node) => node.selected); + const selectedEdges = edges.filter((edge) => edge.selected); + deleteElements({nodes: selectedNodes, edges: selectedEdges}); store.setState({ nodesSelectionActive: false }); } }, [deleteKeyPressed]); diff --git a/packages/core/src/hooks/useReactFlow.ts b/packages/core/src/hooks/useReactFlow.ts index 2cba0fae..0801a772 100644 --- a/packages/core/src/hooks/useReactFlow.ts +++ b/packages/core/src/hooks/useReactFlow.ts @@ -11,7 +11,12 @@ import type { EdgeResetChange, NodeRemoveChange, EdgeRemoveChange, + NodeChange, + Node, + Rect, } from '../types'; +import { getConnectedEdges } from '../utils/graph'; +import { getOverlappingArea, isRectObject, nodeToRect } from '../utils'; /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ export default function useReactFlow(): ReactFlowInstance { @@ -111,6 +116,140 @@ export default function useReactFlow(): ReactFlo }; }, []); + const deleteElements = useCallback(({ nodes: nodesDeleted, edges: edgesDeleted }) => { + const { + nodeInternals, + edges, + hasDefaultNodes, + hasDefaultEdges, + onNodesDelete, + onEdgesDelete, + onNodesChange, + onEdgesChange, + } = store.getState(); + const nodes = Array.from(nodeInternals.values()); + const nodeIds = (nodesDeleted || []).map((node) => node.id); + const edgeIds = (edgesDeleted || []).map((edge) => edge.id); + const nodesToRemove = nodes.reduce((res, node) => { + const parentHit = !nodeIds.includes(node.id) && node.parentNode && res.find((n) => n.id === node.parentNode); + const deletable = typeof node.deletable === 'boolean' ? node.deletable : true; + if (deletable && (nodeIds.includes(node.id) || parentHit)) { + res.push(node); + } + + return res; + }, []); + const deletableEdges = edges.filter((e) => (typeof e.deletable === 'boolean' ? e.deletable : true)); + const initialHitEdges = deletableEdges.filter((e) => edgeIds.includes(e.id)); + if (nodesToRemove || initialHitEdges) { + const connectedEdges = getConnectedEdges(nodesToRemove, deletableEdges); + const edgesToRemove = [...initialHitEdges, ...connectedEdges]; + const edgeIdsToRemove = edgesToRemove.reduce((res, edge) => { + if (!res.includes(edge.id)) { + res.push(edge.id); + } + return res; + }, []); + + if (hasDefaultEdges || hasDefaultNodes) { + if (hasDefaultEdges) { + store.setState({ + edges: edges.filter((e) => !edgeIdsToRemove.includes(e.id)), + }); + } + + if (hasDefaultNodes) { + nodesToRemove.forEach((node) => { + nodeInternals.delete(node.id); + }); + + store.setState({ + nodeInternals: new Map(nodeInternals), + }); + } + } + + if (edgeIdsToRemove.length > 0) { + onEdgesDelete?.(edgesToRemove); + + if (onEdgesChange) { + onEdgesChange( + edgeIdsToRemove.map((id) => ({ + id, + type: 'remove', + })) + ); + } + } + + if (nodesToRemove.length > 0) { + onNodesDelete?.(nodesToRemove); + + if (onNodesChange) { + const nodeChanges: NodeChange[] = nodesToRemove.map((n) => ({ id: n.id, type: 'remove' })); + onNodesChange(nodeChanges); + } + } + } + }, []); + + const getNodeRect = useCallback( + ( + nodeOrRect: (Partial> & { id: Node['id'] }) | Rect + ): [Rect | null, Node | null | undefined, boolean] => { + const isRect = isRectObject(nodeOrRect); + const node = isRect ? null : store.getState().nodeInternals.get(nodeOrRect.id); + + if (!isRect && !node) { + [null, null, isRect]; + } + + const nodeRect = isRect ? nodeOrRect : nodeToRect(node!); + + return [nodeRect, node, isRect]; + }, + [] + ); + + const getIntersectingNodes = useCallback>( + (nodeOrRect, partially = true, nodes) => { + const [nodeRect, node, isRect] = getNodeRect(nodeOrRect); + + if (!nodeRect) { + return []; + } + + return (nodes || Array.from(store.getState().nodeInternals.values())).filter((n) => { + if (!isRect && (n.id === node!.id || !n.positionAbsolute)) { + return false; + } + + const currNodeRect = nodeToRect(n); + const overlappingArea = getOverlappingArea(currNodeRect, nodeRect); + const partiallyVisible = partially && overlappingArea > 0; + + return partiallyVisible || overlappingArea >= nodeOrRect.width! * nodeOrRect.height!; + }); + }, + [] + ); + + const isNodeIntersecting = useCallback>( + (nodeOrRect, area, partially = true) => { + const [nodeRect] = getNodeRect(nodeOrRect); + + if (!nodeRect) { + return false; + } + + const overlappingArea = getOverlappingArea(nodeRect, area); + const partiallyVisible = partially && overlappingArea > 0; + + return partiallyVisible || overlappingArea >= nodeOrRect.width! * nodeOrRect.height!; + }, + [] + ); + return useMemo(() => { return { ...viewportHelper, @@ -123,6 +262,23 @@ export default function useReactFlow(): ReactFlo addNodes, addEdges, toObject, + deleteElements, + getIntersectingNodes, + isNodeIntersecting, }; - }, [viewportHelper, getNodes, getNode, getEdges, getEdge, setNodes, setEdges, addNodes, addEdges, toObject]); + }, [ + viewportHelper, + getNodes, + getNode, + getEdges, + getEdge, + setNodes, + setEdges, + addNodes, + addEdges, + toObject, + deleteElements, + getIntersectingNodes, + isNodeIntersecting, + ]); } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index d7a3215d..e2e077db 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -24,6 +24,7 @@ export { applyNodeChanges, applyEdgeChanges } from './utils/changes'; export { getMarkerEnd } from './components/Edges/utils'; export { default as ReactFlowProvider } from './components/ReactFlowProvider'; export { default as Panel } from './components/Panel'; +export { default as EdgeLabelRenderer } from './components/EdgeLabelRenderer'; export { default as useReactFlow } from './hooks/useReactFlow'; export { default as useUpdateNodeInternals } from './hooks/useUpdateNodeInternals'; diff --git a/packages/core/src/styles/init.css b/packages/core/src/styles/init.css index c24bc2d0..a500c17d 100644 --- a/packages/core/src/styles/init.css +++ b/packages/core/src/styles/init.css @@ -220,3 +220,10 @@ stroke-dashoffset: 10; } } + +.react-flow__edgelabel-renderer { + position: absolute; + width: 100%; + height: 100%; + pointer-events: none; +} diff --git a/packages/core/src/types/instance.ts b/packages/core/src/types/instance.ts index dab4299e..7ff85606 100644 --- a/packages/core/src/types/instance.ts +++ b/packages/core/src/types/instance.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-namespace */ -import { ViewportHelperFunctions, Viewport, Node, Edge } from '.'; +import { ViewportHelperFunctions, Viewport, Node, Edge, Rect } from '.'; export type ReactFlowJsonObject = { nodes: Node[]; @@ -8,6 +8,10 @@ export type ReactFlowJsonObject = { viewport: Viewport; }; +export type DeleteElementsOptions = { + nodes?: (Partial & { id: Node['id'] })[]; + edges?: (Partial & { id: Edge['id'] })[]; +}; export namespace Instance { export type GetNodes = () => Node[]; export type SetNodes = ( @@ -22,6 +26,17 @@ export namespace Instance { export type GetEdge = (id: string) => Edge | undefined; export type AddEdges = (payload: Edge[] | Edge) => void; export type ToObject = () => ReactFlowJsonObject; + export type DeleteElements = ({ nodes, edges }: DeleteElementsOptions) => void; + export type GetIntersectingNodes = ( + node: (Partial> & { id: Node['id'] }) | Rect, + partially?: boolean, + nodes?: Node[] + ) => Node[]; + export type IsNodeIntersecting = ( + node: (Partial> & { id: Node['id'] }) | Rect, + area: Rect, + partially?: boolean + ) => boolean; } export type ReactFlowInstance = { @@ -34,5 +49,8 @@ export type ReactFlowInstance = { addEdges: Instance.AddEdges; getEdge: Instance.GetEdge; toObject: Instance.ToObject; + deleteElements: Instance.DeleteElements; + getIntersectingNodes: Instance.GetIntersectingNodes; + isNodeIntersecting: Instance.IsNodeIntersecting; viewportInitialized: boolean; } & Omit; diff --git a/packages/core/src/utils/graph.ts b/packages/core/src/utils/graph.ts index 123fd13e..78569276 100644 --- a/packages/core/src/utils/graph.ts +++ b/packages/core/src/utils/graph.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import type { Selection as D3Selection } from 'd3'; -import { boxToRect, clamp, devWarn, getBoundsOfBoxes, rectToBox } from '../utils'; +import { boxToRect, clamp, devWarn, getBoundsOfBoxes, getOverlappingArea, rectToBox } from '../utils'; import type { Node, Edge, Connection, EdgeMarkerType, Transform, XYPosition, Rect, NodeInternals } from '../types'; export const isEdge = (element: Node | Connection | Edge): element is Edge => @@ -161,12 +161,12 @@ export const getNodesInside = ( // set excludeNonSelectableNodes if you want to pay attention to the nodes "selectable" attribute excludeNonSelectableNodes = false ): Node[] => { - const rBox = rectToBox({ + const paneRect = { x: (rect.x - tx) / tScale, y: (rect.y - ty) / tScale, width: rect.width / tScale, height: rect.height / tScale, - }); + }; const visibleNodes: Node[] = []; @@ -177,10 +177,8 @@ export const getNodesInside = ( return false; } - const nBox = rectToBox({ ...positionAbsolute, width: width || 0, height: height || 0 }); - const xOverlap = Math.max(0, Math.min(rBox.x2, nBox.x2) - Math.max(rBox.x, nBox.x)); - const yOverlap = Math.max(0, Math.min(rBox.y2, nBox.y2) - Math.max(rBox.y, nBox.y)); - const overlappingArea = Math.ceil(xOverlap * yOverlap); + const nodeRect = { ...positionAbsolute, width: width || 0, height: height || 0 }; + const overlappingArea = getOverlappingArea(paneRect, nodeRect); const notInitialized = typeof width === 'undefined' || typeof height === 'undefined' || width === null || height === null; diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index c3afe773..02cff5a0 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -1,4 +1,4 @@ -import type { Dimensions, XYPosition, CoordinateExtent, Box, Rect } from '../types'; +import type { Dimensions, Node, XYPosition, CoordinateExtent, Box, Rect } from '../types'; export const getDimensions = (node: HTMLDivElement): Dimensions => ({ width: node.offsetWidth, @@ -36,9 +36,25 @@ export const boxToRect = ({ x, y, x2, y2 }: Box): Rect => ({ height: y2 - y, }); +export const nodeToRect = (node: Node): Rect => ({ + ...(node.positionAbsolute || { x: 0, y: 0 }), + width: node.width || 0, + height: node.height || 0, +}); + export const getBoundsOfRects = (rect1: Rect, rect2: Rect): Rect => boxToRect(getBoundsOfBoxes(rectToBox(rect1), rectToBox(rect2))); +export const getOverlappingArea = (rectA: Rect, rectB: Rect): number => { + const xOverlap = Math.max(0, Math.min(rectA.x + rectA.width, rectB.x + rectB.width) - Math.max(rectA.x, rectB.x)); + const yOverlap = Math.max(0, Math.min(rectA.y + rectA.height, rectB.y + rectB.height) - Math.max(rectA.y, rectB.y)); + + return Math.ceil(xOverlap * yOverlap); +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const isRectObject = (obj: any): obj is Rect => !!obj.width && !!obj.height && !!obj.x && !!obj.y; + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ export const isNumeric = (n: any): n is number => !isNaN(n) && isFinite(n); diff --git a/packages/minimap/CHANGELOG.md b/packages/minimap/CHANGELOG.md index 4fd440ab..c9660630 100644 --- a/packages/minimap/CHANGELOG.md +++ b/packages/minimap/CHANGELOG.md @@ -1,5 +1,16 @@ # @reactflow/minimap +## 11.1.0 + +### Minor Changes + +- [#2530](https://github.com/wbkd/react-flow/pull/2530) [`8ba4dd5d`](https://github.com/wbkd/react-flow/commit/8ba4dd5d1d4b2e6f107c148de62aec0b688d8b21) Thanks [@moklick](https://github.com/moklick)! - Feat: Add pan and zoom to mini map + +### Patch Changes + +- Updated dependencies [[`740659c0`](https://github.com/wbkd/react-flow/commit/740659c0e788c7572d4a1e64e1d33d60712233fc), [`7902a3ce`](https://github.com/wbkd/react-flow/commit/7902a3ce3188426d5cd07cf0943a68f679e67948), [`b25d499e`](https://github.com/wbkd/react-flow/commit/b25d499ec05b5c6f21ac552d03650eb37433552e), [`4fc1253e`](https://github.com/wbkd/react-flow/commit/4fc1253eadf9b7dd392d8dc2348f44fa8d08f931), [`8ba4dd5d`](https://github.com/wbkd/react-flow/commit/8ba4dd5d1d4b2e6f107c148de62aec0b688d8b21)]: + - @reactflow/core@11.2.0 + ## 11.0.3 ### Patch Changes diff --git a/packages/minimap/package.json b/packages/minimap/package.json index 527aab38..49dcd7cc 100644 --- a/packages/minimap/package.json +++ b/packages/minimap/package.json @@ -1,6 +1,6 @@ { "name": "@reactflow/minimap", - "version": "11.0.3", + "version": "11.1.0", "description": "Minimap component for React Flow.", "keywords": [ "react", @@ -40,7 +40,11 @@ "dependencies": { "@babel/runtime": "^7.18.9", "@reactflow/core": "workspace:*", + "@types/d3-selection": "^3.0.3", + "@types/d3-zoom": "^3.0.1", "classcat": "^5.0.3", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", "zustand": "^4.1.1" }, "peerDependencies": { diff --git a/packages/minimap/src/MiniMap.tsx b/packages/minimap/src/MiniMap.tsx index 05cd8771..f2487f18 100644 --- a/packages/minimap/src/MiniMap.tsx +++ b/packages/minimap/src/MiniMap.tsx @@ -1,8 +1,13 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { memo } from 'react'; +import { memo, useEffect, useRef } from 'react'; +import type { MouseEvent } from 'react'; import cc from 'classcat'; import shallow from 'zustand/shallow'; -import { useStore, getRectOfNodes, getBoundsOfRects, Panel } from '@reactflow/core'; +import { zoom, zoomIdentity } from 'd3-zoom'; +import type { D3ZoomEvent } from 'd3-zoom'; +import { select, pointer } from 'd3-selection'; +import { useStore, getRectOfNodes, Panel, getBoundsOfRects, useStoreApi } from '@reactflow/core'; import type { ReactFlowState, Rect } from '@reactflow/core'; import MiniMapNode from './MiniMapNode'; @@ -37,14 +42,20 @@ const ARIA_LABEL_KEY = 'react-flow__minimap-desc'; function MiniMap({ style, className, - nodeStrokeColor = '#555', - nodeColor = '#fff', + nodeStrokeColor = 'transparent', + nodeColor = '#e2e2e2', nodeClassName = '', nodeBorderRadius = 5, nodeStrokeWidth = 2, - maskColor = 'rgb(240, 242, 243, 0.7)', + maskColor = 'rgb(240, 240, 240, 0.6)', position = 'bottom-right', + onClick, + onNodeClick, + pannable = false, + zoomable = false, }: MiniMapProps) { + const store = useStoreApi(); + const svg = useRef(null); const { boundingRect, viewBB, nodes, rfId } = useStore(selector, shallow); const elementWidth = (style?.width as number) ?? defaultWidth; const elementHeight = (style?.height as number) ?? defaultHeight; @@ -63,6 +74,75 @@ function MiniMap({ const height = viewHeight + offset * 2; const shapeRendering = typeof window === 'undefined' || !!window.chrome ? 'crispEdges' : 'geometricPrecision'; const labelledBy = `${ARIA_LABEL_KEY}-${rfId}`; + const viewScaleRef = useRef(0); + + viewScaleRef.current = viewScale; + + useEffect(() => { + if (svg.current) { + const selection = select(svg.current as Element); + + const zoomHandler = (event: D3ZoomEvent) => { + const { transform, d3Selection, d3Zoom } = store.getState(); + + if (event.sourceEvent.type !== 'wheel' || !d3Selection || !d3Zoom) { + return; + } + + const pinchDelta = + -event.sourceEvent.deltaY * + (event.sourceEvent.deltaMode === 1 ? 0.05 : event.sourceEvent.deltaMode ? 1 : 0.002) * + 10; + const zoom = transform[2] * Math.pow(2, pinchDelta); + + d3Zoom.scaleTo(d3Selection, zoom); + }; + + const panHandler = (event: D3ZoomEvent) => { + const { transform, d3Selection, d3Zoom } = store.getState(); + + if (event.sourceEvent.type !== 'mousemove' || !d3Selection || !d3Zoom) { + return; + } + + // @TODO: how to calculate the correct next position? Math.max(1, transform[2]) is a workaround. + const position = { + x: transform[0] - event.sourceEvent.movementX * viewScaleRef.current * Math.max(1, transform[2]), + y: transform[1] - event.sourceEvent.movementY * viewScaleRef.current * Math.max(1, transform[2]), + }; + + const nextTransform = zoomIdentity.translate(position.x, position.y).scale(transform[2]); + + d3Zoom.transform(d3Selection, nextTransform); + }; + + const zoomAndPanHandler = zoom() + // @ts-ignore + .on('zoom', pannable ? panHandler : null) + // @ts-ignore + .on('zoom.wheel', zoomable ? zoomHandler : null); + + selection.call(zoomAndPanHandler); + + return () => { + selection.on('zoom', null); + }; + } + }, [pannable, zoomable]); + + const onSvgClick = onClick + ? (event: MouseEvent) => { + const rfCoord = pointer(event); + onClick(event, { x: rfCoord[0], y: rfCoord[1] }); + } + : undefined; + + const onSvgNodeClick = onNodeClick + ? (event: MouseEvent, nodeId: string) => { + const node = store.getState().nodeInternals.get(nodeId)!; + onNodeClick(event, node); + } + : undefined; return ( @@ -72,6 +152,8 @@ function MiniMap({ viewBox={`${x} ${y} ${width} ${height}`} role="img" aria-labelledby={labelledBy} + ref={svg} + onClick={onSvgClick} > React Flow mini map {nodes.map((node) => { @@ -89,6 +171,8 @@ function MiniMap({ strokeColor={nodeStrokeColorFunc(node)} strokeWidth={nodeStrokeWidth} shapeRendering={shapeRendering} + onClick={onSvgNodeClick} + id={node.id} /> ); })} diff --git a/packages/minimap/src/MiniMapNode.tsx b/packages/minimap/src/MiniMapNode.tsx index 111c7ee0..ae000d02 100644 --- a/packages/minimap/src/MiniMapNode.tsx +++ b/packages/minimap/src/MiniMapNode.tsx @@ -1,8 +1,9 @@ import { memo } from 'react'; -import type { CSSProperties } from 'react'; +import type { CSSProperties, MouseEvent } from 'react'; import cc from 'classcat'; interface MiniMapNodeProps { + id: string; x: number; y: number; width: number; @@ -14,9 +15,11 @@ interface MiniMapNodeProps { strokeColor: string; strokeWidth: number; style?: CSSProperties; + onClick?: (event: MouseEvent, id: string) => void; } const MiniMapNode = ({ + id, x, y, width, @@ -28,6 +31,7 @@ const MiniMapNode = ({ className, borderRadius, shapeRendering, + onClick, }: MiniMapNodeProps) => { const { background, backgroundColor } = style || {}; const fill = (color || background || backgroundColor) as string; @@ -45,6 +49,7 @@ const MiniMapNode = ({ stroke={strokeColor} strokeWidth={strokeWidth} shapeRendering={shapeRendering} + onClick={onClick ? (event) => onClick(event, id) : undefined} /> ); }; diff --git a/packages/minimap/src/types.ts b/packages/minimap/src/types.ts index 4fed29ba..63d8c6d9 100644 --- a/packages/minimap/src/types.ts +++ b/packages/minimap/src/types.ts @@ -1,10 +1,10 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import type { HTMLAttributes } from 'react'; -import type { Node, PanelPosition } from '@reactflow/core'; +import type { HTMLAttributes, MouseEvent } from 'react'; +import type { Node, PanelPosition, XYPosition } from '@reactflow/core'; export type GetMiniMapNodeAttribute = (node: Node) => string; -export type MiniMapProps = HTMLAttributes & { +export type MiniMapProps = Omit, 'onClick'> & { nodeColor?: string | GetMiniMapNodeAttribute; nodeStrokeColor?: string | GetMiniMapNodeAttribute; nodeClassName?: string | GetMiniMapNodeAttribute; @@ -12,4 +12,8 @@ export type MiniMapProps = HTMLAttributes & { nodeStrokeWidth?: number; maskColor?: string; position?: PanelPosition; + onClick?: (event: MouseEvent, position: XYPosition) => void; + onNodeClick?: (event: MouseEvent, node: Node) => void; + pannable?: boolean; + zoomable?: boolean; }; diff --git a/packages/reactflow/CHANGELOG.md b/packages/reactflow/CHANGELOG.md index a0d75bf3..10b93714 100644 --- a/packages/reactflow/CHANGELOG.md +++ b/packages/reactflow/CHANGELOG.md @@ -1,5 +1,27 @@ # reactflow +## 11.2.0 + +### Minor Changes + +- [#2535](https://github.com/wbkd/react-flow/pull/2535) [`7902a3ce`](https://github.com/wbkd/react-flow/commit/7902a3ce3188426d5cd07cf0943a68f679e67948) Thanks [@moklick](https://github.com/moklick)! - Feat: Add edge label renderer + +- [#2536](https://github.com/wbkd/react-flow/pull/2536) [`b25d499e`](https://github.com/wbkd/react-flow/commit/b25d499ec05b5c6f21ac552d03650eb37433552e) Thanks [@pengfu](https://github.com/pengfu)! - Feat: add deleteElements helper function + +- [#2539](https://github.com/wbkd/react-flow/pull/2539) [`4fc1253e`](https://github.com/wbkd/react-flow/commit/4fc1253eadf9b7dd392d8dc2348f44fa8d08f931) Thanks [@moklick](https://github.com/moklick)! - Feat: add intersection helpers + +- [#2530](https://github.com/wbkd/react-flow/pull/2530) [`8ba4dd5d`](https://github.com/wbkd/react-flow/commit/8ba4dd5d1d4b2e6f107c148de62aec0b688d8b21) Thanks [@moklick](https://github.com/moklick)! - Feat: Add pan and zoom to mini map + +### Patch Changes + +- [#2538](https://github.com/wbkd/react-flow/pull/2538) [`740659c0`](https://github.com/wbkd/react-flow/commit/740659c0e788c7572d4a1e64e1d33d60712233fc) Thanks [@neo](https://github.com/neo)! - Refactor: put React Flow in isolated stacking context + +- Updated dependencies [[`740659c0`](https://github.com/wbkd/react-flow/commit/740659c0e788c7572d4a1e64e1d33d60712233fc), [`7902a3ce`](https://github.com/wbkd/react-flow/commit/7902a3ce3188426d5cd07cf0943a68f679e67948), [`b25d499e`](https://github.com/wbkd/react-flow/commit/b25d499ec05b5c6f21ac552d03650eb37433552e), [`4fc1253e`](https://github.com/wbkd/react-flow/commit/4fc1253eadf9b7dd392d8dc2348f44fa8d08f931), [`8ba4dd5d`](https://github.com/wbkd/react-flow/commit/8ba4dd5d1d4b2e6f107c148de62aec0b688d8b21)]: + - @reactflow/core@11.2.0 + - @reactflow/minimap@11.1.0 + - @reactflow/background@11.0.4 + - @reactflow/controls@11.0.4 + ## 11.1.2 Housekeeping release with some fixes and some cleanups for the types. diff --git a/packages/reactflow/package.json b/packages/reactflow/package.json index d26d4e28..3cd02d16 100644 --- a/packages/reactflow/package.json +++ b/packages/reactflow/package.json @@ -1,6 +1,6 @@ { "name": "reactflow", - "version": "11.1.2", + "version": "11.2.0", "description": "A highly customizable React library for building node-based editors and interactive flow charts", "keywords": [ "react", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 78749f21..afd78032 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,15 +32,15 @@ importers: '@changesets/changelog-github': registry.npmjs.org/@changesets/changelog-github/0.4.7 '@changesets/cli': registry.npmjs.org/@changesets/cli/2.25.0 '@preconstruct/cli': registry.npmjs.org/@preconstruct/cli/2.2.1 - '@typescript-eslint/eslint-plugin': registry.npmjs.org/@typescript-eslint/eslint-plugin/5.39.0_jdrczqv3ll7udvbunca43w7rz4 - '@typescript-eslint/parser': registry.npmjs.org/@typescript-eslint/parser/5.39.0_irgkl5vooow2ydyo6aokmferha + '@typescript-eslint/eslint-plugin': registry.npmjs.org/@typescript-eslint/eslint-plugin/5.42.0_dcn2ddfkdgyby36a3kmqplwxkq + '@typescript-eslint/parser': registry.npmjs.org/@typescript-eslint/parser/5.42.0_irgkl5vooow2ydyo6aokmferha autoprefixer: registry.npmjs.org/autoprefixer/10.4.9_postcss@8.4.16 concurrently: registry.npmjs.org/concurrently/7.4.0 cypress: registry.npmjs.org/cypress/10.7.0 eslint: registry.npmjs.org/eslint/8.23.1 eslint-config-prettier: registry.npmjs.org/eslint-config-prettier/8.5.0_eslint@8.23.1 eslint-plugin-prettier: registry.npmjs.org/eslint-plugin-prettier/4.2.1_cabrci5exjdaojcvd6xoxgeowu - eslint-plugin-react: registry.npmjs.org/eslint-plugin-react/7.31.9_eslint@8.23.1 + eslint-plugin-react: registry.npmjs.org/eslint-plugin-react/7.31.10_eslint@8.23.1 postcss: registry.npmjs.org/postcss/8.4.16 postcss-cli: registry.npmjs.org/postcss-cli/10.0.0_postcss@8.4.16 postcss-combine-duplicated-selectors: registry.npmjs.org/postcss-combine-duplicated-selectors/10.0.3_postcss@8.4.16 @@ -152,6 +152,7 @@ importers: '@types/d3-zoom': ^3.0.1 '@types/node': ^18.7.16 '@types/react': ^18.0.17 + '@types/react-dom': ^18.0.6 classcat: ^5.0.3 d3-drag: ^3.0.0 d3-selection: ^3.0.0 @@ -175,6 +176,7 @@ importers: '@reactflow/tsconfig': link:../../tooling/tsconfig '@types/node': registry.npmjs.org/@types/node/18.7.16 '@types/react': registry.npmjs.org/@types/react/18.0.19 + '@types/react-dom': registry.npmjs.org/@types/react-dom/18.0.6 react: registry.npmjs.org/react/18.2.0 typescript: registry.npmjs.org/typescript/4.8.3 @@ -185,16 +187,24 @@ importers: '@reactflow/eslint-config': workspace:^0.0.0 '@reactflow/rollup-config': workspace:* '@reactflow/tsconfig': workspace:* + '@types/d3-selection': ^3.0.3 + '@types/d3-zoom': ^3.0.1 '@types/node': ^18.7.16 '@types/react': ^18.0.19 classcat: ^5.0.3 + d3-selection: ^3.0.0 + d3-zoom: ^3.0.0 react: ^18.2.0 typescript: ^4.8.3 zustand: ^4.1.1 dependencies: '@babel/runtime': registry.npmjs.org/@babel/runtime/7.19.0 '@reactflow/core': link:../core + '@types/d3-selection': registry.npmjs.org/@types/d3-selection/3.0.3 + '@types/d3-zoom': registry.npmjs.org/@types/d3-zoom/3.0.1 classcat: registry.npmjs.org/classcat/5.0.4 + d3-selection: registry.npmjs.org/d3-selection/3.0.0 + d3-zoom: registry.npmjs.org/d3-zoom/3.0.0 zustand: registry.npmjs.org/zustand/4.1.1_react@18.2.0 devDependencies: '@reactflow/eslint-config': link:../../tooling/eslint-config @@ -242,7 +252,7 @@ importers: eslint: registry.npmjs.org/eslint/8.23.1 eslint-config-prettier: registry.npmjs.org/eslint-config-prettier/8.5.0_eslint@8.23.1 eslint-config-turbo: registry.npmjs.org/eslint-config-turbo/0.0.4_eslint@8.23.1 - eslint-plugin-react: registry.npmjs.org/eslint-plugin-react/7.31.8_eslint@8.23.1 + eslint-plugin-react: registry.npmjs.org/eslint-plugin-react/7.31.10_eslint@8.23.1 tooling/rollup-config: specifiers: @@ -1682,6 +1692,12 @@ packages: version: 6.2.3 dev: true + registry.npmjs.org/@types/semver/7.3.13: + resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz} + name: '@types/semver' + version: 7.3.13 + dev: true + registry.npmjs.org/@types/sinonjs__fake-timers/8.1.1: resolution: {integrity: sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz} name: '@types/sinonjs__fake-timers' @@ -1704,11 +1720,11 @@ packages: dev: true optional: true - registry.npmjs.org/@typescript-eslint/eslint-plugin/5.39.0_jdrczqv3ll7udvbunca43w7rz4: - resolution: {integrity: sha512-xVfKOkBm5iWMNGKQ2fwX5GVgBuHmZBO1tCRwXmY5oAIsPscfwm2UADDuNB8ZVYCtpQvJK4xpjrK7jEhcJ0zY9A==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.39.0.tgz} - id: registry.npmjs.org/@typescript-eslint/eslint-plugin/5.39.0 + registry.npmjs.org/@typescript-eslint/eslint-plugin/5.42.0_dcn2ddfkdgyby36a3kmqplwxkq: + resolution: {integrity: sha512-5TJh2AgL6+wpL8H/GTSjNb4WrjKoR2rqvFxR/DDTqYNk6uXn8BJMEcncLSpMbf/XV1aS0jAjYwn98uvVCiAywQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.42.0.tgz} + id: registry.npmjs.org/@typescript-eslint/eslint-plugin/5.42.0 name: '@typescript-eslint/eslint-plugin' - version: 5.39.0 + version: 5.42.0 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: '@typescript-eslint/parser': ^5.0.0 @@ -1718,13 +1734,14 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': registry.npmjs.org/@typescript-eslint/parser/5.39.0_irgkl5vooow2ydyo6aokmferha - '@typescript-eslint/scope-manager': registry.npmjs.org/@typescript-eslint/scope-manager/5.39.0 - '@typescript-eslint/type-utils': registry.npmjs.org/@typescript-eslint/type-utils/5.39.0_irgkl5vooow2ydyo6aokmferha - '@typescript-eslint/utils': registry.npmjs.org/@typescript-eslint/utils/5.39.0_irgkl5vooow2ydyo6aokmferha + '@typescript-eslint/parser': registry.npmjs.org/@typescript-eslint/parser/5.42.0_irgkl5vooow2ydyo6aokmferha + '@typescript-eslint/scope-manager': registry.npmjs.org/@typescript-eslint/scope-manager/5.42.0 + '@typescript-eslint/type-utils': registry.npmjs.org/@typescript-eslint/type-utils/5.42.0_irgkl5vooow2ydyo6aokmferha + '@typescript-eslint/utils': registry.npmjs.org/@typescript-eslint/utils/5.42.0_irgkl5vooow2ydyo6aokmferha debug: registry.npmjs.org/debug/4.3.4 eslint: registry.npmjs.org/eslint/8.23.1 ignore: registry.npmjs.org/ignore/5.2.0 + natural-compare-lite: registry.npmjs.org/natural-compare-lite/1.4.0 regexpp: registry.npmjs.org/regexpp/3.2.0 semver: registry.npmjs.org/semver/7.3.8 tsutils: registry.npmjs.org/tsutils/3.21.0_typescript@4.8.3 @@ -1733,11 +1750,11 @@ packages: - supports-color dev: true - registry.npmjs.org/@typescript-eslint/parser/5.39.0_irgkl5vooow2ydyo6aokmferha: - resolution: {integrity: sha512-PhxLjrZnHShe431sBAGHaNe6BDdxAASDySgsBCGxcBecVCi8NQWxQZMcizNA4g0pN51bBAn/FUfkWG3SDVcGlA==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.39.0.tgz} - id: registry.npmjs.org/@typescript-eslint/parser/5.39.0 + registry.npmjs.org/@typescript-eslint/parser/5.42.0_irgkl5vooow2ydyo6aokmferha: + resolution: {integrity: sha512-Ixh9qrOTDRctFg3yIwrLkgf33AHyEIn6lhyf5cCfwwiGtkWhNpVKlEZApi3inGQR/barWnY7qY8FbGKBO7p3JA==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.42.0.tgz} + id: registry.npmjs.org/@typescript-eslint/parser/5.42.0 name: '@typescript-eslint/parser' - version: 5.39.0 + version: 5.42.0 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -1746,9 +1763,9 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': registry.npmjs.org/@typescript-eslint/scope-manager/5.39.0 - '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.39.0 - '@typescript-eslint/typescript-estree': registry.npmjs.org/@typescript-eslint/typescript-estree/5.39.0_typescript@4.8.3 + '@typescript-eslint/scope-manager': registry.npmjs.org/@typescript-eslint/scope-manager/5.42.0 + '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.42.0 + '@typescript-eslint/typescript-estree': registry.npmjs.org/@typescript-eslint/typescript-estree/5.42.0_typescript@4.8.3 debug: registry.npmjs.org/debug/4.3.4 eslint: registry.npmjs.org/eslint/8.23.1 typescript: registry.npmjs.org/typescript/4.8.3 @@ -1756,21 +1773,21 @@ packages: - supports-color dev: true - registry.npmjs.org/@typescript-eslint/scope-manager/5.39.0: - resolution: {integrity: sha512-/I13vAqmG3dyqMVSZPjsbuNQlYS082Y7OMkwhCfLXYsmlI0ca4nkL7wJ/4gjX70LD4P8Hnw1JywUVVAwepURBw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.39.0.tgz} + registry.npmjs.org/@typescript-eslint/scope-manager/5.42.0: + resolution: {integrity: sha512-l5/3IBHLH0Bv04y+H+zlcLiEMEMjWGaCX6WyHE5Uk2YkSGAMlgdUPsT/ywTSKgu9D1dmmKMYgYZijObfA39Wow==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.42.0.tgz} name: '@typescript-eslint/scope-manager' - version: 5.39.0 + version: 5.42.0 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.39.0 - '@typescript-eslint/visitor-keys': registry.npmjs.org/@typescript-eslint/visitor-keys/5.39.0 + '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.42.0 + '@typescript-eslint/visitor-keys': registry.npmjs.org/@typescript-eslint/visitor-keys/5.42.0 dev: true - registry.npmjs.org/@typescript-eslint/type-utils/5.39.0_irgkl5vooow2ydyo6aokmferha: - resolution: {integrity: sha512-KJHJkOothljQWzR3t/GunL0TPKY+fGJtnpl+pX+sJ0YiKTz3q2Zr87SGTmFqsCMFrLt5E0+o+S6eQY0FAXj9uA==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.39.0.tgz} - id: registry.npmjs.org/@typescript-eslint/type-utils/5.39.0 + registry.npmjs.org/@typescript-eslint/type-utils/5.42.0_irgkl5vooow2ydyo6aokmferha: + resolution: {integrity: sha512-HW14TXC45dFVZxnVW8rnUGnvYyRC0E/vxXShFCthcC9VhVTmjqOmtqj6H5rm9Zxv+ORxKA/1aLGD7vmlLsdlOg==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.42.0.tgz} + id: registry.npmjs.org/@typescript-eslint/type-utils/5.42.0 name: '@typescript-eslint/type-utils' - version: 5.39.0 + version: 5.42.0 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: '*' @@ -1779,8 +1796,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': registry.npmjs.org/@typescript-eslint/typescript-estree/5.39.0_typescript@4.8.3 - '@typescript-eslint/utils': registry.npmjs.org/@typescript-eslint/utils/5.39.0_irgkl5vooow2ydyo6aokmferha + '@typescript-eslint/typescript-estree': registry.npmjs.org/@typescript-eslint/typescript-estree/5.42.0_typescript@4.8.3 + '@typescript-eslint/utils': registry.npmjs.org/@typescript-eslint/utils/5.42.0_irgkl5vooow2ydyo6aokmferha debug: registry.npmjs.org/debug/4.3.4 eslint: registry.npmjs.org/eslint/8.23.1 tsutils: registry.npmjs.org/tsutils/3.21.0_typescript@4.8.3 @@ -1789,18 +1806,18 @@ packages: - supports-color dev: true - registry.npmjs.org/@typescript-eslint/types/5.39.0: - resolution: {integrity: sha512-gQMZrnfEBFXK38hYqt8Lkwt8f4U6yq+2H5VDSgP/qiTzC8Nw8JO3OuSUOQ2qW37S/dlwdkHDntkZM6SQhKyPhw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/types/-/types-5.39.0.tgz} + registry.npmjs.org/@typescript-eslint/types/5.42.0: + resolution: {integrity: sha512-t4lzO9ZOAUcHY6bXQYRuu+3SSYdD9TS8ooApZft4WARt4/f2Cj/YpvbTe8A4GuhT4bNW72goDMOy7SW71mZwGw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/types/-/types-5.42.0.tgz} name: '@typescript-eslint/types' - version: 5.39.0 + version: 5.42.0 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - registry.npmjs.org/@typescript-eslint/typescript-estree/5.39.0_typescript@4.8.3: - resolution: {integrity: sha512-qLFQP0f398sdnogJoLtd43pUgB18Q50QSA+BTE5h3sUxySzbWDpTSdgt4UyxNSozY/oDK2ta6HVAzvGgq8JYnA==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.39.0.tgz} - id: registry.npmjs.org/@typescript-eslint/typescript-estree/5.39.0 + registry.npmjs.org/@typescript-eslint/typescript-estree/5.42.0_typescript@4.8.3: + resolution: {integrity: sha512-2O3vSq794x3kZGtV7i4SCWZWCwjEtkWfVqX4m5fbUBomOsEOyd6OAD1qU2lbvV5S8tgy/luJnOYluNyYVeOTTg==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.42.0.tgz} + id: registry.npmjs.org/@typescript-eslint/typescript-estree/5.42.0 name: '@typescript-eslint/typescript-estree' - version: 5.39.0 + version: 5.42.0 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: typescript: '*' @@ -1808,8 +1825,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.39.0 - '@typescript-eslint/visitor-keys': registry.npmjs.org/@typescript-eslint/visitor-keys/5.39.0 + '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.42.0 + '@typescript-eslint/visitor-keys': registry.npmjs.org/@typescript-eslint/visitor-keys/5.42.0 debug: registry.npmjs.org/debug/4.3.4 globby: registry.npmjs.org/globby/11.1.0 is-glob: registry.npmjs.org/is-glob/4.0.3 @@ -1820,34 +1837,36 @@ packages: - supports-color dev: true - registry.npmjs.org/@typescript-eslint/utils/5.39.0_irgkl5vooow2ydyo6aokmferha: - resolution: {integrity: sha512-+DnY5jkpOpgj+EBtYPyHRjXampJfC0yUZZzfzLuUWVZvCuKqSdJVC8UhdWipIw7VKNTfwfAPiOWzYkAwuIhiAg==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.39.0.tgz} - id: registry.npmjs.org/@typescript-eslint/utils/5.39.0 + registry.npmjs.org/@typescript-eslint/utils/5.42.0_irgkl5vooow2ydyo6aokmferha: + resolution: {integrity: sha512-JZ++3+h1vbeG1NUECXQZE3hg0kias9kOtcQr3+JVQ3whnjvKuMyktJAAIj6743OeNPnGBmjj7KEmiDL7qsdnCQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.42.0.tgz} + id: registry.npmjs.org/@typescript-eslint/utils/5.42.0 name: '@typescript-eslint/utils' - version: 5.39.0 + version: 5.42.0 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: '@types/json-schema': registry.npmjs.org/@types/json-schema/7.0.11 - '@typescript-eslint/scope-manager': registry.npmjs.org/@typescript-eslint/scope-manager/5.39.0 - '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.39.0 - '@typescript-eslint/typescript-estree': registry.npmjs.org/@typescript-eslint/typescript-estree/5.39.0_typescript@4.8.3 + '@types/semver': registry.npmjs.org/@types/semver/7.3.13 + '@typescript-eslint/scope-manager': registry.npmjs.org/@typescript-eslint/scope-manager/5.42.0 + '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.42.0 + '@typescript-eslint/typescript-estree': registry.npmjs.org/@typescript-eslint/typescript-estree/5.42.0_typescript@4.8.3 eslint: registry.npmjs.org/eslint/8.23.1 eslint-scope: registry.npmjs.org/eslint-scope/5.1.1 eslint-utils: registry.npmjs.org/eslint-utils/3.0.0_eslint@8.23.1 + semver: registry.npmjs.org/semver/7.3.8 transitivePeerDependencies: - supports-color - typescript dev: true - registry.npmjs.org/@typescript-eslint/visitor-keys/5.39.0: - resolution: {integrity: sha512-yyE3RPwOG+XJBLrhvsxAidUgybJVQ/hG8BhiJo0k8JSAYfk/CshVcxf0HwP4Jt7WZZ6vLmxdo1p6EyN3tzFTkg==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.39.0.tgz} + registry.npmjs.org/@typescript-eslint/visitor-keys/5.42.0: + resolution: {integrity: sha512-QHbu5Hf/2lOEOwy+IUw0GoSCuAzByTAWWrOTKzTzsotiUnWFpuKnXcAhC9YztAf2EElQ0VvIK+pHJUPkM0q7jg==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.42.0.tgz} name: '@typescript-eslint/visitor-keys' - version: 5.39.0 + version: 5.42.0 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.39.0 + '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.42.0 eslint-visitor-keys: registry.npmjs.org/eslint-visitor-keys/3.3.0 dev: true @@ -3370,37 +3389,11 @@ packages: prettier-linter-helpers: registry.npmjs.org/prettier-linter-helpers/1.0.0 dev: true - registry.npmjs.org/eslint-plugin-react/7.31.8_eslint@8.23.1: - resolution: {integrity: sha512-5lBTZmgQmARLLSYiwI71tiGVTLUuqXantZM6vlSY39OaDSV0M7+32K5DnLkmFrwTe+Ksz0ffuLUC91RUviVZfw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.8.tgz} - id: registry.npmjs.org/eslint-plugin-react/7.31.8 + registry.npmjs.org/eslint-plugin-react/7.31.10_eslint@8.23.1: + resolution: {integrity: sha512-e4N/nc6AAlg4UKW/mXeYWd3R++qUano5/o+t+wnWxIf+bLsOaH3a4q74kX3nDjYym3VBN4HyO9nEn1GcAqgQOA==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.10.tgz} + id: registry.npmjs.org/eslint-plugin-react/7.31.10 name: eslint-plugin-react - version: 7.31.8 - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - dependencies: - array-includes: registry.npmjs.org/array-includes/3.1.5 - array.prototype.flatmap: registry.npmjs.org/array.prototype.flatmap/1.3.0 - doctrine: registry.npmjs.org/doctrine/2.1.0 - eslint: registry.npmjs.org/eslint/8.23.1 - estraverse: registry.npmjs.org/estraverse/5.3.0 - jsx-ast-utils: registry.npmjs.org/jsx-ast-utils/3.3.3 - minimatch: registry.npmjs.org/minimatch/3.1.2 - object.entries: registry.npmjs.org/object.entries/1.1.5 - object.fromentries: registry.npmjs.org/object.fromentries/2.0.5 - object.hasown: registry.npmjs.org/object.hasown/1.1.1 - object.values: registry.npmjs.org/object.values/1.1.5 - prop-types: registry.npmjs.org/prop-types/15.8.1 - resolve: registry.npmjs.org/resolve/2.0.0-next.4 - semver: registry.npmjs.org/semver/6.3.0 - string.prototype.matchall: registry.npmjs.org/string.prototype.matchall/4.0.7 - dev: true - - registry.npmjs.org/eslint-plugin-react/7.31.9_eslint@8.23.1: - resolution: {integrity: sha512-vrVJwusIw4L99lyfXjtCw8HWdloajsiYslMavogrBe2Gl8gr95TJsJnOMRasN4b4N24I3XuJf6aAV6MhyGmjqw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.9.tgz} - id: registry.npmjs.org/eslint-plugin-react/7.31.9 - name: eslint-plugin-react - version: 7.31.9 + version: 7.31.10 engines: {node: '>=4'} peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 @@ -5174,6 +5167,12 @@ packages: hasBin: true dev: true + registry.npmjs.org/natural-compare-lite/1.4.0: + resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz} + name: natural-compare-lite + version: 1.4.0 + dev: true + registry.npmjs.org/natural-compare/1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz} name: natural-compare