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/examples/vite-app/src/examples/Figma/index.tsx b/examples/vite-app/src/examples/Figma/index.tsx index 22f0dcce..64cfb4bb 100644 --- a/examples/vite-app/src/examples/Figma/index.tsx +++ b/examples/vite-app/src/examples/Figma/index.tsx @@ -38,6 +38,9 @@ const BasicFlow = () => { onSelectionContextMenu={onPaneContextMenu} > +
+ +
); }; diff --git a/examples/vite-app/src/examples/NodeResizer/CustomResizer.tsx b/examples/vite-app/src/examples/NodeResizer/CustomResizer.tsx index 6cf58fd9..a36d9f18 100644 --- a/examples/vite-app/src/examples/NodeResizer/CustomResizer.tsx +++ b/examples/vite-app/src/examples/NodeResizer/CustomResizer.tsx @@ -10,10 +10,21 @@ const controlStyle = { border: 'none', }; -const CustomNode: FC = ({ id, data }) => { +const CustomResizerNode: FC = ({ data }) => { return ( <> - + @@ -24,4 +35,4 @@ const CustomNode: FC = ({ id, data }) => { ); }; -export default memo(CustomNode); +export default memo(CustomResizerNode); diff --git a/examples/vite-app/src/examples/NodeResizer/CustomResizer2.tsx b/examples/vite-app/src/examples/NodeResizer/CustomResizer2.tsx deleted file mode 100644 index 63ff061b..00000000 --- a/examples/vite-app/src/examples/NodeResizer/CustomResizer2.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { memo, FC } from 'react'; -import { Handle, Position, NodeProps } from 'reactflow'; - -import { NodeResizeControl } from '@reactflow/node-resizer'; -import '@reactflow/node-resizer/dist/style.css'; - -const CustomNode: FC = ({ id, data }) => { - return ( - <> - - - -
{data.label}
- - - ); -}; - -export default memo(CustomNode); diff --git a/examples/vite-app/src/examples/NodeResizer/CustomResizer3.tsx b/examples/vite-app/src/examples/NodeResizer/CustomResizer3.tsx deleted file mode 100644 index 422f2982..00000000 --- a/examples/vite-app/src/examples/NodeResizer/CustomResizer3.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { memo, FC } from 'react'; -import { Handle, Position, NodeProps } from 'reactflow'; - -import { NodeResizeControl, OnResize, ShouldResize } from '@reactflow/node-resizer'; -import '@reactflow/node-resizer/dist/style.css'; - -const shouldResize: ShouldResize = (event, params) => { - console.log('before resize', params); - - if (params.width > 100) { - return false; - } - - return true; -}; - -const onResize: OnResize = (event, params) => { - console.log('resize', params.direction); -}; - -const CustomNode: FC = ({ id, data }) => { - return ( - <> - - - -
{data.label}
- - - ); -}; - -export default memo(CustomNode); diff --git a/examples/vite-app/src/examples/NodeResizer/DefaultResizer.tsx b/examples/vite-app/src/examples/NodeResizer/DefaultResizer.tsx new file mode 100644 index 00000000..3d9649a5 --- /dev/null +++ b/examples/vite-app/src/examples/NodeResizer/DefaultResizer.tsx @@ -0,0 +1,29 @@ +import { memo, FC } from 'react'; +import { Handle, Position, NodeProps } from 'reactflow'; +import { NodeResizer } from '@reactflow/node-resizer'; + +import '@reactflow/node-resizer/dist/style.css'; + +const DefaultResizerNode: FC = ({ data, selected }) => { + return ( + <> + + +
{data.label}
+ + + ); +}; + +export default memo(DefaultResizerNode); diff --git a/examples/vite-app/src/examples/NodeResizer/HorizontalResizer.tsx b/examples/vite-app/src/examples/NodeResizer/HorizontalResizer.tsx new file mode 100644 index 00000000..2798ca3f --- /dev/null +++ b/examples/vite-app/src/examples/NodeResizer/HorizontalResizer.tsx @@ -0,0 +1,43 @@ +import { memo, FC } from 'react'; +import { Handle, Position, NodeProps } from 'reactflow'; +import { NodeResizeControl } from '@reactflow/node-resizer'; + +import '@reactflow/node-resizer/dist/style.css'; + +const HorizontalResizerNode: FC = ({ data }) => { + return ( + <> + + + +
{data.label}
+ + + ); +}; + +export default memo(HorizontalResizerNode); diff --git a/examples/vite-app/src/examples/NodeResizer/NodeResizerNode.tsx b/examples/vite-app/src/examples/NodeResizer/NodeResizerNode.tsx deleted file mode 100644 index 03e4960f..00000000 --- a/examples/vite-app/src/examples/NodeResizer/NodeResizerNode.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { memo, FC } from 'react'; -import { Handle, Position, NodeProps } from 'reactflow'; - -import { NodeResizer, ShouldResize, OnResize, OnResizeEnd, OnResizeStart } from '@reactflow/node-resizer'; -import '@reactflow/node-resizer/dist/style.css'; - -const onResizeStart: OnResizeStart = (_, params) => { - console.log('resize start', params); -}; - -const onResize: OnResize = (_, params) => { - console.log('resize', params); -}; - -const onResizeEnd: OnResizeEnd = (_, params) => { - console.log('resize end', params); -}; - -const shouldResize: ShouldResize = (_, params) => { - console.log('should resize', params); - - return true; -}; - -const CustomNode: FC = ({ data, selected }) => { - return ( - <> - - -
{data.label}
- - - ); -}; - -export default memo(CustomNode); diff --git a/examples/vite-app/src/examples/NodeResizer/VerticalResizer.tsx b/examples/vite-app/src/examples/NodeResizer/VerticalResizer.tsx new file mode 100644 index 00000000..a3d6515e --- /dev/null +++ b/examples/vite-app/src/examples/NodeResizer/VerticalResizer.tsx @@ -0,0 +1,43 @@ +import { memo, FC } from 'react'; +import { Handle, Position, NodeProps } from 'reactflow'; + +import { NodeResizeControl } from '@reactflow/node-resizer'; +import '@reactflow/node-resizer/dist/style.css'; + +const CustomNode: FC = ({ id, data }) => { + return ( + <> + + + +
{data.label}
+ + + ); +}; + +export default memo(CustomNode); diff --git a/examples/vite-app/src/examples/NodeResizer/index.tsx b/examples/vite-app/src/examples/NodeResizer/index.tsx index 01bb0851..d94a8d8b 100644 --- a/examples/vite-app/src/examples/NodeResizer/index.tsx +++ b/examples/vite-app/src/examples/NodeResizer/index.tsx @@ -1,73 +1,110 @@ import { useCallback, useState } from 'react'; -import ReactFlow, { Controls, addEdge, Position, Connection, useNodesState, useEdgesState, Panel } from 'reactflow'; +import ReactFlow, { Controls, addEdge, Connection, useNodesState, useEdgesState, Panel, Node, Edge } from 'reactflow'; -import NodeResizerNode from './NodeResizerNode'; +import DefaultResizer from './DefaultResizer'; import CustomResizer from './CustomResizer'; -import CustomResizer2 from './CustomResizer2'; -import CustomResizer3 from './CustomResizer3'; +import VerticalResizer from './VerticalResizer'; +import HorizontalResizer from './HorizontalResizer'; const nodeTypes = { - resizer: NodeResizerNode, + defaultResizer: DefaultResizer, customResizer: CustomResizer, - customResizer2: CustomResizer2, - customResizer3: CustomResizer3, + verticalResizer: VerticalResizer, + horizontalResizer: HorizontalResizer, }; -const initialEdges = [ - { - id: 'e1-2', - source: '1', - target: '2', - }, -]; +const nodeStyle = { + border: '1px solid #222', + fontSize: 10, + backgroundColor: '#ddd', +}; -const initialNodes = [ +const initialEdges: Edge[] = []; + +const initialNodes: Node[] = [ { id: '1', - type: 'input', - data: { label: 'An input node' }, + type: 'defaultResizer', + data: { label: 'default resizer' }, position: { x: 0, y: 0 }, - sourcePosition: Position.Right, + style: { ...nodeStyle }, }, { - id: '2', - type: 'resizer', - data: { label: 'default resizer' }, + id: '1a', + type: 'defaultResizer', + data: { + label: 'default resizer with min and max dimensions', + minWidth: 100, + minHeight: 80, + maxWidth: 200, + maxHeight: 200, + }, + position: { x: 0, y: 60 }, + style: { ...nodeStyle, width: 100, height: 80 }, + }, + { + id: '1b', + type: 'defaultResizer', + data: { + label: 'default resizer with initial size and aspect ratio', + keepAspectRatio: true, + minWidth: 100, + minHeight: 60, + maxWidth: 400, + maxHeight: 400, + }, position: { x: 250, y: 0 }, style: { - width: 200, - height: 150, - border: '1px solid #222', - fontSize: 10, + width: 174, + height: 123, + ...nodeStyle, }, }, { - id: '3', + id: '2', type: 'customResizer', - data: { label: 'resize control with child component' }, + data: { label: 'custom resize icon' }, + position: { x: 0, y: 200 }, + style: { width: 100, height: 60, ...nodeStyle }, + }, + { + id: '3', + type: 'verticalResizer', + data: { label: 'vertical resizer' }, position: { x: 250, y: 200 }, - style: { border: '1px solid #222', fontSize: 10, width: 100 }, + style: { ...nodeStyle }, + }, + { + id: '3a', + type: 'verticalResizer', + data: { + label: 'vertical resizer with min/maxHeight and aspect ratio', + minHeight: 50, + maxHeight: 200, + keepAspectRatio: true, + }, + position: { x: 400, y: 200 }, + style: { ...nodeStyle, height: 50 }, }, { id: '4', - type: 'customResizer2', - data: { label: 'resize controls' }, - position: { x: 100, y: 150 }, - style: { border: '1px solid #222', fontSize: 10 }, + type: 'horizontalResizer', + data: { + label: 'horizontal resizer with aspect ratio', + keepAspectRatio: true, + minHeight: 20, + maxHeight: 80, + maxWidth: 300, + }, + position: { x: 250, y: 300 }, + style: { ...nodeStyle }, }, { - id: '5', - type: 'customResizer2', - data: { label: 'min width and height' }, - position: { x: 250, y: 250 }, - style: { border: '1px solid #222', fontSize: 10 }, - }, - { - id: '6', - type: 'customResizer3', - data: { label: 'resize controls' }, - position: { x: 400, y: 200 }, - style: { border: '1px solid #222', fontSize: 10 }, + id: '4a', + type: 'horizontalResizer', + data: { label: 'horizontal resizer with maxWidth', maxWidth: 300 }, + position: { x: 250, y: 400 }, + style: { ...nodeStyle }, }, ]; diff --git a/examples/vite-app/src/examples/UseOnSelectionChange/index.tsx b/examples/vite-app/src/examples/UseOnSelectionChange/index.tsx index 160ca769..b6f5531f 100644 --- a/examples/vite-app/src/examples/UseOnSelectionChange/index.tsx +++ b/examples/vite-app/src/examples/UseOnSelectionChange/index.tsx @@ -18,6 +18,20 @@ const initialNodes: Node[] = [ data: { label: 'Node 1' }, position: { x: 250, y: 5 }, }, + { + id: '2', + type: 'default', + data: { label: 'Node 2' }, + position: { x: 250, y: 100 }, + }, +]; + +const initialEdges: Edge[] = [ + { + id: 'e1-2', + source: '1', + target: '2', + }, ]; const SelectionLogger = () => { @@ -34,7 +48,7 @@ const SelectionLogger = () => { const Flow = () => { const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); - const [edges, setEdges, onEdgesChange] = useEdgesState([]); + const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); const onConnect = useCallback((params: Edge | Connection) => setEdges((els) => addEdge(params, els)), [setEdges]); return ( @@ -52,6 +66,9 @@ const WrappedFlow = () => ( +
+ +
); diff --git a/examples/vite-app/src/examples/Validation/index.tsx b/examples/vite-app/src/examples/Validation/index.tsx index 1488b48c..c55d8178 100644 --- a/examples/vite-app/src/examples/Validation/index.tsx +++ b/examples/vite-app/src/examples/Validation/index.tsx @@ -12,6 +12,8 @@ import ReactFlow, { OnConnectStart, OnConnectEnd, OnConnect, + updateEdge, + Edge, } from 'reactflow'; import styles from './validation.module.css'; @@ -28,15 +30,15 @@ const isValidConnection = (connection: Connection) => connection.target === 'B'; const CustomInput: FC = () => ( <>
Only connectable with B
- + ); const CustomNode: FC = ({ id }) => ( <> - +
{id}
- + ); @@ -74,6 +76,11 @@ const ValidationFlow = () => { [value] ); + const onEdgeUpdate = useCallback( + (oldEdge: Edge, newConnection: Connection) => setEdges((els) => updateEdge(oldEdge, newConnection, els)), + [setEdges] + ); + return ( { nodeTypes={nodeTypes} onConnectStart={onConnectStart} onConnectEnd={onConnectEnd} + onEdgeUpdate={onEdgeUpdate} + isValidConnection={isValidConnection} fitView /> ); diff --git a/packages/background/CHANGELOG.md b/packages/background/CHANGELOG.md index 4729e9a4..d179fca5 100644 --- a/packages/background/CHANGELOG.md +++ b/packages/background/CHANGELOG.md @@ -1,5 +1,14 @@ # @reactflow/background +## 11.1.9 + +### Patch Changes + +- [#2895](https://github.com/wbkd/react-flow/pull/2895) [`3d5764ca`](https://github.com/wbkd/react-flow/commit/3d5764cac6548984a30cbf85899024e62fd69425) Thanks [@moklick](https://github.com/moklick)! - add data-testid for controls, minimap and background + +- Updated dependencies [[`3d5764ca`](https://github.com/wbkd/react-flow/commit/3d5764cac6548984a30cbf85899024e62fd69425), [`83fc4675`](https://github.com/wbkd/react-flow/commit/83fc467545527729633e817dbccfe59d0040da4b), [`b1190837`](https://github.com/wbkd/react-flow/commit/b11908370bc438ca8d4179497cd4eb1f8c656798), [`5fabd272`](https://github.com/wbkd/react-flow/commit/5fabd2720f6367f75f79a45822d8f675a3b8e1cf), [`8f080bd5`](https://github.com/wbkd/react-flow/commit/8f080bd5e0e7e6c71f51eee9c9f2bc4b25182861), [`b8886514`](https://github.com/wbkd/react-flow/commit/b88865140c72fa7e92a883498768000cb2cc96a7), [`16bf89f2`](https://github.com/wbkd/react-flow/commit/16bf89f2b7bbf8449c00d0e2c07c19c3ff6d2533)]: + - @reactflow/core@11.6.0 + ## 11.1.8 ### Patch Changes diff --git a/packages/background/package.json b/packages/background/package.json index 8dcd3148..5f2a889f 100644 --- a/packages/background/package.json +++ b/packages/background/package.json @@ -1,6 +1,6 @@ { "name": "@reactflow/background", - "version": "11.1.8", + "version": "11.1.9", "description": "Background component with different variants for React Flow", "keywords": [ "react", diff --git a/packages/background/src/Background.tsx b/packages/background/src/Background.tsx index 10702d81..bf8a4336 100644 --- a/packages/background/src/Background.tsx +++ b/packages/background/src/Background.tsx @@ -59,6 +59,7 @@ function Background({ left: 0, }} ref={ref} + data-testid="rf__background" > > = ({ }; return ( - + {showZoom && ( <> true; + export default (EdgeComponent: ComponentType) => { const EdgeWrapper = ({ id, @@ -93,12 +95,14 @@ export default (EdgeComponent: ComponentType) => { return; } + const { edges, isValidConnection: isValidConnectionStore } = store.getState(); const nodeId = isSourceHandle ? target : source; const handleId = (isSourceHandle ? targetHandleId : sourceHandleId) || null; const handleType = isSourceHandle ? 'target' : 'source'; - const isValidConnection = () => true; + const isValidConnection = isValidConnectionStore || alwaysValidConnection; + const isTarget = isSourceHandle; - const edge = store.getState().edges.find((e) => e.id === id)!; + const edge = edges.find((e) => e.id === id)!; setUpdating(true); onEdgeUpdateStart?.(event, edge, handleType); diff --git a/packages/core/src/components/Handle/index.tsx b/packages/core/src/components/Handle/index.tsx index 89d1290b..71ff1ab1 100644 --- a/packages/core/src/components/Handle/index.tsx +++ b/packages/core/src/components/Handle/index.tsx @@ -26,7 +26,7 @@ const Handle = forwardRef( { type = 'source', position = Position.Top, - isValidConnection = alwaysValid, + isValidConnection, isConnectable = true, id, onConnect, @@ -60,8 +60,8 @@ const Handle = forwardRef( ...params, }; if (hasDefaultEdges) { - const { edges } = store.getState(); - store.setState({ edges: addEdge(edgeParams, edges) }); + const { edges, setEdges } = store.getState(); + setEdges(addEdge(edgeParams, edges)); } onConnectAction?.(edgeParams); @@ -80,7 +80,7 @@ const Handle = forwardRef( isTarget, getState: store.getState, setState: store.setState, - isValidConnection, + isValidConnection: isValidConnection || store.getState().isValidConnection || alwaysValid, }); } @@ -92,7 +92,12 @@ const Handle = forwardRef( }; const onClick = (event: ReactMouseEvent) => { - const { onClickConnectStart, onClickConnectEnd, connectionMode } = store.getState(); + const { + onClickConnectStart, + onClickConnectEnd, + connectionMode, + isValidConnection: isValidConnectionStore, + } = store.getState(); if (!connectionStartHandle) { onClickConnectStart?.(event, { nodeId, handleId, handleType: type }); store.setState({ connectionStartHandle: { nodeId, type, handleId } }); @@ -100,6 +105,7 @@ const Handle = forwardRef( } const doc = getHostForElement(event.target as HTMLElement); + const isValidConnectionHandler = isValidConnection || isValidConnectionStore || alwaysValid; const { connection, isValid } = isValidHandle( event.nativeEvent, { @@ -111,7 +117,7 @@ const Handle = forwardRef( connectionStartHandle.nodeId, connectionStartHandle.handleId || null, connectionStartHandle.type, - isValidConnection, + isValidConnectionHandler, doc ); diff --git a/packages/core/src/components/Handle/utils.ts b/packages/core/src/components/Handle/utils.ts index a9cd94eb..06984359 100644 --- a/packages/core/src/components/Handle/utils.ts +++ b/packages/core/src/components/Handle/utils.ts @@ -112,9 +112,10 @@ export function isValidHandle( // in strict mode we don't allow target to target or source to source connections const isValid = - connectionMode === ConnectionMode.Strict + handleToCheck.classList.contains('connectable') && + (connectionMode === ConnectionMode.Strict ? (isTarget && handleType === 'source') || (!isTarget && handleType === 'target') - : handleNodeId !== fromNodeId || handleId !== fromHandleId; + : handleNodeId !== fromNodeId || handleId !== fromHandleId); if (isValid) { result.isValid = isValidConnection(connection); diff --git a/packages/core/src/components/Nodes/utils.ts b/packages/core/src/components/Nodes/utils.ts index 37574ad0..d015916c 100644 --- a/packages/core/src/components/Nodes/utils.ts +++ b/packages/core/src/components/Nodes/utils.ts @@ -1,4 +1,4 @@ -import { MouseEvent } from 'react'; +import { MouseEvent, RefObject } from 'react'; import { StoreApi } from 'zustand'; import { getDimensions } from '@reactflow/utils'; import { Position, type HandleElement, type NodeOrigin } from '@reactflow/system'; @@ -58,6 +58,7 @@ export function handleNodeClick({ id, store, unselect = false, + nodeRef, }: { id: string; store: { @@ -65,6 +66,7 @@ export function handleNodeClick({ setState: StoreApi['setState']; }; unselect?: boolean; + nodeRef?: RefObject; }) { const { addSelectedNodes, unselectNodesAndEdges, multiSelectionActive, nodeInternals } = store.getState(); const node = nodeInternals.get(id)!; @@ -75,5 +77,7 @@ export function handleNodeClick({ addSelectedNodes([id]); } else if (unselect || (node.selected && multiSelectionActive)) { unselectNodesAndEdges({ nodes: [node] }); + + requestAnimationFrame(() => nodeRef?.current?.blur()); } } diff --git a/packages/core/src/components/Nodes/wrapNode.tsx b/packages/core/src/components/Nodes/wrapNode.tsx index cab628ce..1b021170 100644 --- a/packages/core/src/components/Nodes/wrapNode.tsx +++ b/packages/core/src/components/Nodes/wrapNode.tsx @@ -74,6 +74,7 @@ export default (NodeComponent: ComponentType) => { handleNodeClick({ id, store, + nodeRef, }); } @@ -90,13 +91,12 @@ export default (NodeComponent: ComponentType) => { if (elementSelectionKeys.includes(event.key) && isSelectable) { const unselect = event.key === 'Escape'; - if (unselect) { - nodeRef.current?.blur(); - } + handleNodeClick({ id, store, unselect, + nodeRef, }); } else if ( !disableKeyboardA11y && diff --git a/packages/core/src/components/StoreUpdater/index.tsx b/packages/core/src/components/StoreUpdater/index.tsx index f911f614..16dd5904 100644 --- a/packages/core/src/components/StoreUpdater/index.tsx +++ b/packages/core/src/components/StoreUpdater/index.tsx @@ -50,6 +50,7 @@ type StoreUpdaterProps = Pick< | 'autoPanOnNodeDrag' | 'onError' | 'connectionRadius' + | 'isValidConnection' > & { rfId: string }; const selector = (s: ReactFlowState) => ({ @@ -128,6 +129,7 @@ const StoreUpdater = ({ autoPanOnNodeDrag, onError, connectionRadius, + isValidConnection, }: StoreUpdaterProps) => { const { setNodes, @@ -185,6 +187,7 @@ const StoreUpdater = ({ useDirectStoreUpdater('autoPanOnNodeDrag', autoPanOnNodeDrag, store.setState); useDirectStoreUpdater('onError', onError, store.setState); useDirectStoreUpdater('connectionRadius', connectionRadius, store.setState); + useDirectStoreUpdater('isValidConnection', isValidConnection, store.setState); useStoreUpdater(nodes, setNodes); useStoreUpdater(edges, setEdges); diff --git a/packages/core/src/container/EdgeRenderer/utils.ts b/packages/core/src/container/EdgeRenderer/utils.ts index 8907b264..506a7196 100644 --- a/packages/core/src/container/EdgeRenderer/utils.ts +++ b/packages/core/src/container/EdgeRenderer/utils.ts @@ -75,10 +75,10 @@ export function getHandle(bounds: HandleElement[], handleId?: string | null): Ha return null; } - if (handleId) { - return bounds.find((d) => d.id === handleId)!; - } else if (bounds.length === 1) { + if (bounds.length === 1 || !handleId) { return bounds[0]; + } else if (handleId) { + return bounds.find((d) => d.id === handleId) || null; } return null; diff --git a/packages/core/src/container/Pane/index.tsx b/packages/core/src/container/Pane/index.tsx index 7b3de5ab..c89978d5 100644 --- a/packages/core/src/container/Pane/index.tsx +++ b/packages/core/src/container/Pane/index.tsx @@ -111,7 +111,7 @@ const Pane = memo( return; } - const { x, y } = getEventPosition(event, containerBounds.current); + const { x, y } = getEventPosition(event.nativeEvent, containerBounds.current); resetSelectedElements(); @@ -138,7 +138,7 @@ const Pane = memo( store.setState({ userSelectionActive: true, nodesSelectionActive: false }); - const mousePos = getEventPosition(event, containerBounds.current); + const mousePos = getEventPosition(event.nativeEvent, containerBounds.current); const startX = userSelectionRect.startX ?? 0; const startY = userSelectionRect.startY ?? 0; diff --git a/packages/core/src/container/ReactFlow/index.tsx b/packages/core/src/container/ReactFlow/index.tsx index 74c407d2..910a93b9 100644 --- a/packages/core/src/container/ReactFlow/index.tsx +++ b/packages/core/src/container/ReactFlow/index.tsx @@ -167,6 +167,7 @@ const ReactFlow = forwardRef( autoPanOnConnect = true, autoPanOnNodeDrag = true, connectionRadius = 20, + isValidConnection, onError, style, id, @@ -299,6 +300,7 @@ const ReactFlow = forwardRef( autoPanOnNodeDrag={autoPanOnNodeDrag} onError={onError} connectionRadius={connectionRadius} + isValidConnection={isValidConnection} /> {children} diff --git a/packages/core/src/hooks/useDrag/index.ts b/packages/core/src/hooks/useDrag/index.ts index 98ff2758..87eaf75d 100644 --- a/packages/core/src/hooks/useDrag/index.ts +++ b/packages/core/src/hooks/useDrag/index.ts @@ -153,6 +153,7 @@ function useDrag({ handleNodeClick({ id: nodeId, store, + nodeRef: nodeRef as RefObject, }); } diff --git a/packages/core/src/hooks/useKeyPress.ts b/packages/core/src/hooks/useKeyPress.ts index 6209ff11..efddc507 100644 --- a/packages/core/src/hooks/useKeyPress.ts +++ b/packages/core/src/hooks/useKeyPress.ts @@ -18,6 +18,9 @@ const doc = typeof document !== 'undefined' ? document : null; export default (keyCode: KeyCode | null = null, options: UseKeyPressOptions = { target: doc }): boolean => { const [keyPressed, setKeyPressed] = useState(false); + // we need to remember if a modifier key is pressed in order to track it + const modifierPressed = useRef(false); + // we need to remember the pressed keys in order to support combinations const pressedKeys = useRef(new Set([])); @@ -42,7 +45,10 @@ export default (keyCode: KeyCode | null = null, options: UseKeyPressOptions = { useEffect(() => { if (keyCode !== null) { const downHandler = (event: KeyboardEvent) => { - if (isInputDOMNode(event)) { + + modifierPressed.current = event.ctrlKey || event.metaKey || event.shiftKey; + + if (!modifierPressed.current && isInputDOMNode(event)) { return false; } const keyOrCode = useKeyOrCode(event.code, keysToWatch); @@ -55,7 +61,7 @@ export default (keyCode: KeyCode | null = null, options: UseKeyPressOptions = { }; const upHandler = (event: KeyboardEvent) => { - if (isInputDOMNode(event)) { + if (!modifierPressed.current && isInputDOMNode(event)) { return false; } const keyOrCode = useKeyOrCode(event.code, keysToWatch); @@ -66,6 +72,7 @@ export default (keyCode: KeyCode | null = null, options: UseKeyPressOptions = { } else { pressedKeys.current.delete(event[keyOrCode]); } + modifierPressed.current = false; }; const resetHandler = () => { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 055aad42..cf112bf6 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -7,7 +7,6 @@ export { default as BezierEdge } from './components/Edges/BezierEdge'; export { default as SimpleBezierEdge, getSimpleBezierPath } from './components/Edges/SimpleBezierEdge'; export { default as SmoothStepEdge } from './components/Edges/SmoothStepEdge'; export { default as BaseEdge } from './components/Edges/BaseEdge'; - export { default as ReactFlowProvider } from './components/ReactFlowProvider'; export { default as Panel } from './components/Panel'; export { default as EdgeLabelRenderer } from './components/EdgeLabelRenderer'; @@ -34,6 +33,7 @@ export { rectToBox, boxToRect, getBoundsOfRects, + clamp, } from '@reactflow/utils'; export { applyNodeChanges, applyEdgeChanges } from './utils/changes'; diff --git a/packages/core/src/store/index.ts b/packages/core/src/store/index.ts index e305336f..c4e408c7 100644 --- a/packages/core/src/store/index.ts +++ b/packages/core/src/store/index.ts @@ -36,8 +36,8 @@ const createRFStore = () => return Array.from(get().nodeInternals.values()); }, setEdges: (edges: Edge[]) => { - const { defaultEdgeOptions = {} } = get(); - set({ edges: edges.map((e) => ({ ...defaultEdgeOptions, ...e })) }); + const { defaultEdgeOptions = null } = get(); + set({ edges: defaultEdgeOptions ? edges.map((e) => ({ ...defaultEdgeOptions, ...e })) : edges }); }, setDefaultNodesAndEdges: (nodes?: Node[], edges?: Edge[]) => { const hasDefaultNodes = typeof nodes !== 'undefined'; diff --git a/packages/core/src/store/initialState.ts b/packages/core/src/store/initialState.ts index 65550c46..c949aead 100644 --- a/packages/core/src/store/initialState.ts +++ b/packages/core/src/store/initialState.ts @@ -58,6 +58,7 @@ const initialState: ReactFlowStore = { autoPanOnNodeDrag: true, connectionRadius: 20, onError: devWarn, + isValidConnection: undefined, }; export default initialState; diff --git a/packages/core/src/types/component-props.ts b/packages/core/src/types/component-props.ts index 1840f238..bd93282e 100644 --- a/packages/core/src/types/component-props.ts +++ b/packages/core/src/types/component-props.ts @@ -40,6 +40,7 @@ import type { SelectionDragHandler, EdgeMouseHandler, } from '.'; +import { ValidConnectionFunc } from '../components/Handle/utils'; export type ReactFlowProps = HTMLAttributes & { nodes?: Node[]; @@ -146,6 +147,7 @@ export type ReactFlowProps = HTMLAttributes & { autoPanOnConnect?: boolean; connectionRadius?: number; onError?: OnError; + isValidConnection?: ValidConnectionFunc; }; export type ReactFlowRefType = HTMLDivElement; diff --git a/packages/core/src/types/general.ts b/packages/core/src/types/general.ts index aa947c01..ec86e7e8 100644 --- a/packages/core/src/types/general.ts +++ b/packages/core/src/types/general.ts @@ -18,6 +18,7 @@ import { SetCenter, FitBounds, Project, + Connection, } from '@reactflow/system'; import type { NodeChange, EdgeChange, Node, WrapNodeProps, Edge, EdgeProps, WrapEdgeProps, ReactFlowInstance } from '.'; @@ -53,6 +54,8 @@ export type FitViewOptions = FitViewOptionsBase; export type FitView = (fitViewOptions?: FitViewOptions) => boolean; export type OnInit = (reactFlowInstance: ReactFlowInstance) => void; +export type IsValidConnection = (edge: Edge | Connection) => boolean; + export type ViewportHelperFunctions = { zoomIn: ZoomInOut; zoomOut: ZoomInOut; diff --git a/packages/core/src/types/store.ts b/packages/core/src/types/store.ts index b4277137..494b43c5 100644 --- a/packages/core/src/types/store.ts +++ b/packages/core/src/types/store.ts @@ -35,6 +35,7 @@ import type { OnEdgesDelete, OnSelectionChangeFunc, UnselectNodesAndEdgesParams, + IsValidConnection, } from '.'; export type ReactFlowStore = { @@ -123,6 +124,8 @@ export type ReactFlowStore = { autoPanOnConnect: boolean; autoPanOnNodeDrag: boolean; connectionRadius: number; + + isValidConnection?: IsValidConnection; }; export type ReactFlowActions = { diff --git a/packages/minimap/CHANGELOG.md b/packages/minimap/CHANGELOG.md index e0ab8a44..a1a322a9 100644 --- a/packages/minimap/CHANGELOG.md +++ b/packages/minimap/CHANGELOG.md @@ -1,5 +1,18 @@ # @reactflow/minimap +## 11.4.0 + +### Minor Changes + +- [#2906](https://github.com/wbkd/react-flow/pull/2906) [`4a30185a`](https://github.com/wbkd/react-flow/commit/4a30185a12899691ff61259f0db84bc5494cb573) Thanks [@hayleigh-dot-dev](https://github.com/hayleigh-dot-dev)! - Add `nodeComponent` prop for passing custom component for the nodes + +### Patch Changes + +- [#2895](https://github.com/wbkd/react-flow/pull/2895) [`3d5764ca`](https://github.com/wbkd/react-flow/commit/3d5764cac6548984a30cbf85899024e62fd69425) - add data-testid for controls, minimap and background + +- Updated dependencies [[`3d5764ca`](https://github.com/wbkd/react-flow/commit/3d5764cac6548984a30cbf85899024e62fd69425), [`83fc4675`](https://github.com/wbkd/react-flow/commit/83fc467545527729633e817dbccfe59d0040da4b), [`b1190837`](https://github.com/wbkd/react-flow/commit/b11908370bc438ca8d4179497cd4eb1f8c656798), [`5fabd272`](https://github.com/wbkd/react-flow/commit/5fabd2720f6367f75f79a45822d8f675a3b8e1cf), [`8f080bd5`](https://github.com/wbkd/react-flow/commit/8f080bd5e0e7e6c71f51eee9c9f2bc4b25182861), [`b8886514`](https://github.com/wbkd/react-flow/commit/b88865140c72fa7e92a883498768000cb2cc96a7), [`16bf89f2`](https://github.com/wbkd/react-flow/commit/16bf89f2b7bbf8449c00d0e2c07c19c3ff6d2533)]: + - @reactflow/core@11.6.0 + ## 11.3.8 ### Patch Changes diff --git a/packages/minimap/package.json b/packages/minimap/package.json index 66e22ced..81f0ff1c 100644 --- a/packages/minimap/package.json +++ b/packages/minimap/package.json @@ -1,6 +1,6 @@ { "name": "@reactflow/minimap", - "version": "11.3.8", + "version": "11.4.0", "description": "Minimap component for React Flow.", "keywords": [ "react", diff --git a/packages/minimap/src/MiniMap.tsx b/packages/minimap/src/MiniMap.tsx index bb4a2f89..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, @@ -161,7 +164,12 @@ function MiniMap({ : undefined; 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; +} diff --git a/packages/node-resizer/CHANGELOG.md b/packages/node-resizer/CHANGELOG.md index 9d7870b5..b8cfb5cb 100644 --- a/packages/node-resizer/CHANGELOG.md +++ b/packages/node-resizer/CHANGELOG.md @@ -1,5 +1,17 @@ # @reactflow/node-resizer +## 2.1.0 + +### Minor Changes + +- [#2900](https://github.com/wbkd/react-flow/pull/2900) [`b1190837`](https://github.com/wbkd/react-flow/commit/b11908370bc438ca8d4179497cd4eb1f8c656798) Thanks [@stffabi](https://github.com/stffabi)! - add `maxWidth` and `maxHeight` props +- [#2900](https://github.com/wbkd/react-flow/pull/2900) [`b1190837`](https://github.com/wbkd/react-flow/commit/b11908370bc438ca8d4179497cd4eb1f8c656798) - add `keepAspectRatio` prop + +### Patch Changes + +- Updated dependencies [[`3d5764ca`](https://github.com/wbkd/react-flow/commit/3d5764cac6548984a30cbf85899024e62fd69425), [`83fc4675`](https://github.com/wbkd/react-flow/commit/83fc467545527729633e817dbccfe59d0040da4b), [`b1190837`](https://github.com/wbkd/react-flow/commit/b11908370bc438ca8d4179497cd4eb1f8c656798), [`5fabd272`](https://github.com/wbkd/react-flow/commit/5fabd2720f6367f75f79a45822d8f675a3b8e1cf), [`8f080bd5`](https://github.com/wbkd/react-flow/commit/8f080bd5e0e7e6c71f51eee9c9f2bc4b25182861), [`b8886514`](https://github.com/wbkd/react-flow/commit/b88865140c72fa7e92a883498768000cb2cc96a7), [`16bf89f2`](https://github.com/wbkd/react-flow/commit/16bf89f2b7bbf8449c00d0e2c07c19c3ff6d2533)]: + - @reactflow/core@11.6.0 + ## 2.0.1 ### Patch Changes diff --git a/packages/node-resizer/package.json b/packages/node-resizer/package.json index 6bbc73e4..b40e0ead 100644 --- a/packages/node-resizer/package.json +++ b/packages/node-resizer/package.json @@ -1,6 +1,6 @@ { "name": "@reactflow/node-resizer", - "version": "2.0.1", + "version": "2.1.0", "description": "A helper component for resizing nodes.", "keywords": [ "react", @@ -38,7 +38,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@reactflow/core": "workspace:^11.3.3", + "@reactflow/core": "workspace:^11.6.0", "classcat": "^5.0.4", "d3-drag": "^3.0.0", "d3-selection": "^3.0.0", diff --git a/packages/node-resizer/src/NodeResizer.tsx b/packages/node-resizer/src/NodeResizer.tsx index c7a3b8a6..485b8efc 100644 --- a/packages/node-resizer/src/NodeResizer.tsx +++ b/packages/node-resizer/src/NodeResizer.tsx @@ -14,6 +14,9 @@ export default function NodeResizer({ color, minWidth = 10, minHeight = 10, + maxWidth = Number.MAX_VALUE, + maxHeight = Number.MAX_VALUE, + keepAspectRatio = false, shouldResize, onResizeStart, onResize, @@ -36,7 +39,10 @@ export default function NodeResizer({ color={color} minWidth={minWidth} minHeight={minHeight} + maxWidth={maxWidth} + maxHeight={maxHeight} onResizeStart={onResizeStart} + keepAspectRatio={keepAspectRatio} shouldResize={shouldResize} onResize={onResize} onResizeEnd={onResizeEnd} @@ -52,7 +58,10 @@ export default function NodeResizer({ color={color} minWidth={minWidth} minHeight={minHeight} + maxWidth={maxWidth} + maxHeight={maxHeight} onResizeStart={onResizeStart} + keepAspectRatio={keepAspectRatio} shouldResize={shouldResize} onResize={onResize} onResizeEnd={onResizeEnd} diff --git a/packages/node-resizer/src/ResizeControl.tsx b/packages/node-resizer/src/ResizeControl.tsx index 3397537d..6757b2a3 100644 --- a/packages/node-resizer/src/ResizeControl.tsx +++ b/packages/node-resizer/src/ResizeControl.tsx @@ -9,6 +9,7 @@ import { NodeDimensionChange, useNodeId, NodePositionChange, + clamp, } from '@reactflow/core'; import { ResizeDragEvent, ResizeControlProps, ResizeControlLineProps, ResizeControlVariant } from './types'; @@ -20,6 +21,7 @@ const initStartValues = { ...initPrevValues, pointerX: 0, pointerY: 0, + aspectRatio: 1, }; function ResizeControl({ @@ -32,6 +34,9 @@ function ResizeControl({ color, minWidth = 10, minHeight = 10, + maxWidth = Number.MAX_VALUE, + maxHeight = Number.MAX_VALUE, + keepAspectRatio = false, shouldResize, onResizeStart, onResize, @@ -53,6 +58,12 @@ function ResizeControl({ } const selection = select(resizeControlRef.current); + + const enableX = controlPosition.includes('right') || controlPosition.includes('left'); + const enableY = controlPosition.includes('bottom') || controlPosition.includes('top'); + const invertX = controlPosition.includes('left'); + const invertY = controlPosition.includes('top'); + const dragHandler = drag() .on('start', (event: ResizeDragEvent) => { const node = store.getState().nodeInternals.get(id); @@ -69,6 +80,7 @@ function ResizeControl({ ...prevValues.current, pointerX: xSnapped, pointerY: ySnapped, + aspectRatio: prevValues.current.width / prevValues.current.height, }; onResizeStart?.(event, { ...prevValues.current }); @@ -77,10 +89,6 @@ function ResizeControl({ const { nodeInternals, triggerNodeChanges } = store.getState(); const { xSnapped, ySnapped } = getPointerPosition(event); const node = nodeInternals.get(id); - const enableX = controlPosition.includes('right') || controlPosition.includes('left'); - const enableY = controlPosition.includes('bottom') || controlPosition.includes('top'); - const invertX = controlPosition.includes('left'); - const invertY = controlPosition.includes('top'); if (node) { const changes: NodeChange[] = []; @@ -91,13 +99,43 @@ function ResizeControl({ height: startHeight, x: startNodeX, y: startNodeY, + aspectRatio, } = startValues.current; const { x: prevX, y: prevY, width: prevWidth, height: prevHeight } = prevValues.current; + const distX = Math.floor(enableX ? xSnapped - startX : 0); const distY = Math.floor(enableY ? ySnapped - startY : 0); - const width = Math.max(startWidth + (invertX ? -distX : distX), minWidth); - const height = Math.max(startHeight + (invertY ? -distY : distY), minHeight); + + let width = clamp(startWidth + (invertX ? -distX : distX), minWidth, maxWidth); + let height = clamp(startHeight + (invertY ? -distY : distY), minHeight, maxHeight); + + if (keepAspectRatio) { + const nextAspectRatio = width / height; + const isDiagonal = enableX && enableY; + const isHorizontal = enableX && !enableY; + const isVertical = enableY && !enableX; + + width = (nextAspectRatio <= aspectRatio && isDiagonal) || isVertical ? height * aspectRatio : width; + height = (nextAspectRatio > aspectRatio && isDiagonal) || isHorizontal ? width / aspectRatio : height; + + if (width >= maxWidth) { + width = maxWidth; + height = maxWidth / aspectRatio; + } else if (width <= minWidth) { + width = minWidth; + height = minWidth / aspectRatio; + } + + if (height >= maxHeight) { + height = maxHeight; + width = maxHeight * aspectRatio; + } else if (height <= minHeight) { + height = minHeight; + width = minHeight * aspectRatio; + } + } + const isWidthChange = width !== prevWidth; const isHeightChange = height !== prevHeight; @@ -183,7 +221,7 @@ function ResizeControl({ return () => { selection.on('.drag', null); }; - }, [id, controlPosition, minWidth, minHeight, getPointerPosition]); + }, [id, controlPosition, minWidth, minHeight, maxWidth, maxHeight, keepAspectRatio, getPointerPosition]); const positionClassNames = controlPosition.split('-'); const colorStyleProp = variant === ResizeControlVariant.Line ? 'borderColor' : 'backgroundColor'; diff --git a/packages/node-resizer/src/types.ts b/packages/node-resizer/src/types.ts index 75d40ace..ad9114b2 100644 --- a/packages/node-resizer/src/types.ts +++ b/packages/node-resizer/src/types.ts @@ -29,6 +29,9 @@ export type NodeResizerProps = { isVisible?: boolean; minWidth?: number; minHeight?: number; + maxWidth?: number; + maxHeight?: number; + keepAspectRatio?: boolean; shouldResize?: ShouldResize; onResizeStart?: OnResizeStart; onResize?: OnResize; @@ -46,7 +49,17 @@ export enum ResizeControlVariant { export type ResizeControlProps = Pick< NodeResizerProps, - 'nodeId' | 'color' | 'minWidth' | 'minHeight' | 'shouldResize' | 'onResizeStart' | 'onResize' | 'onResizeEnd' + | 'nodeId' + | 'color' + | 'minWidth' + | 'minHeight' + | 'maxWidth' + | 'maxHeight' + | 'keepAspectRatio' + | 'shouldResize' + | 'onResizeStart' + | 'onResize' + | 'onResizeEnd' > & { position?: ControlPosition; variant?: ResizeControlVariant; diff --git a/packages/node-toolbar/CHANGELOG.md b/packages/node-toolbar/CHANGELOG.md index ddd7225e..7fc031e2 100644 --- a/packages/node-toolbar/CHANGELOG.md +++ b/packages/node-toolbar/CHANGELOG.md @@ -1,5 +1,12 @@ # @reactflow/node-toolbar +## 1.1.9 + +### Patch Changes + +- Updated dependencies [[`3d5764ca`](https://github.com/wbkd/react-flow/commit/3d5764cac6548984a30cbf85899024e62fd69425), [`83fc4675`](https://github.com/wbkd/react-flow/commit/83fc467545527729633e817dbccfe59d0040da4b), [`b1190837`](https://github.com/wbkd/react-flow/commit/b11908370bc438ca8d4179497cd4eb1f8c656798), [`5fabd272`](https://github.com/wbkd/react-flow/commit/5fabd2720f6367f75f79a45822d8f675a3b8e1cf), [`8f080bd5`](https://github.com/wbkd/react-flow/commit/8f080bd5e0e7e6c71f51eee9c9f2bc4b25182861), [`b8886514`](https://github.com/wbkd/react-flow/commit/b88865140c72fa7e92a883498768000cb2cc96a7), [`16bf89f2`](https://github.com/wbkd/react-flow/commit/16bf89f2b7bbf8449c00d0e2c07c19c3ff6d2533)]: + - @reactflow/core@11.6.0 + ## 1.1.8 ### Patch Changes diff --git a/packages/node-toolbar/package.json b/packages/node-toolbar/package.json index 8500fda7..35378ec0 100644 --- a/packages/node-toolbar/package.json +++ b/packages/node-toolbar/package.json @@ -1,6 +1,6 @@ { "name": "@reactflow/node-toolbar", - "version": "1.1.8", + "version": "1.1.9", "description": "A toolbar component for React Flow that can be attached to a node.", "keywords": [ "react", diff --git a/packages/reactflow/CHANGELOG.md b/packages/reactflow/CHANGELOG.md index 1a6a38dd..b8d6693f 100644 --- a/packages/reactflow/CHANGELOG.md +++ b/packages/reactflow/CHANGELOG.md @@ -1,5 +1,29 @@ # reactflow +## 11.6.0 + +This release introduces a new `isValidConnection` prop for the ReactFlow component. You no longer need to pass it to all your Handle components but can pass it once. We also added a new option for the `updateEdge` function that allows you to specify if you want to replace an id when updating it. More over the `MiniMap` got a new `nodeComponent` prop to pass a custom component for the mini map nodes. + +### Minor Changes + +- [#2877](https://github.com/wbkd/react-flow/pull/2877) [`b8886514`](https://github.com/wbkd/react-flow/commit/b88865140c72fa7e92a883498768000cb2cc96a7) - add `isValidConnection` prop for ReactFlow component +- [#2847](https://github.com/wbkd/react-flow/pull/2847) [`16bf89f2`](https://github.com/wbkd/react-flow/commit/16bf89f2b7bbf8449c00d0e2c07c19c3ff6d2533) Thanks [@bcakmakoglu](https://github.com/bcakmakoglu)! - Add option to enable/disable replacing edge id when using `updateEdge` +- [#2906](https://github.com/wbkd/react-flow/pull/2906) [`4a30185a`](https://github.com/wbkd/react-flow/commit/4a30185a12899691ff61259f0db84bc5494cb573) Thanks [@hayleigh-dot-dev](https://github.com/hayleigh-dot-dev)! - Minimap: add nodeComponent prop for passing custom component + +### Patch Changes + +- [#2895](https://github.com/wbkd/react-flow/pull/2895) [`3d5764ca`](https://github.com/wbkd/react-flow/commit/3d5764cac6548984a30cbf85899024e62fd69425) - add data-testid for controls, minimap and background +- [#2894](https://github.com/wbkd/react-flow/pull/2894) [`83fc4675`](https://github.com/wbkd/react-flow/commit/83fc467545527729633e817dbccfe59d0040da4b) - fix(nodes): blur when node gets unselected +- [#2892](https://github.com/wbkd/react-flow/pull/2892) [`5fabd272`](https://github.com/wbkd/react-flow/commit/5fabd2720f6367f75f79a45822d8f675a3b8e1cf) Thanks [@danielgek](https://github.com/danielgek) - track modifier keys on useKeypress +- [#2893](https://github.com/wbkd/react-flow/pull/2893) [`8f080bd5`](https://github.com/wbkd/react-flow/commit/8f080bd5e0e7e6c71f51eee9c9f2bc4b25182861) - fix: check if handle is connectable + +- Updated dependencies [[`3d5764ca`](https://github.com/wbkd/react-flow/commit/3d5764cac6548984a30cbf85899024e62fd69425), [`4a30185a`](https://github.com/wbkd/react-flow/commit/4a30185a12899691ff61259f0db84bc5494cb573), [`83fc4675`](https://github.com/wbkd/react-flow/commit/83fc467545527729633e817dbccfe59d0040da4b), [`b1190837`](https://github.com/wbkd/react-flow/commit/b11908370bc438ca8d4179497cd4eb1f8c656798), [`5fabd272`](https://github.com/wbkd/react-flow/commit/5fabd2720f6367f75f79a45822d8f675a3b8e1cf), [`8f080bd5`](https://github.com/wbkd/react-flow/commit/8f080bd5e0e7e6c71f51eee9c9f2bc4b25182861), [`b8886514`](https://github.com/wbkd/react-flow/commit/b88865140c72fa7e92a883498768000cb2cc96a7), [`16bf89f2`](https://github.com/wbkd/react-flow/commit/16bf89f2b7bbf8449c00d0e2c07c19c3ff6d2533)]: + - @reactflow/background@11.1.9 + - @reactflow/controls@11.1.9 + - @reactflow/core@11.6.0 + - @reactflow/minimap@11.4.0 + - @reactflow/node-toolbar@1.1.9 + ## 11.5.6 ### Patch Changes diff --git a/packages/reactflow/package.json b/packages/reactflow/package.json index c2b29c19..2663a4cd 100644 --- a/packages/reactflow/package.json +++ b/packages/reactflow/package.json @@ -1,6 +1,6 @@ { "name": "reactflow", - "version": "11.5.6", + "version": "11.6.0", "description": "A highly customizable React library for building node-based editors and interactive flow charts", "keywords": [ "react", diff --git a/packages/utils/src/graph.ts b/packages/utils/src/graph.ts index 404c3e75..60e7e356 100644 --- a/packages/utils/src/graph.ts +++ b/packages/utils/src/graph.ts @@ -108,11 +108,18 @@ export const addEdgeBase = ( return edges.concat(edge); }; +export type UpdateEdgeOptions = { + shouldReplaceId?: boolean; +}; + export const updateEdgeBase = ( oldEdge: EdgeType, newConnection: Connection, - edges: EdgeType[] + edges: EdgeType[], + options: UpdateEdgeOptions = { shouldReplaceId: true } ): EdgeType[] => { + const { id: oldEdgeId, ...rest } = oldEdge; + if (!newConnection.source || !newConnection.target) { devWarn('006', errorMessages['006']()); @@ -122,22 +129,22 @@ export const updateEdgeBase = ( const foundEdge = edges.find((e) => e.id === oldEdge.id) as EdgeType; if (!foundEdge) { - devWarn('007', errorMessages['007'](oldEdge.id)); + devWarn('007', errorMessages['007'](oldEdgeId)); return edges; } // Remove old edge and create the new edge with parameters of old edge. const edge = { - ...oldEdge, - id: getEdgeId(newConnection), + ...rest, + id: options.shouldReplaceId ? getEdgeId(newConnection) : oldEdgeId, source: newConnection.source, target: newConnection.target, sourceHandle: newConnection.sourceHandle, targetHandle: newConnection.targetHandle, } as EdgeType; - return edges.filter((e) => e.id !== oldEdge.id).concat(edge); + return edges.filter((e) => e.id !== oldEdgeId).concat(edge); }; export const pointToRendererPoint = ( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4f0464fe..b2c91a27 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,8 +32,8 @@ 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.54.1_icdgnfqbkli2ydghsaevjiv5ae - '@typescript-eslint/parser': registry.npmjs.org/@typescript-eslint/parser/5.54.1_id2eilsndvzhjjktb64trvy3gu + '@typescript-eslint/eslint-plugin': registry.npmjs.org/@typescript-eslint/eslint-plugin/5.55.0_yarhzymb3lojuq6uyipzo3xllu + '@typescript-eslint/parser': registry.npmjs.org/@typescript-eslint/parser/5.55.0_id2eilsndvzhjjktb64trvy3gu autoprefixer: registry.npmjs.org/autoprefixer/10.4.9_postcss@8.4.21 concurrently: registry.npmjs.org/concurrently/7.6.0 cypress: registry.npmjs.org/cypress/10.7.0 @@ -244,7 +244,7 @@ importers: packages/node-resizer: specifiers: - '@reactflow/core': workspace:^11.3.3 + '@reactflow/core': workspace:^11.6.0 '@reactflow/eslint-config': workspace:* '@reactflow/rollup-config': workspace:* '@reactflow/tsconfig': workspace:* @@ -1723,6 +1723,26 @@ packages: dev: true optional: true + registry.npmjs.org/@eslint-community/eslint-utils/4.2.0_eslint@8.23.1: + resolution: {integrity: sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.2.0.tgz} + id: registry.npmjs.org/@eslint-community/eslint-utils/4.2.0 + name: '@eslint-community/eslint-utils' + version: 4.2.0 + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: registry.npmjs.org/eslint/8.23.1 + eslint-visitor-keys: registry.npmjs.org/eslint-visitor-keys/3.3.0 + dev: true + + registry.npmjs.org/@eslint-community/regexpp/4.4.0: + resolution: {integrity: sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.4.0.tgz} + name: '@eslint-community/regexpp' + version: 4.4.0 + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + registry.npmjs.org/@eslint/eslintrc/1.3.2: resolution: {integrity: sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.2.tgz} name: '@eslint/eslintrc' @@ -2754,11 +2774,11 @@ packages: - supports-color dev: true - registry.npmjs.org/@typescript-eslint/eslint-plugin/5.54.1_icdgnfqbkli2ydghsaevjiv5ae: - resolution: {integrity: sha512-a2RQAkosH3d3ZIV08s3DcL/mcGc2M/UC528VkPULFxR9VnVPT8pBu0IyBAJJmVsCmhVfwQX1v6q+QGnmSe1bew==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.54.1.tgz} - id: registry.npmjs.org/@typescript-eslint/eslint-plugin/5.54.1 + registry.npmjs.org/@typescript-eslint/eslint-plugin/5.55.0_yarhzymb3lojuq6uyipzo3xllu: + resolution: {integrity: sha512-IZGc50rtbjk+xp5YQoJvmMPmJEYoC53SiKPXyqWfv15XoD2Y5Kju6zN0DwlmaGJp1Iw33JsWJcQ7nw0lGCGjVg==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.55.0.tgz} + id: registry.npmjs.org/@typescript-eslint/eslint-plugin/5.55.0 name: '@typescript-eslint/eslint-plugin' - version: 5.54.1 + version: 5.55.0 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: '@typescript-eslint/parser': ^5.0.0 @@ -2768,16 +2788,16 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': registry.npmjs.org/@typescript-eslint/parser/5.54.1_id2eilsndvzhjjktb64trvy3gu - '@typescript-eslint/scope-manager': registry.npmjs.org/@typescript-eslint/scope-manager/5.54.1 - '@typescript-eslint/type-utils': registry.npmjs.org/@typescript-eslint/type-utils/5.54.1_id2eilsndvzhjjktb64trvy3gu - '@typescript-eslint/utils': registry.npmjs.org/@typescript-eslint/utils/5.54.1_id2eilsndvzhjjktb64trvy3gu + '@eslint-community/regexpp': registry.npmjs.org/@eslint-community/regexpp/4.4.0 + '@typescript-eslint/parser': registry.npmjs.org/@typescript-eslint/parser/5.55.0_id2eilsndvzhjjktb64trvy3gu + '@typescript-eslint/scope-manager': registry.npmjs.org/@typescript-eslint/scope-manager/5.55.0 + '@typescript-eslint/type-utils': registry.npmjs.org/@typescript-eslint/type-utils/5.55.0_id2eilsndvzhjjktb64trvy3gu + '@typescript-eslint/utils': registry.npmjs.org/@typescript-eslint/utils/5.55.0_id2eilsndvzhjjktb64trvy3gu debug: registry.npmjs.org/debug/4.3.4 eslint: registry.npmjs.org/eslint/8.23.1 grapheme-splitter: registry.npmjs.org/grapheme-splitter/1.0.4 ignore: registry.npmjs.org/ignore/5.2.4 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.9.4 typescript: registry.npmjs.org/typescript/4.9.4 @@ -2808,11 +2828,11 @@ packages: - supports-color dev: true - registry.npmjs.org/@typescript-eslint/parser/5.54.1_id2eilsndvzhjjktb64trvy3gu: - resolution: {integrity: sha512-8zaIXJp/nG9Ff9vQNh7TI+C3nA6q6iIsGJ4B4L6MhZ7mHnTMR4YP5vp2xydmFXIy8rpyIVbNAG44871LMt6ujg==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.54.1.tgz} - id: registry.npmjs.org/@typescript-eslint/parser/5.54.1 + registry.npmjs.org/@typescript-eslint/parser/5.55.0_id2eilsndvzhjjktb64trvy3gu: + resolution: {integrity: sha512-ppvmeF7hvdhUUZWSd2EEWfzcFkjJzgNQzVST22nzg958CR+sphy8A6K7LXQZd6V75m1VKjp+J4g/PCEfSCmzhw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.55.0.tgz} + id: registry.npmjs.org/@typescript-eslint/parser/5.55.0 name: '@typescript-eslint/parser' - version: 5.54.1 + version: 5.55.0 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -2821,9 +2841,9 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': registry.npmjs.org/@typescript-eslint/scope-manager/5.54.1 - '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.54.1 - '@typescript-eslint/typescript-estree': registry.npmjs.org/@typescript-eslint/typescript-estree/5.54.1_typescript@4.9.4 + '@typescript-eslint/scope-manager': registry.npmjs.org/@typescript-eslint/scope-manager/5.55.0 + '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.55.0 + '@typescript-eslint/typescript-estree': registry.npmjs.org/@typescript-eslint/typescript-estree/5.55.0_typescript@4.9.4 debug: registry.npmjs.org/debug/4.3.4 eslint: registry.npmjs.org/eslint/8.23.1 typescript: registry.npmjs.org/typescript/4.9.4 @@ -2841,14 +2861,14 @@ packages: '@typescript-eslint/visitor-keys': registry.npmjs.org/@typescript-eslint/visitor-keys/5.49.0 dev: true - registry.npmjs.org/@typescript-eslint/scope-manager/5.54.1: - resolution: {integrity: sha512-zWKuGliXxvuxyM71UA/EcPxaviw39dB2504LqAmFDjmkpO8qNLHcmzlh6pbHs1h/7YQ9bnsO8CCcYCSA8sykUg==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.54.1.tgz} + registry.npmjs.org/@typescript-eslint/scope-manager/5.55.0: + resolution: {integrity: sha512-OK+cIO1ZGhJYNCL//a3ROpsd83psf4dUJ4j7pdNVzd5DmIk+ffkuUIX2vcZQbEW/IR41DYsfJTB19tpCboxQuw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.55.0.tgz} name: '@typescript-eslint/scope-manager' - version: 5.54.1 + version: 5.55.0 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.54.1 - '@typescript-eslint/visitor-keys': registry.npmjs.org/@typescript-eslint/visitor-keys/5.54.1 + '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.55.0 + '@typescript-eslint/visitor-keys': registry.npmjs.org/@typescript-eslint/visitor-keys/5.55.0 dev: true registry.npmjs.org/@typescript-eslint/type-utils/5.49.0_zkdaqh7it7uc4cvz2haft7rc6u: @@ -2874,11 +2894,11 @@ packages: - supports-color dev: true - registry.npmjs.org/@typescript-eslint/type-utils/5.54.1_id2eilsndvzhjjktb64trvy3gu: - resolution: {integrity: sha512-WREHsTz0GqVYLIbzIZYbmUUr95DKEKIXZNH57W3s+4bVnuF1TKe2jH8ZNH8rO1CeMY3U4j4UQeqPNkHMiGem3g==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.54.1.tgz} - id: registry.npmjs.org/@typescript-eslint/type-utils/5.54.1 + registry.npmjs.org/@typescript-eslint/type-utils/5.55.0_id2eilsndvzhjjktb64trvy3gu: + resolution: {integrity: sha512-ObqxBgHIXj8rBNm0yh8oORFrICcJuZPZTqtAFh0oZQyr5DnAHZWfyw54RwpEEH+fD8suZaI0YxvWu5tYE/WswA==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.55.0.tgz} + id: registry.npmjs.org/@typescript-eslint/type-utils/5.55.0 name: '@typescript-eslint/type-utils' - version: 5.54.1 + version: 5.55.0 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: '*' @@ -2887,8 +2907,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': registry.npmjs.org/@typescript-eslint/typescript-estree/5.54.1_typescript@4.9.4 - '@typescript-eslint/utils': registry.npmjs.org/@typescript-eslint/utils/5.54.1_id2eilsndvzhjjktb64trvy3gu + '@typescript-eslint/typescript-estree': registry.npmjs.org/@typescript-eslint/typescript-estree/5.55.0_typescript@4.9.4 + '@typescript-eslint/utils': registry.npmjs.org/@typescript-eslint/utils/5.55.0_id2eilsndvzhjjktb64trvy3gu 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.9.4 @@ -2904,10 +2924,10 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - registry.npmjs.org/@typescript-eslint/types/5.54.1: - resolution: {integrity: sha512-G9+1vVazrfAfbtmCapJX8jRo2E4MDXxgm/IMOF4oGh3kq7XuK3JRkOg6y2Qu1VsTRmWETyTkWt1wxy7X7/yLkw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/types/-/types-5.54.1.tgz} + registry.npmjs.org/@typescript-eslint/types/5.55.0: + resolution: {integrity: sha512-M4iRh4AG1ChrOL6Y+mETEKGeDnT7Sparn6fhZ5LtVJF1909D5O4uqK+C5NPbLmpfZ0XIIxCdwzKiijpZUOvOug==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/types/-/types-5.55.0.tgz} name: '@typescript-eslint/types' - version: 5.54.1 + version: 5.55.0 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true @@ -2935,11 +2955,11 @@ packages: - supports-color dev: true - registry.npmjs.org/@typescript-eslint/typescript-estree/5.54.1_typescript@4.9.4: - resolution: {integrity: sha512-bjK5t+S6ffHnVwA0qRPTZrxKSaFYocwFIkZx5k7pvWfsB1I57pO/0M0Skatzzw1sCkjJ83AfGTL0oFIFiDX3bg==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.54.1.tgz} - id: registry.npmjs.org/@typescript-eslint/typescript-estree/5.54.1 + registry.npmjs.org/@typescript-eslint/typescript-estree/5.55.0_typescript@4.9.4: + resolution: {integrity: sha512-I7X4A9ovA8gdpWMpr7b1BN9eEbvlEtWhQvpxp/yogt48fy9Lj3iE3ild/1H3jKBBIYj5YYJmS2+9ystVhC7eaQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.55.0.tgz} + id: registry.npmjs.org/@typescript-eslint/typescript-estree/5.55.0 name: '@typescript-eslint/typescript-estree' - version: 5.54.1 + version: 5.55.0 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: typescript: '*' @@ -2947,8 +2967,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.54.1 - '@typescript-eslint/visitor-keys': registry.npmjs.org/@typescript-eslint/visitor-keys/5.54.1 + '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.55.0 + '@typescript-eslint/visitor-keys': registry.npmjs.org/@typescript-eslint/visitor-keys/5.55.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 @@ -2982,23 +3002,23 @@ packages: - typescript dev: true - registry.npmjs.org/@typescript-eslint/utils/5.54.1_id2eilsndvzhjjktb64trvy3gu: - resolution: {integrity: sha512-IY5dyQM8XD1zfDe5X8jegX6r2EVU5o/WJnLu/znLPWCBF7KNGC+adacXnt5jEYS9JixDcoccI6CvE4RCjHMzCQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.54.1.tgz} - id: registry.npmjs.org/@typescript-eslint/utils/5.54.1 + registry.npmjs.org/@typescript-eslint/utils/5.55.0_id2eilsndvzhjjktb64trvy3gu: + resolution: {integrity: sha512-FkW+i2pQKcpDC3AY6DU54yl8Lfl14FVGYDgBTyGKB75cCwV3KpkpTMFi9d9j2WAJ4271LR2HeC5SEWF/CZmmfw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.55.0.tgz} + id: registry.npmjs.org/@typescript-eslint/utils/5.55.0 name: '@typescript-eslint/utils' - version: 5.54.1 + version: 5.55.0 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: + '@eslint-community/eslint-utils': registry.npmjs.org/@eslint-community/eslint-utils/4.2.0_eslint@8.23.1 '@types/json-schema': registry.npmjs.org/@types/json-schema/7.0.11 '@types/semver': registry.npmjs.org/@types/semver/7.3.13 - '@typescript-eslint/scope-manager': registry.npmjs.org/@typescript-eslint/scope-manager/5.54.1 - '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.54.1 - '@typescript-eslint/typescript-estree': registry.npmjs.org/@typescript-eslint/typescript-estree/5.54.1_typescript@4.9.4 + '@typescript-eslint/scope-manager': registry.npmjs.org/@typescript-eslint/scope-manager/5.55.0 + '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.55.0 + '@typescript-eslint/typescript-estree': registry.npmjs.org/@typescript-eslint/typescript-estree/5.55.0_typescript@4.9.4 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 @@ -3015,13 +3035,13 @@ packages: eslint-visitor-keys: registry.npmjs.org/eslint-visitor-keys/3.3.0 dev: true - registry.npmjs.org/@typescript-eslint/visitor-keys/5.54.1: - resolution: {integrity: sha512-q8iSoHTgwCfgcRJ2l2x+xCbu8nBlRAlsQ33k24Adj8eoVBE0f8dUeI+bAa8F84Mv05UGbAx57g2zrRsYIooqQg==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.54.1.tgz} + registry.npmjs.org/@typescript-eslint/visitor-keys/5.55.0: + resolution: {integrity: sha512-q2dlHHwWgirKh1D3acnuApXG+VNXpEY5/AwRxDVuEQpxWaB0jCDe0jFMVMALJ3ebSfuOVE8/rMS+9ZOYGg1GWw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.55.0.tgz} name: '@typescript-eslint/visitor-keys' - version: 5.54.1 + version: 5.55.0 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.54.1 + '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.55.0 eslint-visitor-keys: registry.npmjs.org/eslint-visitor-keys/3.3.0 dev: true