From 52816debbaf4074618a9281323f1b8b97858e37b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=B6ller?= Date: Mon, 7 Nov 2022 13:55:20 +0100 Subject: [PATCH 01/31] feat(minimap): add prop to configure the minimap title --- packages/minimap/src/MiniMap.tsx | 4 ++-- packages/minimap/src/types.ts | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/minimap/src/MiniMap.tsx b/packages/minimap/src/MiniMap.tsx index f2487f18..a6212672 100644 --- a/packages/minimap/src/MiniMap.tsx +++ b/packages/minimap/src/MiniMap.tsx @@ -38,7 +38,6 @@ const selector = (s: ReactFlowState) => { const getAttrFunction = (func: any): GetMiniMapNodeAttribute => (func instanceof Function ? func : () => func); const ARIA_LABEL_KEY = 'react-flow__minimap-desc'; - function MiniMap({ style, className, @@ -53,6 +52,7 @@ function MiniMap({ onNodeClick, pannable = false, zoomable = false, + ariaLabel = 'React Flow mini map', }: MiniMapProps) { const store = useStoreApi(); const svg = useRef(null); @@ -155,7 +155,7 @@ function MiniMap({ ref={svg} onClick={onSvgClick} > - React Flow mini map + {ariaLabel} {nodes.map((node) => { return ( = Omit, ' onNodeClick?: (event: MouseEvent, node: Node) => void; pannable?: boolean; zoomable?: boolean; + ariaLabel?: string; }; From a9e7411d6daa8562a8e70d2acb9dc6b291d9c6f6 Mon Sep 17 00:00:00 2001 From: GeoffreyLiu Date: Tue, 8 Nov 2022 13:56:05 +0800 Subject: [PATCH 02/31] test: add the test case --- .../reactflow/multiple-instance.cy.tsx | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 examples/vite-app/cypress/components/reactflow/multiple-instance.cy.tsx diff --git a/examples/vite-app/cypress/components/reactflow/multiple-instance.cy.tsx b/examples/vite-app/cypress/components/reactflow/multiple-instance.cy.tsx new file mode 100644 index 00000000..eacf8d4f --- /dev/null +++ b/examples/vite-app/cypress/components/reactflow/multiple-instance.cy.tsx @@ -0,0 +1,61 @@ +import ReactFlow, { BaseEdge, EdgeLabelRenderer, EdgeProps, getSmoothStepPath, ReactFlowProvider } from 'reactflow'; +import * as simpleflow from '../../fixtures/simpleflow'; + +function CustomEdge(props: EdgeProps) { + const [path, labelX, labelY] = getSmoothStepPath(props); + return ( + <> + + +
{props.id}
+
+ + ); +} + +const simpleflow1 = { ...simpleflow }; +simpleflow1.edges = [...simpleflow1.edges]; +simpleflow1.edges[0] = { ...simpleflow1.edges[0], id: 'edge1' }; + +const simpleflow2 = { ...simpleflow }; +simpleflow2.edges = [...simpleflow2.edges]; +simpleflow2.edges[0] = { ...simpleflow2.edges[0], id: 'edge2' }; + +describe(': Multiple Instances', () => { + describe('render EdgeLabelRenderer', () => { + beforeEach(() => { + cy.mount( + <> + + + + + + + + ); + }); + + it('Each ReactFlow instance has its EdgeLabelRenderer DOM element without conflict', () => { + cy.get('#edgelabel-portal-reactflow-a').should('have.length', 1); + cy.get('#edgelabel-portal-reactflow-b').should('have.length', 1); + }); + + it('Each ReactFlow instance has one edge label in EdgeLabelRenderer', () => { + cy.get('#edgelabel-portal-reactflow-a .label').should('have.length', 1); + cy.get('#edgelabel-portal-reactflow-a .label').should('contain.text', 'edge1'); + cy.get('#edgelabel-portal-reactflow-b .label').should('have.length', 1); + cy.get('#edgelabel-portal-reactflow-b .label').should('contain.text', 'edge2'); + }); + }); +}); From 55cf7560c0713cf4b499e142ed9fa2b96f440af4 Mon Sep 17 00:00:00 2001 From: GeoffreyLiu Date: Tue, 8 Nov 2022 13:53:48 +0800 Subject: [PATCH 03/31] fix: access the edge-label-renderer by reactflow's id --- packages/core/src/components/EdgeLabelRenderer/index.tsx | 6 +++++- packages/core/src/container/GraphView/index.tsx | 3 ++- packages/core/src/utils/graph.ts | 4 ++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/core/src/components/EdgeLabelRenderer/index.tsx b/packages/core/src/components/EdgeLabelRenderer/index.tsx index 0647364a..772a8372 100644 --- a/packages/core/src/components/EdgeLabelRenderer/index.tsx +++ b/packages/core/src/components/EdgeLabelRenderer/index.tsx @@ -1,9 +1,13 @@ import { useRef } from 'react'; import type { ReactNode } from 'react'; import { createPortal } from 'react-dom'; +import { useStoreApi } from '../../hooks/useStore'; +import { getEdgeLabelRendererId } from '../../utils/graph'; function EdgeLabelRenderer({ children }: { children: ReactNode }) { - const wrapperRef = useRef(document.getElementById('edgelabel-portal')); + const store = useStoreApi() + const state = store.getState() + const wrapperRef = useRef(document.getElementById(getEdgeLabelRendererId(state.rfId))); if (!wrapperRef.current) { return null; diff --git a/packages/core/src/container/GraphView/index.tsx b/packages/core/src/container/GraphView/index.tsx index 4190bed0..47be5bfc 100644 --- a/packages/core/src/container/GraphView/index.tsx +++ b/packages/core/src/container/GraphView/index.tsx @@ -6,6 +6,7 @@ import EdgeRenderer from '../EdgeRenderer'; import ViewportWrapper from '../Viewport'; import useOnInitHandler from '../../hooks/useOnInitHandler'; import type { EdgeTypesWrapped, NodeTypesWrapped, ReactFlowProps } from '../../types'; +import { getEdgeLabelRendererId } from '../../utils/graph'; export type GraphViewProps = Omit & Required< @@ -158,7 +159,7 @@ const GraphView = ({ disableKeyboardA11y={disableKeyboardA11y} rfId={rfId} /> -
+
, duration = 0) => { return selection.transition().duration(duration); }; + +export function getEdgeLabelRendererId(id: string) { + return `edgelabel-portal-${id}` +} From 3170f875e543d81c90ebe4eb2a3ff9dda65340d7 Mon Sep 17 00:00:00 2001 From: GeoffreyLiu Date: Fri, 11 Nov 2022 09:31:53 +0800 Subject: [PATCH 04/31] refactor: access edge-label-renderer by storing the root element --- .../components/EdgeLabelRenderer/index.tsx | 21 ++++++++++++------- .../src/components/StoreUpdater/index.tsx | 14 +++++++++++-- packages/core/src/constants/component.ts | 1 + .../core/src/container/GraphView/index.tsx | 4 ++-- .../core/src/container/ReactFlow/index.tsx | 20 ++++++++++++++++-- packages/core/src/store/initialState.ts | 1 + packages/core/src/types/general.ts | 5 ++++- packages/core/src/utils/graph.ts | 3 --- 8 files changed, 52 insertions(+), 17 deletions(-) create mode 100644 packages/core/src/constants/component.ts diff --git a/packages/core/src/components/EdgeLabelRenderer/index.tsx b/packages/core/src/components/EdgeLabelRenderer/index.tsx index 772a8372..815916f0 100644 --- a/packages/core/src/components/EdgeLabelRenderer/index.tsx +++ b/packages/core/src/components/EdgeLabelRenderer/index.tsx @@ -1,19 +1,26 @@ -import { useRef } from 'react'; import type { ReactNode } from 'react'; import { createPortal } from 'react-dom'; import { useStoreApi } from '../../hooks/useStore'; -import { getEdgeLabelRendererId } from '../../utils/graph'; +import { EDGE_LABEL_RENDERER_MAIN_CLASS } from '../../constants/component'; function EdgeLabelRenderer({ children }: { children: ReactNode }) { - const store = useStoreApi() - const state = store.getState() - const wrapperRef = useRef(document.getElementById(getEdgeLabelRendererId(state.rfId))); + const store = useStoreApi(); + const { + rootElementRef: { current: rootElement }, + } = store.getState(); - if (!wrapperRef.current) { + if (!rootElement) { return null; } - return createPortal(children, wrapperRef.current); + const collection = rootElement.getElementsByClassName(EDGE_LABEL_RENDERER_MAIN_CLASS); + + const edgeLabelRendererElement = collection.item(0); + if (!edgeLabelRendererElement) { + return null; + } + + return createPortal(children, edgeLabelRendererElement); } export default EdgeLabelRenderer; diff --git a/packages/core/src/components/StoreUpdater/index.tsx b/packages/core/src/components/StoreUpdater/index.tsx index 2f98a453..bb3f89ca 100644 --- a/packages/core/src/components/StoreUpdater/index.tsx +++ b/packages/core/src/components/StoreUpdater/index.tsx @@ -3,7 +3,15 @@ import { StoreApi } from 'zustand'; import shallow from 'zustand/shallow'; import { useStore, useStoreApi } from '../../hooks/useStore'; -import type { Node, Edge, ReactFlowState, CoordinateExtent, ReactFlowProps, ReactFlowStore } from '../../types'; +import type { + Node, + Edge, + ReactFlowState, + CoordinateExtent, + ReactFlowProps, + ReactFlowStore, + RootElementRef, +} from '../../types'; type StoreUpdaterProps = Pick< ReactFlowProps, @@ -44,7 +52,7 @@ type StoreUpdaterProps = Pick< | 'onSelectionDragStop' | 'noPanClassName' | 'nodeOrigin' -> & { rfId: string }; +> & { rfId: string; rootElementRef: RootElementRef }; const selector = (s: ReactFlowState) => ({ setNodes: s.setNodes, @@ -117,6 +125,7 @@ const StoreUpdater = ({ noPanClassName, nodeOrigin, rfId, + rootElementRef, }: StoreUpdaterProps) => { const { setNodes, @@ -169,6 +178,7 @@ const StoreUpdater = ({ useDirectStoreUpdater('noPanClassName', noPanClassName, store.setState); useDirectStoreUpdater('nodeOrigin', nodeOrigin, store.setState); useDirectStoreUpdater('rfId', rfId, store.setState); + useDirectStoreUpdater('rootElementRef', rootElementRef, store.setState); useStoreUpdater(nodes, setNodes); useStoreUpdater(edges, setEdges); diff --git a/packages/core/src/constants/component.ts b/packages/core/src/constants/component.ts new file mode 100644 index 00000000..abb2f46b --- /dev/null +++ b/packages/core/src/constants/component.ts @@ -0,0 +1 @@ +export const EDGE_LABEL_RENDERER_MAIN_CLASS = 'react-flow__edgelabel-renderer' diff --git a/packages/core/src/container/GraphView/index.tsx b/packages/core/src/container/GraphView/index.tsx index 47be5bfc..98f9bede 100644 --- a/packages/core/src/container/GraphView/index.tsx +++ b/packages/core/src/container/GraphView/index.tsx @@ -6,7 +6,7 @@ import EdgeRenderer from '../EdgeRenderer'; import ViewportWrapper from '../Viewport'; import useOnInitHandler from '../../hooks/useOnInitHandler'; import type { EdgeTypesWrapped, NodeTypesWrapped, ReactFlowProps } from '../../types'; -import { getEdgeLabelRendererId } from '../../utils/graph'; +import { EDGE_LABEL_RENDERER_MAIN_CLASS } from '../../constants/component'; export type GraphViewProps = Omit & Required< @@ -159,7 +159,7 @@ const GraphView = ({ disableKeyboardA11y={disableKeyboardA11y} rfId={rfId} /> -
+
( const edgeTypesWrapped = useNodeOrEdgeTypes(edgeTypes, createEdgeTypes) as EdgeTypesWrapped; const rfId = id || '1'; + const rootElementRef = useRef(null); + + function setRootRef(element: HTMLDivElement | null) { + rootElementRef.current = element; + + if (typeof ref === 'function') { + ref(element); + return; + } + + if (ref) { + ref.current = element; + } + } + return (
( noPanClassName={noPanClassName} nodeOrigin={nodeOrigin} rfId={rfId} + rootElementRef={rootElementRef} /> {children} diff --git a/packages/core/src/store/initialState.ts b/packages/core/src/store/initialState.ts index ab918e65..a0353868 100644 --- a/packages/core/src/store/initialState.ts +++ b/packages/core/src/store/initialState.ts @@ -54,6 +54,7 @@ const initialState: ReactFlowStore = { connectOnClick: true, ariaLiveMessage: '', + rootElementRef: { current: null } }; export default initialState; diff --git a/packages/core/src/types/general.ts b/packages/core/src/types/general.ts index b18f4d32..48222f49 100644 --- a/packages/core/src/types/general.ts +++ b/packages/core/src/types/general.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import type { MouseEvent as ReactMouseEvent, ComponentType, MemoExoticComponent } from 'react'; +import type { MouseEvent as ReactMouseEvent, ComponentType, MemoExoticComponent, MutableRefObject } from 'react'; import type { Selection as D3Selection, ZoomBehavior } from 'd3'; import type { XYPosition, Rect, Transform, CoordinateExtent } from './utils'; @@ -129,6 +129,8 @@ export type ViewportHelperFunctions = { viewportInitialized: boolean; }; +export type RootElementRef = MutableRefObject + export type ReactFlowStore = { rfId: string; width: number; @@ -208,6 +210,7 @@ export type ReactFlowStore = { onSelectionChange?: OnSelectionChangeFunc; ariaLiveMessage: string; + rootElementRef: RootElementRef }; export type ReactFlowActions = { diff --git a/packages/core/src/utils/graph.ts b/packages/core/src/utils/graph.ts index 95feec38..5e7e776b 100644 --- a/packages/core/src/utils/graph.ts +++ b/packages/core/src/utils/graph.ts @@ -224,6 +224,3 @@ export const getD3Transition = (selection: D3Selection Date: Fri, 11 Nov 2022 10:38:48 +0800 Subject: [PATCH 05/31] test: update the test cases --- .../components/reactflow/basic-props.cy.tsx | 29 +++++++++++++++++++ .../reactflow/multiple-instance.cy.tsx | 24 ++++++++------- .../vite-app/cypress/support/RefReactFlow.tsx | 12 ++++++++ 3 files changed, 54 insertions(+), 11 deletions(-) create mode 100644 examples/vite-app/cypress/support/RefReactFlow.tsx diff --git a/examples/vite-app/cypress/components/reactflow/basic-props.cy.tsx b/examples/vite-app/cypress/components/reactflow/basic-props.cy.tsx index d777531e..8738436e 100644 --- a/examples/vite-app/cypress/components/reactflow/basic-props.cy.tsx +++ b/examples/vite-app/cypress/components/reactflow/basic-props.cy.tsx @@ -2,6 +2,7 @@ import ReactFlow, { EdgeProps } from 'reactflow'; import ControlledFlow from '../../support/ControlledFlow'; import * as simpleflow from '../../fixtures/simpleflow'; +import { RefReactFlow } from '../../support/RefReactFlow'; describe(': Basic Props', () => { describe('uses defaultNodes and defaultEdges', () => { @@ -194,4 +195,32 @@ describe(': Basic Props', () => { cy.mount(); cy.get('.react-flow').should('have.class', 'custom'); }); + + it('uses function ref', () => { + let pResolve: ((element: HTMLDivElement | PromiseLike | null) => void) | null = null; + const promise: Promise = new Promise((resolve) => { + pResolve = resolve; + }); + + function ref(root: HTMLDivElement | null) { + pResolve && pResolve(root); + } + + cy.mount(); + cy.wrap(promise).should('be.instanceOf', HTMLDivElement); + }); + + it('use mutable ref', () => { + let pResolve: ((element: HTMLDivElement | PromiseLike | null) => void) | null = null; + const promise: Promise = new Promise((resolve) => { + pResolve = resolve; + }); + + function onGetRef(root: HTMLDivElement | null) { + pResolve && pResolve(root); + } + + cy.mount(); + cy.wrap(promise).should('be.instanceOf', HTMLDivElement); + }); }); diff --git a/examples/vite-app/cypress/components/reactflow/multiple-instance.cy.tsx b/examples/vite-app/cypress/components/reactflow/multiple-instance.cy.tsx index eacf8d4f..b1b354a0 100644 --- a/examples/vite-app/cypress/components/reactflow/multiple-instance.cy.tsx +++ b/examples/vite-app/cypress/components/reactflow/multiple-instance.cy.tsx @@ -31,7 +31,6 @@ describe(': Multiple Instances', () => { defaultNodes={simpleflow1.nodes} edgeTypes={{ default: CustomEdge }} defaultEdges={simpleflow1.edges} - id="reactflow-a" /> @@ -39,23 +38,26 @@ describe(': Multiple Instances', () => { defaultNodes={simpleflow2.nodes} edgeTypes={{ default: CustomEdge }} defaultEdges={simpleflow2.edges} - id="reactflow-b" /> ); }); - it('Each ReactFlow instance has its EdgeLabelRenderer DOM element without conflict', () => { - cy.get('#edgelabel-portal-reactflow-a').should('have.length', 1); - cy.get('#edgelabel-portal-reactflow-b').should('have.length', 1); - }); - it('Each ReactFlow instance has one edge label in EdgeLabelRenderer', () => { - cy.get('#edgelabel-portal-reactflow-a .label').should('have.length', 1); - cy.get('#edgelabel-portal-reactflow-a .label').should('contain.text', 'edge1'); - cy.get('#edgelabel-portal-reactflow-b .label').should('have.length', 1); - cy.get('#edgelabel-portal-reactflow-b .label').should('contain.text', 'edge2'); + cy.get('.react-flow__edgelabel-renderer').should('have.length', 2); + + cy.get('.react-flow__edgelabel-renderer') + .eq(0) + .within(() => { + cy.get('.label').should('have.length', 1).should('contain.text', 'edge1'); + }); + + cy.get('.react-flow__edgelabel-renderer') + .eq(1) + .within(() => { + cy.get('.label').should('have.length', 1).should('contain.text', 'edge2'); + }); }); }); }); diff --git a/examples/vite-app/cypress/support/RefReactFlow.tsx b/examples/vite-app/cypress/support/RefReactFlow.tsx new file mode 100644 index 00000000..baec9696 --- /dev/null +++ b/examples/vite-app/cypress/support/RefReactFlow.tsx @@ -0,0 +1,12 @@ +import { useEffect, useRef } from 'react'; +import ReactFlow from 'reactflow'; + +export function RefReactFlow({ onGetRef }: { onGetRef: (element: HTMLDivElement | null) => void }) { + const ref = useRef(null); + + useEffect(() => { + onGetRef(ref.current); + }, []); + + return ; +} From d586202d97e0b9337b27ae920a5b0b1536c8349b Mon Sep 17 00:00:00 2001 From: Wenchen Li <9028430+neo@users.noreply.github.com> Date: Fri, 11 Nov 2022 17:30:31 -0500 Subject: [PATCH 06/31] Always elevate 1000 in `zIndex` when node is selected Instead of only elevating when `zIndex` is unset --- packages/core/src/store/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/store/utils.ts b/packages/core/src/store/utils.ts index f9a79ea1..e54db262 100644 --- a/packages/core/src/store/utils.ts +++ b/packages/core/src/store/utils.ts @@ -39,7 +39,7 @@ export function createNodeInternals(nodes: Node[], nodeInternals: NodeInternals) const parentNodes: ParentNodes = {}; nodes.forEach((node) => { - const z = isNumeric(node.zIndex) ? node.zIndex : node.selected ? 1000 : 0; + const z = (isNumeric(node.zIndex) ? node.zIndex : 0) + (node.selected ? 1000 : 0); const currInternals = nodeInternals.get(node.id); const internals: Node = { From 23aa8bb2707406c79a28b0cba5d49752e8335e81 Mon Sep 17 00:00:00 2001 From: GeoffreyLiu Date: Mon, 14 Nov 2022 08:30:09 +0800 Subject: [PATCH 07/31] refactor: use existing d3Selection to get the element ref --- .../components/reactflow/basic-props.cy.tsx | 29 ------------------- .../vite-app/cypress/support/RefReactFlow.tsx | 12 -------- .../components/EdgeLabelRenderer/index.tsx | 22 ++++++-------- .../src/components/StoreUpdater/index.tsx | 14 ++------- packages/core/src/constants/component.ts | 1 - .../core/src/container/GraphView/index.tsx | 3 +- .../core/src/container/ReactFlow/index.tsx | 20 ++----------- packages/core/src/store/initialState.ts | 1 - packages/core/src/types/general.ts | 5 +--- 9 files changed, 15 insertions(+), 92 deletions(-) delete mode 100644 examples/vite-app/cypress/support/RefReactFlow.tsx delete mode 100644 packages/core/src/constants/component.ts diff --git a/examples/vite-app/cypress/components/reactflow/basic-props.cy.tsx b/examples/vite-app/cypress/components/reactflow/basic-props.cy.tsx index 8738436e..d777531e 100644 --- a/examples/vite-app/cypress/components/reactflow/basic-props.cy.tsx +++ b/examples/vite-app/cypress/components/reactflow/basic-props.cy.tsx @@ -2,7 +2,6 @@ import ReactFlow, { EdgeProps } from 'reactflow'; import ControlledFlow from '../../support/ControlledFlow'; import * as simpleflow from '../../fixtures/simpleflow'; -import { RefReactFlow } from '../../support/RefReactFlow'; describe(': Basic Props', () => { describe('uses defaultNodes and defaultEdges', () => { @@ -195,32 +194,4 @@ describe(': Basic Props', () => { cy.mount(); cy.get('.react-flow').should('have.class', 'custom'); }); - - it('uses function ref', () => { - let pResolve: ((element: HTMLDivElement | PromiseLike | null) => void) | null = null; - const promise: Promise = new Promise((resolve) => { - pResolve = resolve; - }); - - function ref(root: HTMLDivElement | null) { - pResolve && pResolve(root); - } - - cy.mount(); - cy.wrap(promise).should('be.instanceOf', HTMLDivElement); - }); - - it('use mutable ref', () => { - let pResolve: ((element: HTMLDivElement | PromiseLike | null) => void) | null = null; - const promise: Promise = new Promise((resolve) => { - pResolve = resolve; - }); - - function onGetRef(root: HTMLDivElement | null) { - pResolve && pResolve(root); - } - - cy.mount(); - cy.wrap(promise).should('be.instanceOf', HTMLDivElement); - }); }); diff --git a/examples/vite-app/cypress/support/RefReactFlow.tsx b/examples/vite-app/cypress/support/RefReactFlow.tsx deleted file mode 100644 index baec9696..00000000 --- a/examples/vite-app/cypress/support/RefReactFlow.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { useEffect, useRef } from 'react'; -import ReactFlow from 'reactflow'; - -export function RefReactFlow({ onGetRef }: { onGetRef: (element: HTMLDivElement | null) => void }) { - const ref = useRef(null); - - useEffect(() => { - onGetRef(ref.current); - }, []); - - return ; -} diff --git a/packages/core/src/components/EdgeLabelRenderer/index.tsx b/packages/core/src/components/EdgeLabelRenderer/index.tsx index 815916f0..c98665ef 100644 --- a/packages/core/src/components/EdgeLabelRenderer/index.tsx +++ b/packages/core/src/components/EdgeLabelRenderer/index.tsx @@ -1,22 +1,18 @@ import type { ReactNode } from 'react'; import { createPortal } from 'react-dom'; -import { useStoreApi } from '../../hooks/useStore'; -import { EDGE_LABEL_RENDERER_MAIN_CLASS } from '../../constants/component'; +import { useStore } from '../../hooks/useStore'; +import { ReactFlowState } from '../../types'; + +const selector = (s: ReactFlowState) => ({ + d3Selection: s.d3Selection, +}); function EdgeLabelRenderer({ children }: { children: ReactNode }) { - const store = useStoreApi(); - const { - rootElementRef: { current: rootElement }, - } = store.getState(); + const { d3Selection } = useStore(selector); - if (!rootElement) { - return null; - } + const edgeLabelRendererElement = d3Selection?.select('.react-flow__edgelabel-renderer').node(); - const collection = rootElement.getElementsByClassName(EDGE_LABEL_RENDERER_MAIN_CLASS); - - const edgeLabelRendererElement = collection.item(0); - if (!edgeLabelRendererElement) { + if (!edgeLabelRendererElement || !(edgeLabelRendererElement instanceof HTMLDivElement)) { return null; } diff --git a/packages/core/src/components/StoreUpdater/index.tsx b/packages/core/src/components/StoreUpdater/index.tsx index bb3f89ca..2f98a453 100644 --- a/packages/core/src/components/StoreUpdater/index.tsx +++ b/packages/core/src/components/StoreUpdater/index.tsx @@ -3,15 +3,7 @@ import { StoreApi } from 'zustand'; import shallow from 'zustand/shallow'; import { useStore, useStoreApi } from '../../hooks/useStore'; -import type { - Node, - Edge, - ReactFlowState, - CoordinateExtent, - ReactFlowProps, - ReactFlowStore, - RootElementRef, -} from '../../types'; +import type { Node, Edge, ReactFlowState, CoordinateExtent, ReactFlowProps, ReactFlowStore } from '../../types'; type StoreUpdaterProps = Pick< ReactFlowProps, @@ -52,7 +44,7 @@ type StoreUpdaterProps = Pick< | 'onSelectionDragStop' | 'noPanClassName' | 'nodeOrigin' -> & { rfId: string; rootElementRef: RootElementRef }; +> & { rfId: string }; const selector = (s: ReactFlowState) => ({ setNodes: s.setNodes, @@ -125,7 +117,6 @@ const StoreUpdater = ({ noPanClassName, nodeOrigin, rfId, - rootElementRef, }: StoreUpdaterProps) => { const { setNodes, @@ -178,7 +169,6 @@ const StoreUpdater = ({ useDirectStoreUpdater('noPanClassName', noPanClassName, store.setState); useDirectStoreUpdater('nodeOrigin', nodeOrigin, store.setState); useDirectStoreUpdater('rfId', rfId, store.setState); - useDirectStoreUpdater('rootElementRef', rootElementRef, store.setState); useStoreUpdater(nodes, setNodes); useStoreUpdater(edges, setEdges); diff --git a/packages/core/src/constants/component.ts b/packages/core/src/constants/component.ts deleted file mode 100644 index abb2f46b..00000000 --- a/packages/core/src/constants/component.ts +++ /dev/null @@ -1 +0,0 @@ -export const EDGE_LABEL_RENDERER_MAIN_CLASS = 'react-flow__edgelabel-renderer' diff --git a/packages/core/src/container/GraphView/index.tsx b/packages/core/src/container/GraphView/index.tsx index 98f9bede..3af02659 100644 --- a/packages/core/src/container/GraphView/index.tsx +++ b/packages/core/src/container/GraphView/index.tsx @@ -6,7 +6,6 @@ import EdgeRenderer from '../EdgeRenderer'; import ViewportWrapper from '../Viewport'; import useOnInitHandler from '../../hooks/useOnInitHandler'; import type { EdgeTypesWrapped, NodeTypesWrapped, ReactFlowProps } from '../../types'; -import { EDGE_LABEL_RENDERER_MAIN_CLASS } from '../../constants/component'; export type GraphViewProps = Omit & Required< @@ -159,7 +158,7 @@ const GraphView = ({ disableKeyboardA11y={disableKeyboardA11y} rfId={rfId} /> -
+
( const edgeTypesWrapped = useNodeOrEdgeTypes(edgeTypes, createEdgeTypes) as EdgeTypesWrapped; const rfId = id || '1'; - const rootElementRef = useRef(null); - - function setRootRef(element: HTMLDivElement | null) { - rootElementRef.current = element; - - if (typeof ref === 'function') { - ref(element); - return; - } - - if (ref) { - ref.current = element; - } - } - return (
( noPanClassName={noPanClassName} nodeOrigin={nodeOrigin} rfId={rfId} - rootElementRef={rootElementRef} /> {children} diff --git a/packages/core/src/store/initialState.ts b/packages/core/src/store/initialState.ts index a0353868..ab918e65 100644 --- a/packages/core/src/store/initialState.ts +++ b/packages/core/src/store/initialState.ts @@ -54,7 +54,6 @@ const initialState: ReactFlowStore = { connectOnClick: true, ariaLiveMessage: '', - rootElementRef: { current: null } }; export default initialState; diff --git a/packages/core/src/types/general.ts b/packages/core/src/types/general.ts index 48222f49..b18f4d32 100644 --- a/packages/core/src/types/general.ts +++ b/packages/core/src/types/general.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import type { MouseEvent as ReactMouseEvent, ComponentType, MemoExoticComponent, MutableRefObject } from 'react'; +import type { MouseEvent as ReactMouseEvent, ComponentType, MemoExoticComponent } from 'react'; import type { Selection as D3Selection, ZoomBehavior } from 'd3'; import type { XYPosition, Rect, Transform, CoordinateExtent } from './utils'; @@ -129,8 +129,6 @@ export type ViewportHelperFunctions = { viewportInitialized: boolean; }; -export type RootElementRef = MutableRefObject - export type ReactFlowStore = { rfId: string; width: number; @@ -210,7 +208,6 @@ export type ReactFlowStore = { onSelectionChange?: OnSelectionChangeFunc; ariaLiveMessage: string; - rootElementRef: RootElementRef }; export type ReactFlowActions = { From 038835772f54e6e11cc66d89c4ad7d7ae610c537 Mon Sep 17 00:00:00 2001 From: GeoffreyLiu Date: Mon, 14 Nov 2022 19:55:56 +0800 Subject: [PATCH 08/31] refactor: use domNode prop to get edge-label-renderer --- .../core/src/components/EdgeLabelRenderer/index.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/core/src/components/EdgeLabelRenderer/index.tsx b/packages/core/src/components/EdgeLabelRenderer/index.tsx index c98665ef..059dae1b 100644 --- a/packages/core/src/components/EdgeLabelRenderer/index.tsx +++ b/packages/core/src/components/EdgeLabelRenderer/index.tsx @@ -4,19 +4,17 @@ import { useStore } from '../../hooks/useStore'; import { ReactFlowState } from '../../types'; const selector = (s: ReactFlowState) => ({ - d3Selection: s.d3Selection, + edgeLabelRenderer: s.domNode?.querySelector('.react-flow__edgelabel-renderer'), }); function EdgeLabelRenderer({ children }: { children: ReactNode }) { - const { d3Selection } = useStore(selector); + const { edgeLabelRenderer } = useStore(selector); - const edgeLabelRendererElement = d3Selection?.select('.react-flow__edgelabel-renderer').node(); - - if (!edgeLabelRendererElement || !(edgeLabelRendererElement instanceof HTMLDivElement)) { + if (!edgeLabelRenderer) { return null; } - return createPortal(children, edgeLabelRendererElement); + return createPortal(children, edgeLabelRenderer); } export default EdgeLabelRenderer; From a5963e62b648535ce8359561f81a774ded08ce7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=B6ller?= Date: Mon, 14 Nov 2022 15:28:15 +0100 Subject: [PATCH 09/31] feat(minimap): turn ariaLabel into an optional property --- packages/minimap/src/MiniMap.tsx | 2 +- packages/minimap/src/types.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/minimap/src/MiniMap.tsx b/packages/minimap/src/MiniMap.tsx index a6212672..42ceccce 100644 --- a/packages/minimap/src/MiniMap.tsx +++ b/packages/minimap/src/MiniMap.tsx @@ -155,7 +155,7 @@ function MiniMap({ ref={svg} onClick={onSvgClick} > - {ariaLabel} + {ariaLabel && {ariaLabel}} {nodes.map((node) => { return ( = Omit, ' onNodeClick?: (event: MouseEvent, node: Node) => void; pannable?: boolean; zoomable?: boolean; - ariaLabel?: string; + ariaLabel?: string | null; }; From 8f63f751e302d3c935865760d2134350c31ab93f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=B6ller?= Date: Mon, 14 Nov 2022 15:30:26 +0100 Subject: [PATCH 10/31] feat(minimap): add changeset --- .changeset/nine-cougars-turn.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/nine-cougars-turn.md diff --git a/.changeset/nine-cougars-turn.md b/.changeset/nine-cougars-turn.md new file mode 100644 index 00000000..eb6f3776 --- /dev/null +++ b/.changeset/nine-cougars-turn.md @@ -0,0 +1,5 @@ +--- +'@reactflow/minimap': minor +--- + +add a new property "ariaLabel" to configure or remove the aria-label of the minimap component From be89fe0020d9d0791b2dfb32d655923545e646a9 Mon Sep 17 00:00:00 2001 From: moklick Date: Tue, 15 Nov 2022 11:18:12 +0100 Subject: [PATCH 11/31] refactor(edge-label-renderer): simplify selector --- packages/core/src/components/EdgeLabelRenderer/index.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/core/src/components/EdgeLabelRenderer/index.tsx b/packages/core/src/components/EdgeLabelRenderer/index.tsx index 059dae1b..07477493 100644 --- a/packages/core/src/components/EdgeLabelRenderer/index.tsx +++ b/packages/core/src/components/EdgeLabelRenderer/index.tsx @@ -3,12 +3,10 @@ import { createPortal } from 'react-dom'; import { useStore } from '../../hooks/useStore'; import { ReactFlowState } from '../../types'; -const selector = (s: ReactFlowState) => ({ - edgeLabelRenderer: s.domNode?.querySelector('.react-flow__edgelabel-renderer'), -}); +const selector = (s: ReactFlowState) => s.domNode?.querySelector('.react-flow__edgelabel-renderer'); function EdgeLabelRenderer({ children }: { children: ReactNode }) { - const { edgeLabelRenderer } = useStore(selector); + const edgeLabelRenderer = useStore(selector); if (!edgeLabelRenderer) { return null; From 2a1c7db6b27ac0f4f81dcef2d593f4753c4321c7 Mon Sep 17 00:00:00 2001 From: moklick Date: Tue, 15 Nov 2022 11:19:46 +0100 Subject: [PATCH 12/31] chore(changeset): add --- .changeset/tender-pears-knock.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/tender-pears-knock.md diff --git a/.changeset/tender-pears-knock.md b/.changeset/tender-pears-knock.md new file mode 100644 index 00000000..697143ef --- /dev/null +++ b/.changeset/tender-pears-knock.md @@ -0,0 +1,5 @@ +--- +'@reactflow/core': patch +--- + +EdgeLabelRenderer: handle multiple instances on a page From a39224b3a80afbdb83fc4490dd5f4f2be23cd4dd Mon Sep 17 00:00:00 2001 From: moklick Date: Tue, 15 Nov 2022 11:55:32 +0100 Subject: [PATCH 13/31] chore(changeset): add --- .changeset/silly-taxis-drop.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/silly-taxis-drop.md diff --git a/.changeset/silly-taxis-drop.md b/.changeset/silly-taxis-drop.md new file mode 100644 index 00000000..e26bd820 --- /dev/null +++ b/.changeset/silly-taxis-drop.md @@ -0,0 +1,5 @@ +--- +'@reactflow/core': patch +--- + +Core: Always elevate zIndex when node is selected From b70ede04f3496f235ae52009c86c17177b08138f Mon Sep 17 00:00:00 2001 From: moklick Date: Tue, 15 Nov 2022 16:00:21 +0100 Subject: [PATCH 14/31] fix(nodeOrigin): repair multi selection and fitView --- .../vite-app/src/examples/Basic/index.tsx | 8 +++- .../src/components/NodesSelection/index.tsx | 3 +- .../src/components/UserSelection/index.tsx | 4 +- packages/core/src/store/utils.ts | 16 +++++-- packages/core/src/utils/graph.ts | 40 ++++++++++++----- packages/minimap/src/MiniMap.tsx | 43 +++++++++---------- 6 files changed, 74 insertions(+), 40 deletions(-) diff --git a/examples/vite-app/src/examples/Basic/index.tsx b/examples/vite-app/src/examples/Basic/index.tsx index d6f3950f..92077b35 100644 --- a/examples/vite-app/src/examples/Basic/index.tsx +++ b/examples/vite-app/src/examples/Basic/index.tsx @@ -8,6 +8,7 @@ import ReactFlow, { Node, Edge, useReactFlow, + NodeOrigin, } from 'reactflow'; const onNodeDrag = (_: MouseEvent, node: Node) => console.log('drag', node); @@ -47,6 +48,8 @@ const initialEdges: Edge[] = [ { id: 'e1-3', source: '1', target: '3' }, ]; +const nodeOrigin: NodeOrigin = [0.5, 0.5]; + const defaultEdgeOptions = { zIndex: 0 }; const BasicFlow = () => { @@ -91,6 +94,7 @@ const BasicFlow = () => { fitView defaultEdgeOptions={defaultEdgeOptions} selectNodesOnDrag={false} + nodeOrigin={nodeOrigin} > @@ -106,7 +110,9 @@ const BasicFlow = () => { - +
); diff --git a/packages/core/src/components/NodesSelection/index.tsx b/packages/core/src/components/NodesSelection/index.tsx index 5e75d5ed..ed841db6 100644 --- a/packages/core/src/components/NodesSelection/index.tsx +++ b/packages/core/src/components/NodesSelection/index.tsx @@ -24,12 +24,11 @@ export interface NodesSelectionProps { const selector = (s: ReactFlowState) => ({ transformString: `translate(${s.transform[0]}px,${s.transform[1]}px) scale(${s.transform[2]})`, userSelectionActive: s.userSelectionActive, - ...getRectOfNodes(Array.from(s.nodeInternals.values()).filter((n) => n.selected)), }); const bboxSelector = (s: ReactFlowState) => { const selectedNodes = Array.from(s.nodeInternals.values()).filter((n) => n.selected); - return getRectOfNodes(selectedNodes); + return getRectOfNodes(selectedNodes, s.nodeOrigin); }; function NodesSelection({ onSelectionContextMenu, noPanClassName, disableKeyboardA11y }: NodesSelectionProps) { diff --git a/packages/core/src/components/UserSelection/index.tsx b/packages/core/src/components/UserSelection/index.tsx index 142be18f..cbf1e830 100644 --- a/packages/core/src/components/UserSelection/index.tsx +++ b/packages/core/src/components/UserSelection/index.tsx @@ -101,9 +101,9 @@ const UserSelection = memo(({ selectionKeyPressed }: UserSelectionProps) => { height: Math.abs(mousePos.y - startY), }; - const { nodeInternals, edges, transform, onNodesChange, onEdgesChange } = store.getState(); + const { nodeInternals, edges, transform, onNodesChange, onEdgesChange, nodeOrigin } = store.getState(); const nodes = Array.from(nodeInternals.values()); - const selectedNodes = getNodesInside(nodeInternals, nextUserSelectRect, transform, false, true); + const selectedNodes = getNodesInside(nodeInternals, nextUserSelectRect, transform, false, true, nodeOrigin); const selectedEdgeIds = getConnectedEdges(selectedNodes, edges).map((e) => e.id); const selectedNodeIds = selectedNodes.map((n) => n.id); diff --git a/packages/core/src/store/utils.ts b/packages/core/src/store/utils.ts index f9a79ea1..5eaa232d 100644 --- a/packages/core/src/store/utils.ts +++ b/packages/core/src/store/utils.ts @@ -100,8 +100,18 @@ type InternalFitViewOptions = { } & FitViewOptions; export function fitView(get: StoreApi['getState'], options: InternalFitViewOptions = {}) { - const { nodeInternals, width, height, minZoom, maxZoom, d3Zoom, d3Selection, fitViewOnInitDone, fitViewOnInit } = - get(); + const { + nodeInternals, + width, + height, + minZoom, + maxZoom, + d3Zoom, + d3Selection, + fitViewOnInitDone, + fitViewOnInit, + nodeOrigin, + } = get(); if ((options.initial && !fitViewOnInitDone && fitViewOnInit) || !options.initial) { if (d3Zoom && d3Selection) { @@ -112,7 +122,7 @@ export function fitView(get: StoreApi['getState'], options: Inte const nodesInitialized = nodes.every((n) => n.width && n.height); if (nodes.length > 0 && nodesInitialized) { - const bounds = getRectOfNodes(nodes); + const bounds = getRectOfNodes(nodes, nodeOrigin); const [x, y, zoom] = getTransformForBounds( bounds, width, diff --git a/packages/core/src/utils/graph.ts b/packages/core/src/utils/graph.ts index 78569276..e3f6b9d7 100644 --- a/packages/core/src/utils/graph.ts +++ b/packages/core/src/utils/graph.ts @@ -2,7 +2,17 @@ import type { Selection as D3Selection } from 'd3'; import { boxToRect, clamp, devWarn, getBoundsOfBoxes, getOverlappingArea, rectToBox } from '../utils'; -import type { Node, Edge, Connection, EdgeMarkerType, Transform, XYPosition, Rect, NodeInternals } from '../types'; +import type { + Node, + Edge, + Connection, + EdgeMarkerType, + Transform, + XYPosition, + Rect, + NodeInternals, + NodeOrigin, +} from '../types'; export const isEdge = (element: Node | Connection | Edge): element is Edge => 'id' in element && 'source' in element && 'target' in element; @@ -131,22 +141,26 @@ export const pointToRendererPoint = ( return position; }; -export const getRectOfNodes = (nodes: Node[]): Rect => { +export const getRectOfNodes = (nodes: Node[], nodeOrigin: NodeOrigin = [0, 0]): Rect => { if (nodes.length === 0) { return { x: 0, y: 0, width: 0, height: 0 }; } const box = nodes.reduce( - (currBox, { positionAbsolute, position, width, height }) => - getBoundsOfBoxes( + (currBox, { positionAbsolute, position, width, height }) => { + const nodeX = positionAbsolute ? positionAbsolute.x : position.x; + const nodeY = positionAbsolute ? positionAbsolute.y : position.y; + + return getBoundsOfBoxes( currBox, rectToBox({ - x: positionAbsolute ? positionAbsolute.x : position.x, - y: positionAbsolute ? positionAbsolute.y : position.y, + x: nodeX - nodeOrigin[0] * (width || 0), + y: nodeY - nodeOrigin[1] * (height || 0), width: width || 0, height: height || 0, }) - ), + ); + }, { x: Infinity, y: Infinity, x2: -Infinity, y2: -Infinity } ); @@ -159,7 +173,8 @@ export const getNodesInside = ( [tx, ty, tScale]: Transform = [0, 0, 1], partially = false, // set excludeNonSelectableNodes if you want to pay attention to the nodes "selectable" attribute - excludeNonSelectableNodes = false + excludeNonSelectableNodes = false, + nodeOrigin: NodeOrigin = [0, 0] ): Node[] => { const paneRect = { x: (rect.x - tx) / tScale, @@ -171,13 +186,18 @@ export const getNodesInside = ( const visibleNodes: Node[] = []; nodeInternals.forEach((node) => { - const { positionAbsolute = { x: 0, y: 0 }, width, height, selectable = true } = node; + const { width, height, selectable = true, positionAbsolute = { x: 0, y: 0 } } = node; if (excludeNonSelectableNodes && !selectable) { return false; } - const nodeRect = { ...positionAbsolute, width: width || 0, height: height || 0 }; + const nodeRect = { + x: positionAbsolute.x - nodeOrigin[0] * (width || 0), + y: positionAbsolute.y - nodeOrigin[1] * (height || 0), + 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/minimap/src/MiniMap.tsx b/packages/minimap/src/MiniMap.tsx index f2487f18..c278ebd0 100644 --- a/packages/minimap/src/MiniMap.tsx +++ b/packages/minimap/src/MiniMap.tsx @@ -30,8 +30,9 @@ const selector = (s: ReactFlowState) => { return { nodes: nodes.filter((node) => !node.hidden && node.width && node.height), viewBB, - boundingRect: nodes.length > 0 ? getBoundsOfRects(getRectOfNodes(nodes), viewBB) : viewBB, + boundingRect: nodes.length > 0 ? getBoundsOfRects(getRectOfNodes(nodes, s.nodeOrigin), viewBB) : viewBB, rfId: s.rfId, + nodeOrigin: s.nodeOrigin, }; }; @@ -56,7 +57,7 @@ function MiniMap({ }: MiniMapProps) { const store = useStoreApi(); const svg = useRef(null); - const { boundingRect, viewBB, nodes, rfId } = useStore(selector, shallow); + const { boundingRect, viewBB, nodes, rfId, nodeOrigin } = useStore(selector, shallow); const elementWidth = (style?.width as number) ?? defaultWidth; const elementHeight = (style?.height as number) ?? defaultHeight; const nodeColorFunc = getAttrFunction(nodeColor); @@ -156,26 +157,24 @@ function MiniMap({ onClick={onSvgClick} > React Flow mini map - {nodes.map((node) => { - return ( - - ); - })} + {nodes.map((node) => ( + + ))} Date: Tue, 15 Nov 2022 16:11:00 +0100 Subject: [PATCH 15/31] chore(changeset): add --- .changeset/breezy-houses-travel.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/breezy-houses-travel.md diff --git a/.changeset/breezy-houses-travel.md b/.changeset/breezy-houses-travel.md new file mode 100644 index 00000000..cab7b162 --- /dev/null +++ b/.changeset/breezy-houses-travel.md @@ -0,0 +1,5 @@ +--- +'@reactflow/core': patch +--- + +Fix multi selection and fitView when nodeOrigin is used From 5897f6de02e127c92e545906f5fb7775de231604 Mon Sep 17 00:00:00 2001 From: moklick Date: Tue, 15 Nov 2022 16:47:15 +0100 Subject: [PATCH 16/31] feat(minimap): add maskStrokeWidth and maskStrokeColor --- packages/minimap/src/MiniMap.tsx | 4 ++++ packages/minimap/src/types.ts | 2 ++ 2 files changed, 6 insertions(+) diff --git a/packages/minimap/src/MiniMap.tsx b/packages/minimap/src/MiniMap.tsx index c02fa313..4764a987 100644 --- a/packages/minimap/src/MiniMap.tsx +++ b/packages/minimap/src/MiniMap.tsx @@ -48,6 +48,8 @@ function MiniMap({ nodeBorderRadius = 5, nodeStrokeWidth = 2, maskColor = 'rgb(240, 240, 240, 0.6)', + maskStrokeColor = 'none', + maskStrokeWidth = 1, position = 'bottom-right', onClick, onNodeClick, @@ -181,6 +183,8 @@ function MiniMap({ M${viewBB.x},${viewBB.y}h${viewBB.width}v${viewBB.height}h${-viewBB.width}z`} fill={maskColor} fillRule="evenodd" + stroke={maskStrokeColor} + strokeWidth={maskStrokeWidth} /> diff --git a/packages/minimap/src/types.ts b/packages/minimap/src/types.ts index ba3be40e..7d006329 100644 --- a/packages/minimap/src/types.ts +++ b/packages/minimap/src/types.ts @@ -11,6 +11,8 @@ export type MiniMapProps = Omit, ' nodeBorderRadius?: number; nodeStrokeWidth?: number; maskColor?: string; + maskStrokeColor?: string; + maskStrokeWidth?: number; position?: PanelPosition; onClick?: (event: MouseEvent, position: XYPosition) => void; onNodeClick?: (event: MouseEvent, node: Node) => void; From d745aa33fcd1333e12929c862f9a3d6de53f7179 Mon Sep 17 00:00:00 2001 From: moklick Date: Tue, 15 Nov 2022 16:48:32 +0100 Subject: [PATCH 17/31] chore(changeset): add --- .changeset/blue-houses-study.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/blue-houses-study.md diff --git a/.changeset/blue-houses-study.md b/.changeset/blue-houses-study.md new file mode 100644 index 00000000..9501bc2c --- /dev/null +++ b/.changeset/blue-houses-study.md @@ -0,0 +1,5 @@ +--- +'@reactflow/minimap': minor +--- + +Add maskStrokeColor and maskStrokeWidth props From beba0a32de72375cbcfbf0603cbe0b6f14d045b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=B6ller?= Date: Tue, 15 Nov 2022 21:48:08 +0100 Subject: [PATCH 18/31] feat(node-toolbar): add node-toolbar component --- examples/vite-app/src/App/index.tsx | 6 + .../src/examples/NodeToolbar/CustomNode.tsx | 19 +++ .../src/examples/NodeToolbar/index.tsx | 74 ++++++++++ packages/node-toolbar/.eslintrc.js | 4 + packages/node-toolbar/CHANGELOG.md | 1 + packages/node-toolbar/README.md | 10 ++ packages/node-toolbar/package.json | 68 ++++++++++ packages/node-toolbar/src/NodeToolbar.tsx | 83 ++++++++++++ .../node-toolbar/src/NodeToolbarPortal.tsx | 15 +++ packages/node-toolbar/src/index.tsx | 2 + packages/node-toolbar/src/style.css | 4 + packages/node-toolbar/src/types.ts | 9 ++ packages/node-toolbar/tsconfig.json | 6 + packages/reactflow/package.json | 6 +- packages/reactflow/src/index.ts | 1 + packages/reactflow/src/style.css | 1 + pnpm-lock.yaml | 127 +++++++++++------- 17 files changed, 386 insertions(+), 50 deletions(-) create mode 100644 examples/vite-app/src/examples/NodeToolbar/CustomNode.tsx create mode 100644 examples/vite-app/src/examples/NodeToolbar/index.tsx create mode 100644 packages/node-toolbar/.eslintrc.js create mode 100644 packages/node-toolbar/CHANGELOG.md create mode 100644 packages/node-toolbar/README.md create mode 100644 packages/node-toolbar/package.json create mode 100644 packages/node-toolbar/src/NodeToolbar.tsx create mode 100644 packages/node-toolbar/src/NodeToolbarPortal.tsx create mode 100644 packages/node-toolbar/src/index.tsx create mode 100644 packages/node-toolbar/src/style.css create mode 100644 packages/node-toolbar/src/types.ts create mode 100644 packages/node-toolbar/tsconfig.json diff --git a/examples/vite-app/src/App/index.tsx b/examples/vite-app/src/App/index.tsx index f0e32fb8..5b596f48 100644 --- a/examples/vite-app/src/App/index.tsx +++ b/examples/vite-app/src/App/index.tsx @@ -40,6 +40,7 @@ import EdgeRouting from '../examples/EdgeRouting'; import CancelConnection from '../examples/CancelConnection'; import InteractiveMinimap from '../examples/InteractiveMinimap'; import UseOnSelectionChange from '../examples/UseOnSelectionChange'; +import NodeToolbar from '../examples/NodeToolbar'; interface IRoute { name: string; @@ -168,6 +169,11 @@ const routes: IRoute[] = [ path: '/nodetypesobject-change', component: NodeTypesObjectChange, }, + { + name: 'NodeToolbar', + path: '/node-toolbar', + component: NodeToolbar, + }, { name: 'Overview', path: '/overview', diff --git a/examples/vite-app/src/examples/NodeToolbar/CustomNode.tsx b/examples/vite-app/src/examples/NodeToolbar/CustomNode.tsx new file mode 100644 index 00000000..221b0034 --- /dev/null +++ b/examples/vite-app/src/examples/NodeToolbar/CustomNode.tsx @@ -0,0 +1,19 @@ +import { memo, FC } from 'react'; +import { Handle, Position, NodeProps, NodeToolbar } from 'reactflow'; + +const CustomNode: FC = ({ id, data, selected }) => { + return ( + <> + + + + + +
{data.label}
+ + + + ); +}; + +export default memo(CustomNode); diff --git a/examples/vite-app/src/examples/NodeToolbar/index.tsx b/examples/vite-app/src/examples/NodeToolbar/index.tsx new file mode 100644 index 00000000..13dba39b --- /dev/null +++ b/examples/vite-app/src/examples/NodeToolbar/index.tsx @@ -0,0 +1,74 @@ +import ReactFlow, { + MiniMap, + Background, + BackgroundVariant, + Controls, + Node, + Edge, + NodeTypes, + Position, +} from 'reactflow'; + +import CustomNode from './CustomNode'; + +const nodeTypes: NodeTypes = { + custom: CustomNode, +}; + +const initialNodes: Node[] = [ + { + id: '1', + type: 'custom', + data: { label: 'toolbar top', toolbarPosition: Position.Top }, + position: { x: 0, y: 100 }, + className: 'react-flow__node-default', + }, + { + id: '2', + type: 'custom', + data: { label: 'toolbar right', toolbarPosition: Position.Right }, + position: { x: 400, y: 0 }, + className: 'react-flow__node-default', + }, + { + id: '3', + type: 'custom', + data: { label: 'toolbar bottom', toolbarPosition: Position.Bottom }, + position: { x: 400, y: 100 }, + className: 'react-flow__node-default', + }, + { + id: '4', + type: 'custom', + data: { label: 'toolbar left', toolbarPosition: Position.Left }, + position: { x: 400, y: 200 }, + className: 'react-flow__node-default', + }, +]; + +const initialEdges: Edge[] = [ + { id: 'e1-2', source: '1', target: '2' }, + { id: 'e1-3', source: '1', target: '3' }, + { id: 'e1-4', source: '1', target: '4' }, +]; + +const defaultEdgeOptions = { zIndex: 0 }; + +export default function NodeToolbarExample() { + return ( + + + + + + ); +} diff --git a/packages/node-toolbar/.eslintrc.js b/packages/node-toolbar/.eslintrc.js new file mode 100644 index 00000000..31cfbd11 --- /dev/null +++ b/packages/node-toolbar/.eslintrc.js @@ -0,0 +1,4 @@ +module.exports = { + root: true, + extends: ['@reactflow/eslint-config'], +}; diff --git a/packages/node-toolbar/CHANGELOG.md b/packages/node-toolbar/CHANGELOG.md new file mode 100644 index 00000000..b1a03fb6 --- /dev/null +++ b/packages/node-toolbar/CHANGELOG.md @@ -0,0 +1 @@ +# @reactflow/node-toolbar \ No newline at end of file diff --git a/packages/node-toolbar/README.md b/packages/node-toolbar/README.md new file mode 100644 index 00000000..210d62ac --- /dev/null +++ b/packages/node-toolbar/README.md @@ -0,0 +1,10 @@ +# @reactflow/node-toolbar + +A toolbar component for React Flow that can be attached to a node. + +## Installation + +```sh +npm install @reactflow/node-toolbar +``` + diff --git a/packages/node-toolbar/package.json b/packages/node-toolbar/package.json new file mode 100644 index 00000000..01cfd4f3 --- /dev/null +++ b/packages/node-toolbar/package.json @@ -0,0 +1,68 @@ +{ + "name": "@reactflow/node-toolbar", + "version": "0.0.1", + "description": "A toolbar component for React Flow that can be attached to a node.", + "keywords": [ + "react", + "node-based UI", + "graph", + "diagram", + "workflow", + "react-flow" + ], + "files": [ + "dist" + ], + "source": "src/index.tsx", + "main": "dist/umd/index.js", + "module": "dist/esm/index.js", + "types": "dist/esm/index.d.ts", + "sideEffects": [ + "*.css" + ], + "publishConfig": { + "access": "public" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/wbkd/react-flow.git", + "directory": "packages/node-toolbar" + }, + "scripts": { + "dev": "concurrently \"rollup --config node:@reactflow/rollup-config --watch\" pnpm:css-watch", + "build": "rollup --config node:@reactflow/rollup-config --environment NODE_ENV:production && npm run css", + "css": "postcss src/*.css --config ../../tooling/postcss-config/postcss.config.js --dir dist", + "css-watch": "pnpm css --watch", + "lint": "eslint --ext .js,.jsx,.ts,.tsx src", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@babel/runtime": "^7.18.9", + "@reactflow/core": "workspace:*", + "classcat": "^5.0.3", + "zustand": "^4.1.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + }, + "devDependencies": { + "@reactflow/eslint-config": "workspace:^0.0.0", + "@reactflow/rollup-config": "workspace:*", + "@reactflow/tsconfig": "workspace:*", + "@types/node": "^18.7.16", + "@types/react": "^18.0.19", + "@types/react-dom": "^18.0.6", + "react": "^18.2.0", + "typescript": "^4.8.3" + }, + "rollup": { + "globals": { + "zustand": "Zustand", + "zustand/shallow": "zustandShallow", + "classcat": "cc" + }, + "name": "ReactFlowNodeToolbar" + } +} diff --git a/packages/node-toolbar/src/NodeToolbar.tsx b/packages/node-toolbar/src/NodeToolbar.tsx new file mode 100644 index 00000000..696b490e --- /dev/null +++ b/packages/node-toolbar/src/NodeToolbar.tsx @@ -0,0 +1,83 @@ +import { useCallback, CSSProperties } from 'react'; +import { Node, ReactFlowState, useStore, getRectOfNodes, Transform, Rect, Position } from '@reactflow/core'; +import cc from 'classcat'; +import shallow from 'zustand/shallow'; + +import NodeToolbarPortal from './NodeToolbarPortal'; +import { NodeToolbarProps } from './types'; + +type SelectedNode = Node | undefined; + +const nodeEqualityFn = (a: SelectedNode, b: SelectedNode) => + a?.positionAbsolute?.x === b?.positionAbsolute?.x && + a?.positionAbsolute?.y === b?.positionAbsolute?.y && + a?.width === b?.width && + a?.height === b?.height && + a?.selected === b?.selected; + +const transformSelector = (state: ReactFlowState) => state.transform; + +function getTransform(nodeRect: Rect, transform: Transform, position: Position, offset: number): string { + // position === Position.Top + let xPos = (nodeRect.x + nodeRect.width / 2) * transform[2] + transform[0]; + let yPos = nodeRect.y * transform[2] + transform[1] - offset; + let xShift = -50; + let yShift = -100; + + switch (position) { + case Position.Right: + xPos = (nodeRect.x + nodeRect.width) * transform[2] + transform[0] + offset; + yPos = (nodeRect.y + nodeRect.height / 2) * transform[2] + transform[1]; + xShift = 0; + yShift = -50; + break; + case Position.Bottom: + yPos = (nodeRect.y + nodeRect.height) * transform[2] + transform[1] + offset; + yShift = 0; + break; + case Position.Left: + xPos = nodeRect.x * transform[2] + transform[0] - offset; + yPos = (nodeRect.y + nodeRect.height / 2) * transform[2] + transform[1]; + xShift = -100; + yShift = -50; + break; + } + + return `translate(${xPos}px, ${yPos}px) translate(${xShift}%, ${yShift}%)`; +} + +function NodeToolbar({ + nodeId, + children, + className, + style, + isActive, + position = Position.Top, + offset = 10, + ...rest +}: NodeToolbarProps) { + const nodeSelector = useCallback((state: ReactFlowState): SelectedNode => state.nodeInternals.get(nodeId), [nodeId]); + const node = useStore(nodeSelector, nodeEqualityFn); + const transform = useStore(transformSelector, shallow); + + if (!isActive || !node) { + return null; + } + + const nodeRect: Rect = getRectOfNodes([node]); + + const wrapperStyle: CSSProperties = { + transform: getTransform(nodeRect, transform, position, offset), + ...style, + }; + + return ( + +
+ {children} +
+
+ ); +} + +export default NodeToolbar; diff --git a/packages/node-toolbar/src/NodeToolbarPortal.tsx b/packages/node-toolbar/src/NodeToolbarPortal.tsx new file mode 100644 index 00000000..fa1509a4 --- /dev/null +++ b/packages/node-toolbar/src/NodeToolbarPortal.tsx @@ -0,0 +1,15 @@ +import { ReactNode } from 'react'; +import { createPortal } from 'react-dom'; +import { ReactFlowState, useStore } from '@reactflow/core'; + +function NodeToolbarPortal({ children }: { children: ReactNode }) { + const wrapperRef = useStore((state: ReactFlowState) => state.domNode?.querySelector('.react-flow__pane')); + + if (!wrapperRef) { + return null; + } + + return createPortal(children, wrapperRef); +} + +export default NodeToolbarPortal; diff --git a/packages/node-toolbar/src/index.tsx b/packages/node-toolbar/src/index.tsx new file mode 100644 index 00000000..3cded08b --- /dev/null +++ b/packages/node-toolbar/src/index.tsx @@ -0,0 +1,2 @@ +export { default as NodeToolbar } from './NodeToolbar'; +export * from './types'; diff --git a/packages/node-toolbar/src/style.css b/packages/node-toolbar/src/style.css new file mode 100644 index 00000000..2f59ab13 --- /dev/null +++ b/packages/node-toolbar/src/style.css @@ -0,0 +1,4 @@ +.react-flow__node-toolbar { + position: absolute; + z-index: 1000; +} diff --git a/packages/node-toolbar/src/types.ts b/packages/node-toolbar/src/types.ts new file mode 100644 index 00000000..c8720df7 --- /dev/null +++ b/packages/node-toolbar/src/types.ts @@ -0,0 +1,9 @@ +import { Position } from '@reactflow/core'; +import type { HTMLAttributes } from 'react'; + +export type NodeToolbarProps = HTMLAttributes & { + nodeId: string; + isActive?: boolean; + position?: Position; + offset?: number; +}; diff --git a/packages/node-toolbar/tsconfig.json b/packages/node-toolbar/tsconfig.json new file mode 100644 index 00000000..994d3bf0 --- /dev/null +++ b/packages/node-toolbar/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "@reactflow/tsconfig/react.json", + "display": "@reactflow/node-toolbar", + "include": ["**/*.ts", "**/*.tsx"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/reactflow/package.json b/packages/reactflow/package.json index 3cd02d16..bc6095ef 100644 --- a/packages/reactflow/package.json +++ b/packages/reactflow/package.json @@ -38,7 +38,8 @@ "@reactflow/background": "workspace:*", "@reactflow/controls": "workspace:*", "@reactflow/core": "workspace:*", - "@reactflow/minimap": "workspace:*" + "@reactflow/minimap": "workspace:*", + "@reactflow/node-toolbar": "workspace:*" }, "peerDependencies": { "react": ">=17", @@ -58,7 +59,8 @@ "@reactflow/background": "ReactFlowBackground", "@reactflow/controls": "ReactFlowControls", "@reactflow/core": "ReactFlowCore", - "@reactflow/minimap": "ReactFlowMinimap" + "@reactflow/minimap": "ReactFlowMinimap", + "@reactflow/node-toolbar": "ReactFlowNodeToolbar" }, "name": "ReactFlow" } diff --git a/packages/reactflow/src/index.ts b/packages/reactflow/src/index.ts index 310aa1f4..935883dc 100644 --- a/packages/reactflow/src/index.ts +++ b/packages/reactflow/src/index.ts @@ -2,5 +2,6 @@ export * from '@reactflow/core'; export * from '@reactflow/minimap'; export * from '@reactflow/controls'; export * from '@reactflow/background'; +export * from '@reactflow/node-toolbar'; export { ReactFlow as default } from '@reactflow/core'; diff --git a/packages/reactflow/src/style.css b/packages/reactflow/src/style.css index 96c6e6a1..f2d7c76c 100644 --- a/packages/reactflow/src/style.css +++ b/packages/reactflow/src/style.css @@ -1,3 +1,4 @@ @import '@reactflow/core/dist/style.css'; @import '@reactflow/controls/dist/style.css'; @import '@reactflow/minimap/dist/style.css'; +@import '@reactflow/node-toolbar/dist/style.css'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index afd78032..6d9414c1 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.42.0_dcn2ddfkdgyby36a3kmqplwxkq - '@typescript-eslint/parser': registry.npmjs.org/@typescript-eslint/parser/5.42.0_irgkl5vooow2ydyo6aokmferha + '@typescript-eslint/eslint-plugin': registry.npmjs.org/@typescript-eslint/eslint-plugin/5.43.0_nctdnn3gg3ldnbrjkdxid2oppm + '@typescript-eslint/parser': registry.npmjs.org/@typescript-eslint/parser/5.43.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 @@ -215,6 +215,35 @@ importers: react: registry.npmjs.org/react/18.2.0 typescript: registry.npmjs.org/typescript/4.8.3 + packages/node-toolbar: + specifiers: + '@babel/runtime': ^7.18.9 + '@reactflow/core': workspace:* + '@reactflow/eslint-config': workspace:^0.0.0 + '@reactflow/rollup-config': workspace:* + '@reactflow/tsconfig': workspace:* + '@types/node': ^18.7.16 + '@types/react': ^18.0.19 + '@types/react-dom': ^18.0.6 + classcat: ^5.0.3 + 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 + classcat: registry.npmjs.org/classcat/5.0.4 + zustand: registry.npmjs.org/zustand/4.1.1_react@18.2.0 + devDependencies: + '@reactflow/eslint-config': link:../../tooling/eslint-config + '@reactflow/rollup-config': link:../../tooling/rollup-config + '@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 + packages/reactflow: specifiers: '@reactflow/background': workspace:* @@ -222,6 +251,7 @@ importers: '@reactflow/core': workspace:* '@reactflow/eslint-config': workspace:^0.0.0 '@reactflow/minimap': workspace:* + '@reactflow/node-toolbar': workspace:* '@reactflow/rollup-config': workspace:* '@reactflow/tsconfig': workspace:* '@types/node': ^18.7.16 @@ -233,6 +263,7 @@ importers: '@reactflow/controls': link:../controls '@reactflow/core': link:../core '@reactflow/minimap': link:../minimap + '@reactflow/node-toolbar': link:../node-toolbar devDependencies: '@reactflow/eslint-config': link:../../tooling/eslint-config '@reactflow/rollup-config': link:../../tooling/rollup-config @@ -1720,11 +1751,11 @@ packages: dev: true optional: true - 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 + registry.npmjs.org/@typescript-eslint/eslint-plugin/5.43.0_nctdnn3gg3ldnbrjkdxid2oppm: + resolution: {integrity: sha512-wNPzG+eDR6+hhW4yobEmpR36jrqqQv1vxBq5LJO3fBAktjkvekfr4BRl+3Fn1CM/A+s8/EiGUbOMDoYqWdbtXA==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.43.0.tgz} + id: registry.npmjs.org/@typescript-eslint/eslint-plugin/5.43.0 name: '@typescript-eslint/eslint-plugin' - version: 5.42.0 + version: 5.43.0 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: '@typescript-eslint/parser': ^5.0.0 @@ -1734,10 +1765,10 @@ packages: typescript: optional: true dependencies: - '@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 + '@typescript-eslint/parser': registry.npmjs.org/@typescript-eslint/parser/5.43.0_irgkl5vooow2ydyo6aokmferha + '@typescript-eslint/scope-manager': registry.npmjs.org/@typescript-eslint/scope-manager/5.43.0 + '@typescript-eslint/type-utils': registry.npmjs.org/@typescript-eslint/type-utils/5.43.0_irgkl5vooow2ydyo6aokmferha + '@typescript-eslint/utils': registry.npmjs.org/@typescript-eslint/utils/5.43.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 @@ -1750,11 +1781,11 @@ packages: - supports-color dev: true - 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 + registry.npmjs.org/@typescript-eslint/parser/5.43.0_irgkl5vooow2ydyo6aokmferha: + resolution: {integrity: sha512-2iHUK2Lh7PwNUlhFxxLI2haSDNyXvebBO9izhjhMoDC+S3XI9qt2DGFUsiJ89m2k7gGYch2aEpYqV5F/+nwZug==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.43.0.tgz} + id: registry.npmjs.org/@typescript-eslint/parser/5.43.0 name: '@typescript-eslint/parser' - version: 5.42.0 + version: 5.43.0 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -1763,9 +1794,9 @@ packages: typescript: optional: true dependencies: - '@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 + '@typescript-eslint/scope-manager': registry.npmjs.org/@typescript-eslint/scope-manager/5.43.0 + '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.43.0 + '@typescript-eslint/typescript-estree': registry.npmjs.org/@typescript-eslint/typescript-estree/5.43.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 @@ -1773,21 +1804,21 @@ packages: - supports-color dev: true - 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} + registry.npmjs.org/@typescript-eslint/scope-manager/5.43.0: + resolution: {integrity: sha512-XNWnGaqAtTJsUiZaoiGIrdJYHsUOd3BZ3Qj5zKp9w6km6HsrjPk/TGZv0qMTWyWj0+1QOqpHQ2gZOLXaGA9Ekw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.43.0.tgz} name: '@typescript-eslint/scope-manager' - version: 5.42.0 + version: 5.43.0 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@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 + '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.43.0 + '@typescript-eslint/visitor-keys': registry.npmjs.org/@typescript-eslint/visitor-keys/5.43.0 dev: true - 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 + registry.npmjs.org/@typescript-eslint/type-utils/5.43.0_irgkl5vooow2ydyo6aokmferha: + resolution: {integrity: sha512-K21f+KY2/VvYggLf5Pk4tgBOPs2otTaIHy2zjclo7UZGLyFH86VfUOm5iq+OtDtxq/Zwu2I3ujDBykVW4Xtmtg==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.43.0.tgz} + id: registry.npmjs.org/@typescript-eslint/type-utils/5.43.0 name: '@typescript-eslint/type-utils' - version: 5.42.0 + version: 5.43.0 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: '*' @@ -1796,8 +1827,8 @@ packages: typescript: optional: true dependencies: - '@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 + '@typescript-eslint/typescript-estree': registry.npmjs.org/@typescript-eslint/typescript-estree/5.43.0_typescript@4.8.3 + '@typescript-eslint/utils': registry.npmjs.org/@typescript-eslint/utils/5.43.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 @@ -1806,18 +1837,18 @@ packages: - supports-color dev: true - 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} + registry.npmjs.org/@typescript-eslint/types/5.43.0: + resolution: {integrity: sha512-jpsbcD0x6AUvV7tyOlyvon0aUsQpF8W+7TpJntfCUWU1qaIKu2K34pMwQKSzQH8ORgUrGYY6pVIh1Pi8TNeteg==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/types/-/types-5.43.0.tgz} name: '@typescript-eslint/types' - version: 5.42.0 + version: 5.43.0 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - 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 + registry.npmjs.org/@typescript-eslint/typescript-estree/5.43.0_typescript@4.8.3: + resolution: {integrity: sha512-BZ1WVe+QQ+igWal2tDbNg1j2HWUkAa+CVqdU79L4HP9izQY6CNhXfkNwd1SS4+sSZAP/EthI1uiCSY/+H0pROg==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.43.0.tgz} + id: registry.npmjs.org/@typescript-eslint/typescript-estree/5.43.0 name: '@typescript-eslint/typescript-estree' - version: 5.42.0 + version: 5.43.0 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: typescript: '*' @@ -1825,8 +1856,8 @@ packages: typescript: optional: true dependencies: - '@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 + '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.43.0 + '@typescript-eslint/visitor-keys': registry.npmjs.org/@typescript-eslint/visitor-keys/5.43.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 @@ -1837,20 +1868,20 @@ packages: - supports-color dev: true - 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 + registry.npmjs.org/@typescript-eslint/utils/5.43.0_irgkl5vooow2ydyo6aokmferha: + resolution: {integrity: sha512-8nVpA6yX0sCjf7v/NDfeaOlyaIIqL7OaIGOWSPFqUKK59Gnumd3Wa+2l8oAaYO2lk0sO+SbWFWRSvhu8gLGv4A==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.43.0.tgz} + id: registry.npmjs.org/@typescript-eslint/utils/5.43.0 name: '@typescript-eslint/utils' - version: 5.42.0 + version: 5.43.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 '@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 + '@typescript-eslint/scope-manager': registry.npmjs.org/@typescript-eslint/scope-manager/5.43.0 + '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.43.0 + '@typescript-eslint/typescript-estree': registry.npmjs.org/@typescript-eslint/typescript-estree/5.43.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 @@ -1860,13 +1891,13 @@ packages: - typescript dev: true - 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} + registry.npmjs.org/@typescript-eslint/visitor-keys/5.43.0: + resolution: {integrity: sha512-icl1jNH/d18OVHLfcwdL3bWUKsBeIiKYTGxMJCoGe7xFht+E4QgzOqoWYrU8XSLJWhVw8nTacbm03v23J/hFTg==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.43.0.tgz} name: '@typescript-eslint/visitor-keys' - version: 5.42.0 + version: 5.43.0 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.42.0 + '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types/5.43.0 eslint-visitor-keys: registry.npmjs.org/eslint-visitor-keys/3.3.0 dev: true From 20a6653577f806cab5af93d8e11fa187ace40f62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=B6ller?= Date: Wed, 16 Nov 2022 09:56:34 +0100 Subject: [PATCH 19/31] feat(node-toolbar): use z-index of the node for the toolbar --- packages/node-toolbar/src/NodeToolbar.tsx | 15 +++++++++++++-- packages/node-toolbar/src/NodeToolbarPortal.tsx | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/node-toolbar/src/NodeToolbar.tsx b/packages/node-toolbar/src/NodeToolbar.tsx index 696b490e..c8085ecf 100644 --- a/packages/node-toolbar/src/NodeToolbar.tsx +++ b/packages/node-toolbar/src/NodeToolbar.tsx @@ -1,5 +1,14 @@ import { useCallback, CSSProperties } from 'react'; -import { Node, ReactFlowState, useStore, getRectOfNodes, Transform, Rect, Position } from '@reactflow/core'; +import { + Node, + ReactFlowState, + useStore, + getRectOfNodes, + Transform, + Rect, + Position, + internalsSymbol, +} from '@reactflow/core'; import cc from 'classcat'; import shallow from 'zustand/shallow'; @@ -13,7 +22,8 @@ const nodeEqualityFn = (a: SelectedNode, b: SelectedNode) => a?.positionAbsolute?.y === b?.positionAbsolute?.y && a?.width === b?.width && a?.height === b?.height && - a?.selected === b?.selected; + a?.selected === b?.selected && + a?.[internalsSymbol]?.z === b?.[internalsSymbol]?.z; const transformSelector = (state: ReactFlowState) => state.transform; @@ -68,6 +78,7 @@ function NodeToolbar({ const wrapperStyle: CSSProperties = { transform: getTransform(nodeRect, transform, position, offset), + zIndex: node[internalsSymbol]?.z, ...style, }; diff --git a/packages/node-toolbar/src/NodeToolbarPortal.tsx b/packages/node-toolbar/src/NodeToolbarPortal.tsx index fa1509a4..f899352f 100644 --- a/packages/node-toolbar/src/NodeToolbarPortal.tsx +++ b/packages/node-toolbar/src/NodeToolbarPortal.tsx @@ -3,7 +3,7 @@ import { createPortal } from 'react-dom'; import { ReactFlowState, useStore } from '@reactflow/core'; function NodeToolbarPortal({ children }: { children: ReactNode }) { - const wrapperRef = useStore((state: ReactFlowState) => state.domNode?.querySelector('.react-flow__pane')); + const wrapperRef = useStore((state: ReactFlowState) => state.domNode?.querySelector('.react-flow__renderer')); if (!wrapperRef) { return null; From ccbffeb139897ea984066bd77a02f442f9ff6a8c Mon Sep 17 00:00:00 2001 From: moklick Date: Wed, 16 Nov 2022 10:53:26 +0100 Subject: [PATCH 20/31] fix(node-toolbar): handle nodeOrigin --- examples/vite-app/src/examples/NodeToolbar/index.tsx | 3 +++ packages/node-toolbar/src/NodeToolbar.tsx | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/vite-app/src/examples/NodeToolbar/index.tsx b/examples/vite-app/src/examples/NodeToolbar/index.tsx index 13dba39b..424ee202 100644 --- a/examples/vite-app/src/examples/NodeToolbar/index.tsx +++ b/examples/vite-app/src/examples/NodeToolbar/index.tsx @@ -7,6 +7,7 @@ import ReactFlow, { Edge, NodeTypes, Position, + NodeOrigin, } from 'reactflow'; import CustomNode from './CustomNode'; @@ -53,6 +54,7 @@ const initialEdges: Edge[] = [ ]; const defaultEdgeOptions = { zIndex: 0 }; +const nodeOrigin: NodeOrigin = [0.5, 0.5]; export default function NodeToolbarExample() { return ( @@ -65,6 +67,7 @@ export default function NodeToolbarExample() { fitView defaultEdgeOptions={defaultEdgeOptions} nodeTypes={nodeTypes} + nodeOrigin={nodeOrigin} > diff --git a/packages/node-toolbar/src/NodeToolbar.tsx b/packages/node-toolbar/src/NodeToolbar.tsx index c8085ecf..c413eb30 100644 --- a/packages/node-toolbar/src/NodeToolbar.tsx +++ b/packages/node-toolbar/src/NodeToolbar.tsx @@ -25,7 +25,7 @@ const nodeEqualityFn = (a: SelectedNode, b: SelectedNode) => a?.selected === b?.selected && a?.[internalsSymbol]?.z === b?.[internalsSymbol]?.z; -const transformSelector = (state: ReactFlowState) => state.transform; +const transformSelector = (state: ReactFlowState) => ({ transform: state.transform, nodeOrigin: state.nodeOrigin }); function getTransform(nodeRect: Rect, transform: Transform, position: Position, offset: number): string { // position === Position.Top @@ -68,13 +68,13 @@ function NodeToolbar({ }: NodeToolbarProps) { const nodeSelector = useCallback((state: ReactFlowState): SelectedNode => state.nodeInternals.get(nodeId), [nodeId]); const node = useStore(nodeSelector, nodeEqualityFn); - const transform = useStore(transformSelector, shallow); + const { transform, nodeOrigin } = useStore(transformSelector, shallow); if (!isActive || !node) { return null; } - const nodeRect: Rect = getRectOfNodes([node]); + const nodeRect: Rect = getRectOfNodes([node], nodeOrigin); const wrapperStyle: CSSProperties = { transform: getTransform(nodeRect, transform, position, offset), From be8f22e5f3ccde5bf63feab145ec4cf3d8568f4e Mon Sep 17 00:00:00 2001 From: moklick Date: Wed, 16 Nov 2022 10:59:20 +0100 Subject: [PATCH 21/31] chore(node-toolbar): cleanup --- packages/node-toolbar/src/NodeToolbar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/node-toolbar/src/NodeToolbar.tsx b/packages/node-toolbar/src/NodeToolbar.tsx index c413eb30..1b98682e 100644 --- a/packages/node-toolbar/src/NodeToolbar.tsx +++ b/packages/node-toolbar/src/NodeToolbar.tsx @@ -25,7 +25,7 @@ const nodeEqualityFn = (a: SelectedNode, b: SelectedNode) => a?.selected === b?.selected && a?.[internalsSymbol]?.z === b?.[internalsSymbol]?.z; -const transformSelector = (state: ReactFlowState) => ({ transform: state.transform, nodeOrigin: state.nodeOrigin }); +const selector = (state: ReactFlowState) => ({ transform: state.transform, nodeOrigin: state.nodeOrigin }); function getTransform(nodeRect: Rect, transform: Transform, position: Position, offset: number): string { // position === Position.Top @@ -68,7 +68,7 @@ function NodeToolbar({ }: NodeToolbarProps) { const nodeSelector = useCallback((state: ReactFlowState): SelectedNode => state.nodeInternals.get(nodeId), [nodeId]); const node = useStore(nodeSelector, nodeEqualityFn); - const { transform, nodeOrigin } = useStore(transformSelector, shallow); + const { transform, nodeOrigin } = useStore(selector, shallow); if (!isActive || !node) { return null; From edbf894350bc3aa47e8fe7d8fbd4dfe487055500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=B6ller?= Date: Wed, 16 Nov 2022 13:49:15 +0100 Subject: [PATCH 22/31] feat(node-toolbar): change isActive to isVisible and implement default behavior --- .../vite-app/src/examples/NodeToolbar/CustomNode.tsx | 4 ++-- examples/vite-app/src/examples/NodeToolbar/index.tsx | 9 ++++++++- packages/node-toolbar/src/NodeToolbar.tsx | 8 ++++++-- packages/node-toolbar/src/types.ts | 2 +- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/examples/vite-app/src/examples/NodeToolbar/CustomNode.tsx b/examples/vite-app/src/examples/NodeToolbar/CustomNode.tsx index 221b0034..4f8ab270 100644 --- a/examples/vite-app/src/examples/NodeToolbar/CustomNode.tsx +++ b/examples/vite-app/src/examples/NodeToolbar/CustomNode.tsx @@ -1,10 +1,10 @@ import { memo, FC } from 'react'; import { Handle, Position, NodeProps, NodeToolbar } from 'reactflow'; -const CustomNode: FC = ({ id, data, selected }) => { +const CustomNode: FC = ({ id, data }) => { return ( <> - + diff --git a/examples/vite-app/src/examples/NodeToolbar/index.tsx b/examples/vite-app/src/examples/NodeToolbar/index.tsx index 13dba39b..4389873c 100644 --- a/examples/vite-app/src/examples/NodeToolbar/index.tsx +++ b/examples/vite-app/src/examples/NodeToolbar/index.tsx @@ -20,7 +20,7 @@ const initialNodes: Node[] = [ id: '1', type: 'custom', data: { label: 'toolbar top', toolbarPosition: Position.Top }, - position: { x: 0, y: 100 }, + position: { x: 0, y: 0 }, className: 'react-flow__node-default', }, { @@ -44,6 +44,13 @@ const initialNodes: Node[] = [ position: { x: 400, y: 200 }, className: 'react-flow__node-default', }, + { + id: '5', + type: 'custom', + data: { label: 'toolbar always open', toolbarPosition: Position.Top, toolbarVisible: true }, + position: { x: 0, y: 150 }, + className: 'react-flow__node-default', + }, ]; const initialEdges: Edge[] = [ diff --git a/packages/node-toolbar/src/NodeToolbar.tsx b/packages/node-toolbar/src/NodeToolbar.tsx index c8085ecf..50207dda 100644 --- a/packages/node-toolbar/src/NodeToolbar.tsx +++ b/packages/node-toolbar/src/NodeToolbar.tsx @@ -25,7 +25,9 @@ const nodeEqualityFn = (a: SelectedNode, b: SelectedNode) => a?.selected === b?.selected && a?.[internalsSymbol]?.z === b?.[internalsSymbol]?.z; -const transformSelector = (state: ReactFlowState) => state.transform; +const transformSelector = (state: ReactFlowState): Transform => state.transform; +const selectedNodesCountSelector = (state: ReactFlowState): number => + Array.from(state.nodeInternals.values()).filter((node) => node.selected).length; function getTransform(nodeRect: Rect, transform: Transform, position: Position, offset: number): string { // position === Position.Top @@ -61,7 +63,7 @@ function NodeToolbar({ children, className, style, - isActive, + isVisible, position = Position.Top, offset = 10, ...rest @@ -69,6 +71,8 @@ function NodeToolbar({ const nodeSelector = useCallback((state: ReactFlowState): SelectedNode => state.nodeInternals.get(nodeId), [nodeId]); const node = useStore(nodeSelector, nodeEqualityFn); const transform = useStore(transformSelector, shallow); + const selectedNodesCount = useStore(selectedNodesCountSelector); + const isActive = typeof isVisible === 'boolean' ? isVisible : node?.selected && selectedNodesCount === 1; if (!isActive || !node) { return null; diff --git a/packages/node-toolbar/src/types.ts b/packages/node-toolbar/src/types.ts index c8720df7..1e6576b6 100644 --- a/packages/node-toolbar/src/types.ts +++ b/packages/node-toolbar/src/types.ts @@ -3,7 +3,7 @@ import type { HTMLAttributes } from 'react'; export type NodeToolbarProps = HTMLAttributes & { nodeId: string; - isActive?: boolean; + isVisible?: boolean; position?: Position; offset?: number; }; From 1841dfa2cae93e2ab4eb05d63f3a832a095983da Mon Sep 17 00:00:00 2001 From: moklick Date: Wed, 16 Nov 2022 18:28:25 +0100 Subject: [PATCH 23/31] refactor(node-toolbar): define zustand selector outside of the component --- packages/node-toolbar/src/NodeToolbarPortal.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/node-toolbar/src/NodeToolbarPortal.tsx b/packages/node-toolbar/src/NodeToolbarPortal.tsx index f899352f..732362e5 100644 --- a/packages/node-toolbar/src/NodeToolbarPortal.tsx +++ b/packages/node-toolbar/src/NodeToolbarPortal.tsx @@ -2,8 +2,10 @@ import { ReactNode } from 'react'; import { createPortal } from 'react-dom'; import { ReactFlowState, useStore } from '@reactflow/core'; +const selector = (state: ReactFlowState) => state.domNode?.querySelector('.react-flow__renderer'); + function NodeToolbarPortal({ children }: { children: ReactNode }) { - const wrapperRef = useStore((state: ReactFlowState) => state.domNode?.querySelector('.react-flow__renderer')); + const wrapperRef = useStore(selector); if (!wrapperRef) { return null; From b31d8e9dc20e94f916d8d098d08066811958eb14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=B6ller?= Date: Thu, 17 Nov 2022 09:59:28 +0100 Subject: [PATCH 24/31] feat(node-toolbar): remove css from node-toolbar component, set version to 1.0.0 --- packages/node-toolbar/package.json | 6 ++---- packages/node-toolbar/src/NodeToolbar.tsx | 3 ++- packages/node-toolbar/src/style.css | 4 ---- packages/reactflow/src/style.css | 1 - 4 files changed, 4 insertions(+), 10 deletions(-) delete mode 100644 packages/node-toolbar/src/style.css diff --git a/packages/node-toolbar/package.json b/packages/node-toolbar/package.json index 01cfd4f3..ad15e213 100644 --- a/packages/node-toolbar/package.json +++ b/packages/node-toolbar/package.json @@ -1,6 +1,6 @@ { "name": "@reactflow/node-toolbar", - "version": "0.0.1", + "version": "1.0.0", "description": "A toolbar component for React Flow that can be attached to a node.", "keywords": [ "react", @@ -31,9 +31,7 @@ }, "scripts": { "dev": "concurrently \"rollup --config node:@reactflow/rollup-config --watch\" pnpm:css-watch", - "build": "rollup --config node:@reactflow/rollup-config --environment NODE_ENV:production && npm run css", - "css": "postcss src/*.css --config ../../tooling/postcss-config/postcss.config.js --dir dist", - "css-watch": "pnpm css --watch", + "build": "rollup --config node:@reactflow/rollup-config --environment NODE_ENV:production", "lint": "eslint --ext .js,.jsx,.ts,.tsx src", "typecheck": "tsc --noEmit" }, diff --git a/packages/node-toolbar/src/NodeToolbar.tsx b/packages/node-toolbar/src/NodeToolbar.tsx index 1dc5b4a8..caadf257 100644 --- a/packages/node-toolbar/src/NodeToolbar.tsx +++ b/packages/node-toolbar/src/NodeToolbar.tsx @@ -82,8 +82,9 @@ function NodeToolbar({ const nodeRect: Rect = getRectOfNodes([node], nodeOrigin); const wrapperStyle: CSSProperties = { + position: 'absolute', transform: getTransform(nodeRect, transform, position, offset), - zIndex: node[internalsSymbol]?.z, + zIndex: (node[internalsSymbol]?.z || 1) + 1, ...style, }; diff --git a/packages/node-toolbar/src/style.css b/packages/node-toolbar/src/style.css deleted file mode 100644 index 2f59ab13..00000000 --- a/packages/node-toolbar/src/style.css +++ /dev/null @@ -1,4 +0,0 @@ -.react-flow__node-toolbar { - position: absolute; - z-index: 1000; -} diff --git a/packages/reactflow/src/style.css b/packages/reactflow/src/style.css index f2d7c76c..96c6e6a1 100644 --- a/packages/reactflow/src/style.css +++ b/packages/reactflow/src/style.css @@ -1,4 +1,3 @@ @import '@reactflow/core/dist/style.css'; @import '@reactflow/controls/dist/style.css'; @import '@reactflow/minimap/dist/style.css'; -@import '@reactflow/node-toolbar/dist/style.css'; From 98116d431f9fcdcc9b23a5b606a94ec0740b64cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=B6ller?= Date: Thu, 17 Nov 2022 11:28:41 +0100 Subject: [PATCH 25/31] chore(reactflow): add changeset for reactflow package --- .changeset/cuddly-books-kiss.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/cuddly-books-kiss.md diff --git a/.changeset/cuddly-books-kiss.md b/.changeset/cuddly-books-kiss.md new file mode 100644 index 00000000..bda976f5 --- /dev/null +++ b/.changeset/cuddly-books-kiss.md @@ -0,0 +1,5 @@ +--- +'reactflow': minor +--- + +Export a new component "NodeToolbar" that renders a fixed element attached to a node From 4ebbea1960059891756f23b690fafca624425efc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=B6ller?= Date: Thu, 17 Nov 2022 11:36:12 +0100 Subject: [PATCH 26/31] chore(node-toolbar): set initial package version --- packages/node-toolbar/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node-toolbar/package.json b/packages/node-toolbar/package.json index ad15e213..9d9be308 100644 --- a/packages/node-toolbar/package.json +++ b/packages/node-toolbar/package.json @@ -1,6 +1,6 @@ { "name": "@reactflow/node-toolbar", - "version": "1.0.0", + "version": "0.0.1", "description": "A toolbar component for React Flow that can be attached to a node.", "keywords": [ "react", From c793433cafc214281ae97c9a32f5ac2fe453c34f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=B6ller?= Date: Thu, 17 Nov 2022 11:39:45 +0100 Subject: [PATCH 27/31] chore(node-toolbar): add changeset for node-toolbar --- .changeset/young-zoos-push.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/young-zoos-push.md diff --git a/.changeset/young-zoos-push.md b/.changeset/young-zoos-push.md new file mode 100644 index 00000000..a0854519 --- /dev/null +++ b/.changeset/young-zoos-push.md @@ -0,0 +1,5 @@ +--- +'@reactflow/node-toolbar': major +--- + +Release a first version of the NodeToolbar component that renders a fixed element that can be attached to a node From 8e6a353d5872de77062b85c474c6f549f82ebf15 Mon Sep 17 00:00:00 2001 From: moklick Date: Thu, 17 Nov 2022 14:51:21 +0100 Subject: [PATCH 28/31] fix(updatable-edge): fix disappearing connection line for loose flows closes #2529 --- .../src/components/ConnectionLine/index.tsx | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/core/src/components/ConnectionLine/index.tsx b/packages/core/src/components/ConnectionLine/index.tsx index 767d16f3..9da8259a 100644 --- a/packages/core/src/components/ConnectionLine/index.tsx +++ b/packages/core/src/components/ConnectionLine/index.tsx @@ -6,7 +6,7 @@ import { getBezierPath } from '../Edges/BezierEdge'; import { getSmoothStepPath } from '../Edges/SmoothStepEdge'; import { getSimpleBezierPath } from '../Edges/SimpleBezierEdge'; import { internalsSymbol } from '../../utils'; -import type { ConnectionLineComponent, HandleType, ReactFlowStore } from '../../types'; +import { ConnectionLineComponent, ConnectionMode, HandleType, ReactFlowStore } from '../../types'; import { Position, ConnectionLineType } from '../../types'; type ConnectionLineProps = { @@ -33,26 +33,33 @@ const ConnectionLine = ({ isConnectable, CustomConnectionLineComponent, }: ConnectionLineProps) => { - const { fromNode, handleId, toX, toY } = useStore( + const { fromNode, handleId, toX, toY, connectionMode } = useStore( useCallback( (s: ReactFlowStore) => ({ fromNode: s.nodeInternals.get(connectionNodeId), handleId: s.connectionHandleId, toX: (s.connectionPosition.x - s.transform[0]) / s.transform[2], toY: (s.connectionPosition.y - s.transform[1]) / s.transform[2], + connectionMode: s.connectionMode, }), [connectionNodeId] ), shallow ); const fromHandleBounds = fromNode?.[internalsSymbol]?.handleBounds; + let handleBounds = fromHandleBounds?.[connectionHandleType]; - if (!fromNode || !isConnectable || !fromHandleBounds?.[connectionHandleType]) { + if (connectionMode === ConnectionMode.Loose) { + handleBounds = handleBounds + ? handleBounds + : fromHandleBounds?.[connectionHandleType === 'source' ? 'target' : 'source']; + } + + if (!fromNode || !isConnectable || !handleBounds) { return null; } - const handleBound = fromHandleBounds[connectionHandleType]!; - const fromHandle = handleId ? handleBound.find((d) => d.id === handleId) : handleBound[0]; + const fromHandle = handleId ? handleBounds.find((d) => d.id === handleId) : handleBounds[0]; const fromHandleX = fromHandle ? fromHandle.x + fromHandle.width / 2 : (fromNode?.width ?? 0) / 2; const fromHandleY = fromHandle ? fromHandle.y + fromHandle.height / 2 : fromNode?.height ?? 0; const fromX = (fromNode?.positionAbsolute?.x || 0) + fromHandleX; From 493d748c18ab47fd7b3d1362957d7716bf884009 Mon Sep 17 00:00:00 2001 From: moklick Date: Thu, 17 Nov 2022 14:53:32 +0100 Subject: [PATCH 29/31] chore(connection-line): use type for imports --- packages/core/src/components/ConnectionLine/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/components/ConnectionLine/index.tsx b/packages/core/src/components/ConnectionLine/index.tsx index 9da8259a..28f1996a 100644 --- a/packages/core/src/components/ConnectionLine/index.tsx +++ b/packages/core/src/components/ConnectionLine/index.tsx @@ -6,8 +6,8 @@ import { getBezierPath } from '../Edges/BezierEdge'; import { getSmoothStepPath } from '../Edges/SmoothStepEdge'; import { getSimpleBezierPath } from '../Edges/SimpleBezierEdge'; import { internalsSymbol } from '../../utils'; -import { ConnectionLineComponent, ConnectionMode, HandleType, ReactFlowStore } from '../../types'; -import { Position, ConnectionLineType } from '../../types'; +import type { ConnectionLineComponent, HandleType, ReactFlowStore } from '../../types'; +import { Position, ConnectionLineType, ConnectionMode } from '../../types'; type ConnectionLineProps = { connectionNodeId: string; From 5e8b67dd41f9bb60dcd7f5d14cc34b42c970e967 Mon Sep 17 00:00:00 2001 From: moklick Date: Thu, 17 Nov 2022 14:54:23 +0100 Subject: [PATCH 30/31] chore(changeset): add --- .changeset/small-walls-enjoy.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/small-walls-enjoy.md diff --git a/.changeset/small-walls-enjoy.md b/.changeset/small-walls-enjoy.md new file mode 100644 index 00000000..581e6a6f --- /dev/null +++ b/.changeset/small-walls-enjoy.md @@ -0,0 +1,5 @@ +--- +'@reactflow/core': patch +--- + +fix(updatable-edge): fix disappearing connection line for loose flows From a458564bfc476dbede659c82220548987c4b7e1e Mon Sep 17 00:00:00 2001 From: moklick Date: Mon, 21 Nov 2022 14:53:18 +0100 Subject: [PATCH 31/31] chore(packages): bump versions --- .changeset/blue-houses-study.md | 5 ----- .changeset/breezy-houses-travel.md | 5 ----- .changeset/cuddly-books-kiss.md | 5 ----- .changeset/nine-cougars-turn.md | 5 ----- .changeset/silly-taxis-drop.md | 5 ----- .changeset/small-walls-enjoy.md | 5 ----- .changeset/tender-pears-knock.md | 5 ----- .changeset/young-zoos-push.md | 5 ----- packages/background/CHANGELOG.md | 7 +++++++ packages/background/package.json | 2 +- packages/controls/CHANGELOG.md | 7 +++++++ packages/controls/package.json | 2 +- packages/core/CHANGELOG.md | 16 +++++++++++++--- packages/core/package.json | 2 +- packages/minimap/CHANGELOG.md | 12 ++++++++++++ packages/minimap/package.json | 2 +- packages/node-toolbar/CHANGELOG.md | 8 +++++++- packages/node-toolbar/package.json | 2 +- packages/reactflow/CHANGELOG.md | 25 ++++++++++++++++++++++--- packages/reactflow/package.json | 2 +- 20 files changed, 74 insertions(+), 53 deletions(-) delete mode 100644 .changeset/blue-houses-study.md delete mode 100644 .changeset/breezy-houses-travel.md delete mode 100644 .changeset/cuddly-books-kiss.md delete mode 100644 .changeset/nine-cougars-turn.md delete mode 100644 .changeset/silly-taxis-drop.md delete mode 100644 .changeset/small-walls-enjoy.md delete mode 100644 .changeset/tender-pears-knock.md delete mode 100644 .changeset/young-zoos-push.md diff --git a/.changeset/blue-houses-study.md b/.changeset/blue-houses-study.md deleted file mode 100644 index 9501bc2c..00000000 --- a/.changeset/blue-houses-study.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@reactflow/minimap': minor ---- - -Add maskStrokeColor and maskStrokeWidth props diff --git a/.changeset/breezy-houses-travel.md b/.changeset/breezy-houses-travel.md deleted file mode 100644 index cab7b162..00000000 --- a/.changeset/breezy-houses-travel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@reactflow/core': patch ---- - -Fix multi selection and fitView when nodeOrigin is used diff --git a/.changeset/cuddly-books-kiss.md b/.changeset/cuddly-books-kiss.md deleted file mode 100644 index bda976f5..00000000 --- a/.changeset/cuddly-books-kiss.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'reactflow': minor ---- - -Export a new component "NodeToolbar" that renders a fixed element attached to a node diff --git a/.changeset/nine-cougars-turn.md b/.changeset/nine-cougars-turn.md deleted file mode 100644 index eb6f3776..00000000 --- a/.changeset/nine-cougars-turn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@reactflow/minimap': minor ---- - -add a new property "ariaLabel" to configure or remove the aria-label of the minimap component diff --git a/.changeset/silly-taxis-drop.md b/.changeset/silly-taxis-drop.md deleted file mode 100644 index e26bd820..00000000 --- a/.changeset/silly-taxis-drop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@reactflow/core': patch ---- - -Core: Always elevate zIndex when node is selected diff --git a/.changeset/small-walls-enjoy.md b/.changeset/small-walls-enjoy.md deleted file mode 100644 index 581e6a6f..00000000 --- a/.changeset/small-walls-enjoy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@reactflow/core': patch ---- - -fix(updatable-edge): fix disappearing connection line for loose flows diff --git a/.changeset/tender-pears-knock.md b/.changeset/tender-pears-knock.md deleted file mode 100644 index 697143ef..00000000 --- a/.changeset/tender-pears-knock.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@reactflow/core': patch ---- - -EdgeLabelRenderer: handle multiple instances on a page diff --git a/.changeset/young-zoos-push.md b/.changeset/young-zoos-push.md deleted file mode 100644 index a0854519..00000000 --- a/.changeset/young-zoos-push.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@reactflow/node-toolbar': major ---- - -Release a first version of the NodeToolbar component that renders a fixed element that can be attached to a node diff --git a/packages/background/CHANGELOG.md b/packages/background/CHANGELOG.md index 7d2cfbff..eff5abc5 100644 --- a/packages/background/CHANGELOG.md +++ b/packages/background/CHANGELOG.md @@ -1,5 +1,12 @@ # @reactflow/background +## 11.0.5 + +### Patch Changes + +- Updated dependencies [[`92cf497e`](https://github.com/wbkd/react-flow/commit/92cf497eb72f21af592a53f5af9770c9f1e6d940), [`98116d43`](https://github.com/wbkd/react-flow/commit/98116d431f9fcdcc9b23a5b606a94ec0740b64cd), [`a39224b3`](https://github.com/wbkd/react-flow/commit/a39224b3a80afbdb83fc4490dd5f4f2be23cd4dd), [`5e8b67dd`](https://github.com/wbkd/react-flow/commit/5e8b67dd41f9bb60dcd7f5d14cc34b42c970e967), [`2a1c7db6`](https://github.com/wbkd/react-flow/commit/2a1c7db6b27ac0f4f81dcef2d593f4753c4321c7)]: + - @reactflow/core@11.3.0 + ## 11.0.4 ### Patch Changes diff --git a/packages/background/package.json b/packages/background/package.json index b3575939..b821f6b8 100644 --- a/packages/background/package.json +++ b/packages/background/package.json @@ -1,6 +1,6 @@ { "name": "@reactflow/background", - "version": "11.0.4", + "version": "11.0.5", "description": "Background component with different variants for React Flow", "keywords": [ "react", diff --git a/packages/controls/CHANGELOG.md b/packages/controls/CHANGELOG.md index 92d15c50..729e65b7 100644 --- a/packages/controls/CHANGELOG.md +++ b/packages/controls/CHANGELOG.md @@ -1,5 +1,12 @@ # @reactflow/controls +## 11.0.5 + +### Patch Changes + +- Updated dependencies [[`92cf497e`](https://github.com/wbkd/react-flow/commit/92cf497eb72f21af592a53f5af9770c9f1e6d940), [`98116d43`](https://github.com/wbkd/react-flow/commit/98116d431f9fcdcc9b23a5b606a94ec0740b64cd), [`a39224b3`](https://github.com/wbkd/react-flow/commit/a39224b3a80afbdb83fc4490dd5f4f2be23cd4dd), [`5e8b67dd`](https://github.com/wbkd/react-flow/commit/5e8b67dd41f9bb60dcd7f5d14cc34b42c970e967), [`2a1c7db6`](https://github.com/wbkd/react-flow/commit/2a1c7db6b27ac0f4f81dcef2d593f4753c4321c7)]: + - @reactflow/core@11.3.0 + ## 11.0.4 ### Patch Changes diff --git a/packages/controls/package.json b/packages/controls/package.json index 882da3b0..3084073e 100644 --- a/packages/controls/package.json +++ b/packages/controls/package.json @@ -1,6 +1,6 @@ { "name": "@reactflow/controls", - "version": "11.0.4", + "version": "11.0.5", "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 3c8382a4..89c25608 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,15 +1,25 @@ # @reactflow/core +## 11.3.0 + +### Minor Changes + +- [#2563](https://github.com/wbkd/react-flow/pull/2563) [`98116d43`](https://github.com/wbkd/react-flow/commit/98116d431f9fcdcc9b23a5b606a94ec0740b64cd) Thanks [@chrtze](https://github.com/chrtze)! - Export a new component "NodeToolbar" that renders a fixed element attached to a node + +### Patch Changes + +- [#2561](https://github.com/wbkd/react-flow/pull/2561) [`92cf497e`](https://github.com/wbkd/react-flow/commit/92cf497eb72f21af592a53f5af9770c9f1e6d940) Thanks [@moklick](https://github.com/moklick)! - Fix multi selection and fitView when nodeOrigin is used +- [#2560](https://github.com/wbkd/react-flow/pull/2560) [`a39224b3`](https://github.com/wbkd/react-flow/commit/a39224b3a80afbdb83fc4490dd5f4f2be23cd4dd) Thanks [@neo](https://github.com/neo)! - Always elevate zIndex when node is selected +- [#2573](https://github.com/wbkd/react-flow/pull/2573) [`5e8b67dd`](https://github.com/wbkd/react-flow/commit/5e8b67dd41f9bb60dcd7f5d14cc34b42c970e967) Thanks [@moklick](https://github.com/moklick)! - Fix disappearing connection line for loose flows +- [#2558](https://github.com/wbkd/react-flow/pull/2558) [`2a1c7db6`](https://github.com/wbkd/react-flow/commit/2a1c7db6b27ac0f4f81dcef2d593f4753c4321c7) Thanks [@moklick](https://github.com/moklick)! - EdgeLabelRenderer: handle multiple instances on a page + ## 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 diff --git a/packages/core/package.json b/packages/core/package.json index aef400ec..3f112b18 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@reactflow/core", - "version": "11.2.0", + "version": "11.3.0", "description": "Core components and util functions of React Flow.", "keywords": [ "react", diff --git a/packages/minimap/CHANGELOG.md b/packages/minimap/CHANGELOG.md index c9660630..e23e9f1d 100644 --- a/packages/minimap/CHANGELOG.md +++ b/packages/minimap/CHANGELOG.md @@ -1,5 +1,17 @@ # @reactflow/minimap +## 11.2.0 + +### Minor Changes + +- [#2562](https://github.com/wbkd/react-flow/pull/2562) [`d745aa33`](https://github.com/wbkd/react-flow/commit/d745aa33fcd1333e12929c862f9a3d6de53f7179) Thanks [@moklick](https://github.com/moklick)! - Add maskStrokeColor and maskStrokeWidth props +- [#2545](https://github.com/wbkd/react-flow/pull/2545) [`8f63f751`](https://github.com/wbkd/react-flow/commit/8f63f751e302d3c935865760d2134350c31ab93f) Thanks [@chrtze](https://github.com/chrtze)! - add a new property "ariaLabel" to configure or remove the aria-label of the minimap component + +### Patch Changes + +- Updated dependencies [[`92cf497e`](https://github.com/wbkd/react-flow/commit/92cf497eb72f21af592a53f5af9770c9f1e6d940), [`98116d43`](https://github.com/wbkd/react-flow/commit/98116d431f9fcdcc9b23a5b606a94ec0740b64cd), [`a39224b3`](https://github.com/wbkd/react-flow/commit/a39224b3a80afbdb83fc4490dd5f4f2be23cd4dd), [`5e8b67dd`](https://github.com/wbkd/react-flow/commit/5e8b67dd41f9bb60dcd7f5d14cc34b42c970e967), [`2a1c7db6`](https://github.com/wbkd/react-flow/commit/2a1c7db6b27ac0f4f81dcef2d593f4753c4321c7)]: + - @reactflow/core@11.3.0 + ## 11.1.0 ### Minor Changes diff --git a/packages/minimap/package.json b/packages/minimap/package.json index 49dcd7cc..f2fc7d20 100644 --- a/packages/minimap/package.json +++ b/packages/minimap/package.json @@ -1,6 +1,6 @@ { "name": "@reactflow/minimap", - "version": "11.1.0", + "version": "11.2.0", "description": "Minimap component for React Flow.", "keywords": [ "react", diff --git a/packages/node-toolbar/CHANGELOG.md b/packages/node-toolbar/CHANGELOG.md index b1a03fb6..9854f032 100644 --- a/packages/node-toolbar/CHANGELOG.md +++ b/packages/node-toolbar/CHANGELOG.md @@ -1 +1,7 @@ -# @reactflow/node-toolbar \ No newline at end of file +# @reactflow/node-toolbar + +## 1.0.0 + +### Major Changes + +- [#2563](https://github.com/wbkd/react-flow/pull/2563) [`98116d43`](https://github.com/wbkd/react-flow/commit/98116d431f9fcdcc9b23a5b606a94ec0740b64cd) Thanks [@chrtze](https://github.com/chrtze)! - Export a new component "NodeToolbar" that renders a fixed element attached to a node diff --git a/packages/node-toolbar/package.json b/packages/node-toolbar/package.json index 9d9be308..ad15e213 100644 --- a/packages/node-toolbar/package.json +++ b/packages/node-toolbar/package.json @@ -1,6 +1,6 @@ { "name": "@reactflow/node-toolbar", - "version": "0.0.1", + "version": "1.0.0", "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 10b93714..10c2affd 100644 --- a/packages/reactflow/CHANGELOG.md +++ b/packages/reactflow/CHANGELOG.md @@ -1,15 +1,34 @@ # reactflow +## 11.3.0 + +### Minor Changes + +- [#2562](https://github.com/wbkd/react-flow/pull/2562) [`d745aa33`](https://github.com/wbkd/react-flow/commit/d745aa33fcd1333e12929c862f9a3d6de53f7179) Thanks [@moklick](https://github.com/moklick)! - Minimap: Add `maskStrokeColor` and `maskStrokeWidth` props +- [#2563](https://github.com/wbkd/react-flow/pull/2563) [`98116d43`](https://github.com/wbkd/react-flow/commit/98116d431f9fcdcc9b23a5b606a94ec0740b64cd) Thanks [@chrtze](https://github.com/chrtze)! - Core: Add `` component that renders a fixed element attached to a node +- [#2545](https://github.com/wbkd/react-flow/pull/2545) [`8f63f751`](https://github.com/wbkd/react-flow/commit/8f63f751e302d3c935865760d2134350c31ab93f) Thanks [@chrtze](https://github.com/chrtze)! - Minimap: Add `ariaLabel` prop to configure or remove the aria-label + +### Patch Changes + +- [#2561](https://github.com/wbkd/react-flow/pull/2561) [`92cf497e`](https://github.com/wbkd/react-flow/commit/92cf497eb72f21af592a53f5af9770c9f1e6d940) Thanks [@moklick](https://github.com/moklick)! - Core: Fix multi selection and fitView when nodeOrigin is used +- [#2560](https://github.com/wbkd/react-flow/pull/2560) [`a39224b3`](https://github.com/wbkd/react-flow/commit/a39224b3a80afbdb83fc4490dd5f4f2be23cd4dd) Thanks [@neo](https://github.com/neo)! - Core: Always elevate zIndex when node is selected +- [#2573](https://github.com/wbkd/react-flow/pull/2573) [`5e8b67dd`](https://github.com/wbkd/react-flow/commit/5e8b67dd41f9bb60dcd7f5d14cc34b42c970e967) Thanks [@moklick](https://github.com/moklick)! - Core: Fix disappearing connection line for loose flows +- [#2558](https://github.com/wbkd/react-flow/pull/2558) [`2a1c7db6`](https://github.com/wbkd/react-flow/commit/2a1c7db6b27ac0f4f81dcef2d593f4753c4321c7) Thanks [@moklick](https://github.com/moklick)! - Core: Handle multiple instances on a page for EdgeLabelRenderer + +- Updated dependencies [[`d745aa33`](https://github.com/wbkd/react-flow/commit/d745aa33fcd1333e12929c862f9a3d6de53f7179), [`92cf497e`](https://github.com/wbkd/react-flow/commit/92cf497eb72f21af592a53f5af9770c9f1e6d940), [`98116d43`](https://github.com/wbkd/react-flow/commit/98116d431f9fcdcc9b23a5b606a94ec0740b64cd), [`8f63f751`](https://github.com/wbkd/react-flow/commit/8f63f751e302d3c935865760d2134350c31ab93f), [`a39224b3`](https://github.com/wbkd/react-flow/commit/a39224b3a80afbdb83fc4490dd5f4f2be23cd4dd), [`5e8b67dd`](https://github.com/wbkd/react-flow/commit/5e8b67dd41f9bb60dcd7f5d14cc34b42c970e967), [`2a1c7db6`](https://github.com/wbkd/react-flow/commit/2a1c7db6b27ac0f4f81dcef2d593f4753c4321c7), [`c793433c`](https://github.com/wbkd/react-flow/commit/c793433cafc214281ae97c9a32f5ac2fe453c34f)]: + - @reactflow/minimap@11.2.0 + - @reactflow/core@11.3.0 + - @reactflow/node-toolbar@1.0.0 + - @reactflow/background@11.0.5 + - @reactflow/controls@11.0.5 + ## 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 diff --git a/packages/reactflow/package.json b/packages/reactflow/package.json index bc6095ef..1178425d 100644 --- a/packages/reactflow/package.json +++ b/packages/reactflow/package.json @@ -1,6 +1,6 @@ { "name": "reactflow", - "version": "11.2.0", + "version": "11.3.0", "description": "A highly customizable React library for building node-based editors and interactive flow charts", "keywords": [ "react",