diff --git a/examples/react/src/examples/Layouting/index.tsx b/examples/react/src/examples/Layouting/index.tsx index 7383e41e..2371b503 100644 --- a/examples/react/src/examples/Layouting/index.tsx +++ b/examples/react/src/examples/Layouting/index.tsx @@ -19,6 +19,7 @@ import { import initialItems from './initial-elements'; import styles from './layouting.module.css'; +import ReactFlowDevTools from '../DevTools/DevTools'; const dagreGraph = new dagre.graphlib.Graph(); dagreGraph.setDefaultEdgeLabel(() => ({})); @@ -98,6 +99,7 @@ const LayoutFlow = () => { onEdgesChange={onEdgesChange} > + diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index acba7b99..2fd7bf5d 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -5,6 +5,9 @@ ## Patch changes - fix hidden nodes +- use `direction=ltr` for outer wrapper to support rtl sites +- allow pinch zoom even if `preventScrolling=false` +- export node and edge change related types ## 12.0.0-next.13 diff --git a/packages/react/src/components/NodeWrapper/index.tsx b/packages/react/src/components/NodeWrapper/index.tsx index e081a6c7..18c2d314 100644 --- a/packages/react/src/components/NodeWrapper/index.tsx +++ b/packages/react/src/components/NodeWrapper/index.tsx @@ -123,7 +123,7 @@ export function NodeWrapper({ if (targetPosChanged) { prevTargetPosition.current = node.targetPosition; } - store.getState().updateNodeDimensions(new Map([[id, { id, nodeElement: nodeRef.current, force: true }]])); + store.getState().updateNodeInternals(new Map([[id, { id, nodeElement: nodeRef.current, force: true }]])); } }, [id, nodeType, node.sourcePosition, node.targetPosition]); diff --git a/packages/react/src/container/NodeRenderer/useResizeObserver.ts b/packages/react/src/container/NodeRenderer/useResizeObserver.ts index 82c079bd..fa7d501f 100644 --- a/packages/react/src/container/NodeRenderer/useResizeObserver.ts +++ b/packages/react/src/container/NodeRenderer/useResizeObserver.ts @@ -2,12 +2,12 @@ import { useEffect, useMemo, useRef } from 'react'; import { ReactFlowState } from '../../types'; import { useStore } from '../../hooks/useStore'; -import { NodeDimensionUpdate } from '@xyflow/system'; +import { InternalNodeUpdate } from '@xyflow/system'; -const selector = (s: ReactFlowState) => s.updateNodeDimensions; +const selector = (s: ReactFlowState) => s.updateNodeInternals; export function useResizeObserver() { - const updateNodeDimensions = useStore(selector); + const updateNodeInternals = useStore(selector); const resizeObserverRef = useRef(); const resizeObserver = useMemo(() => { @@ -16,7 +16,7 @@ export function useResizeObserver() { } const observer = new ResizeObserver((entries: ResizeObserverEntry[]) => { - const updates = new Map(); + const updates = new Map(); entries.forEach((entry: ResizeObserverEntry) => { const id = entry.target.getAttribute('data-id') as string; @@ -26,7 +26,7 @@ export function useResizeObserver() { }); }); - updateNodeDimensions(updates); + updateNodeInternals(updates); }); resizeObserverRef.current = observer; diff --git a/packages/react/src/hooks/useUpdateNodeInternals.ts b/packages/react/src/hooks/useUpdateNodeInternals.ts index b156d506..63965d72 100644 --- a/packages/react/src/hooks/useUpdateNodeInternals.ts +++ b/packages/react/src/hooks/useUpdateNodeInternals.ts @@ -1,5 +1,5 @@ import { useCallback } from 'react'; -import type { UpdateNodeInternals, NodeDimensionUpdate } from '@xyflow/system'; +import type { UpdateNodeInternals, InternalNodeUpdate } from '@xyflow/system'; import { useStoreApi } from '../hooks/useStore'; @@ -13,9 +13,9 @@ export function useUpdateNodeInternals(): UpdateNodeInternals { const store = useStoreApi(); return useCallback((id: string | string[]) => { - const { domNode, updateNodeDimensions } = store.getState(); + const { domNode, updateNodeInternals } = store.getState(); const updateIds = Array.isArray(id) ? id : [id]; - const updates = new Map(); + const updates = new Map(); updateIds.forEach((updateId) => { const nodeElement = domNode?.querySelector(`.react-flow__node[data-id="${updateId}"]`) as HTMLDivElement; @@ -25,6 +25,6 @@ export function useUpdateNodeInternals(): UpdateNodeInternals { } }); - requestAnimationFrame(() => updateNodeDimensions(updates)); + requestAnimationFrame(() => updateNodeInternals(updates)); }, []); } diff --git a/packages/react/src/store/index.ts b/packages/react/src/store/index.ts index 492d6186..7ce263c9 100644 --- a/packages/react/src/store/index.ts +++ b/packages/react/src/store/index.ts @@ -5,7 +5,7 @@ import { adoptUserNodes, updateAbsolutePositions, panBy as panBySystem, - updateNodeDimensions as updateNodeDimensionsSystem, + updateNodeInternals as updateNodeInternalsSystem, updateConnectionLookup, handleParentExpand, NodeChange, @@ -71,7 +71,7 @@ const createRFStore = ({ // Every node gets registerd at a ResizeObserver. Whenever a node // changes its dimensions, this function is called to measure the // new dimensions and update the nodes. - updateNodeDimensions: (updates) => { + updateNodeInternals: (updates) => { const { onNodesChange, fitView, @@ -84,9 +84,9 @@ const createRFStore = ({ debug, } = get(); - const changes = updateNodeDimensionsSystem(updates, nodeLookup, domNode, nodeOrigin); + const { changes, updatedInternals } = updateNodeInternalsSystem(updates, nodeLookup, domNode, nodeOrigin); - if (changes.length === 0) { + if (!updatedInternals) { return; } diff --git a/packages/react/src/types/store.ts b/packages/react/src/types/store.ts index 416f56f3..397ca6f1 100644 --- a/packages/react/src/types/store.ts +++ b/packages/react/src/types/store.ts @@ -2,7 +2,7 @@ import { ConnectionMode, type ConnectionStatus, type CoordinateExtent, - type NodeDimensionUpdate, + type InternalNodeUpdate, type UpdateNodePositions, type NodeOrigin, type OnConnect, @@ -154,7 +154,7 @@ export type ReactFlowActions = { setNodes: (nodes: NodeType[]) => void; setEdges: (edges: EdgeType[]) => void; setDefaultNodesAndEdges: (nodes?: NodeType[], edges?: EdgeType[]) => void; - updateNodeDimensions: (updates: Map) => void; + updateNodeInternals: (updates: Map) => void; updateNodePositions: UpdateNodePositions; resetSelectedElements: () => void; unselectNodesAndEdges: (params?: UnselectNodesAndEdgesParams) => void; diff --git a/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte b/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte index a466d0ad..384b3369 100644 --- a/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte +++ b/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte @@ -52,7 +52,7 @@ nodeDragThreshold, selectNodesOnDrag, handleNodeSelection, - updateNodeDimensions + updateNodeInternals } = store; let nodeRef: HTMLDivElement; @@ -97,7 +97,7 @@ if (doUpdate) { requestAnimationFrame(() => - updateNodeDimensions( + updateNodeInternals( new Map([ [ id, diff --git a/packages/svelte/src/lib/container/NodeRenderer/NodeRenderer.svelte b/packages/svelte/src/lib/container/NodeRenderer/NodeRenderer.svelte index ae400eb5..c0b8165e 100644 --- a/packages/svelte/src/lib/container/NodeRenderer/NodeRenderer.svelte +++ b/packages/svelte/src/lib/container/NodeRenderer/NodeRenderer.svelte @@ -10,7 +10,7 @@ nodesDraggable, nodesConnectable, elementsSelectable, - updateNodeDimensions + updateNodeInternals } = useStore(); const resizeObserver: ResizeObserver | null = @@ -29,7 +29,7 @@ }); }); - updateNodeDimensions(updates); + updateNodeInternals(updates); }); onDestroy(() => { diff --git a/packages/svelte/src/lib/hooks/useUpdateNodeInternals.ts b/packages/svelte/src/lib/hooks/useUpdateNodeInternals.ts index 45a203eb..cbb240ab 100644 --- a/packages/svelte/src/lib/hooks/useUpdateNodeInternals.ts +++ b/packages/svelte/src/lib/hooks/useUpdateNodeInternals.ts @@ -10,7 +10,7 @@ import { useStore } from '$lib/store'; * @returns function for updating node internals */ export function useUpdateNodeInternals(): UpdateNodeInternals { - const { domNode, updateNodeDimensions } = useStore(); + const { domNode, updateNodeInternals } = useStore(); // @todo: do we want to add this to system? const updateInternals = (id: string | string[]) => { @@ -27,7 +27,7 @@ export function useUpdateNodeInternals(): UpdateNodeInternals { } }); - requestAnimationFrame(() => updateNodeDimensions(updates)); + requestAnimationFrame(() => updateNodeInternals(updates)); }; return updateInternals; diff --git a/packages/svelte/src/lib/store/index.ts b/packages/svelte/src/lib/store/index.ts index 2f6266eb..c808d2af 100644 --- a/packages/svelte/src/lib/store/index.ts +++ b/packages/svelte/src/lib/store/index.ts @@ -5,10 +5,10 @@ import { fitView as fitViewUtil, getElementsToRemove, panBy as panBySystem, - updateNodeDimensions as updateNodeDimensionsSystem, + updateNodeInternals as updateNodeInternalsSystem, addEdge as addEdgeUtil, type UpdateNodePositions, - type NodeDimensionUpdate, + type InternalNodeUpdate, type ViewportHelperFunctionOptions, type Connection, type XYPosition, @@ -78,16 +78,16 @@ export function createStore({ store.nodes.update((nds) => nds); }; - function updateNodeDimensions(updates: Map) { + function updateNodeInternals(updates: Map) { const nodeLookup = get(store.nodeLookup); - const changes = updateNodeDimensionsSystem( + const { changes, updatedInternals } = updateNodeInternalsSystem( updates, nodeLookup, get(store.domNode), get(store.nodeOrigin) ); - if (!changes) { + if (!updatedInternals) { return; } @@ -400,7 +400,7 @@ export function createStore({ setEdgeTypes, addEdge, updateNodePositions, - updateNodeDimensions, + updateNodeInternals, zoomIn, zoomOut, fitView: (options?: FitViewOptions) => fitView(options), diff --git a/packages/svelte/src/lib/store/types.ts b/packages/svelte/src/lib/store/types.ts index 8d41d63f..d87ca907 100644 --- a/packages/svelte/src/lib/store/types.ts +++ b/packages/svelte/src/lib/store/types.ts @@ -1,6 +1,6 @@ import type { Writable } from 'svelte/store'; import type { - NodeDimensionUpdate, + InternalNodeUpdate, XYPosition, ViewportHelperFunctionOptions, Connection, @@ -27,7 +27,7 @@ export type SvelteFlowStoreActions = { setTranslateExtent: (extent: CoordinateExtent) => void; fitView: (options?: FitViewOptions) => boolean; updateNodePositions: UpdateNodePositions; - updateNodeDimensions: (updates: Map) => void; + updateNodeInternals: (updates: Map) => void; unselectNodesAndEdges: (params?: { nodes?: Node[]; edges?: Edge[] }) => void; addSelectedNodes: (ids: string[]) => void; addSelectedEdges: (ids: string[]) => void; diff --git a/packages/system/src/types/nodes.ts b/packages/system/src/types/nodes.ts index c8c85a55..d802dad8 100644 --- a/packages/system/src/types/nodes.ts +++ b/packages/system/src/types/nodes.ts @@ -105,7 +105,7 @@ export type NodeHandleBounds = { target: HandleElement[] | null; }; -export type NodeDimensionUpdate = { +export type InternalNodeUpdate = { id: string; nodeElement: HTMLDivElement; force?: boolean; diff --git a/packages/system/src/utils/store.ts b/packages/system/src/utils/store.ts index 38a01b76..0d7f2645 100644 --- a/packages/system/src/utils/store.ts +++ b/packages/system/src/utils/store.ts @@ -1,7 +1,7 @@ import { NodeBase, CoordinateExtent, - NodeDimensionUpdate, + InternalNodeUpdate, NodeOrigin, PanZoomInstance, Transform, @@ -11,7 +11,6 @@ import { EdgeBase, EdgeLookup, InternalNodeBase, - NodeChange, NodeLookup, Rect, NodeDimensionChange, @@ -213,16 +212,17 @@ export function handleParentExpand( return changes; } -export function updateNodeDimensions( - updates: Map, +export function updateNodeInternals( + updates: Map, nodeLookup: Map, domNode: HTMLElement | null, nodeOrigin?: NodeOrigin -): (NodeDimensionChange | NodePositionChange)[] { +): { changes: (NodeDimensionChange | NodePositionChange)[]; updatedInternals: boolean } { const viewportNode = domNode?.querySelector('.xyflow__viewport'); + let updatedInternals = false; if (!viewportNode) { - return []; + return { changes: [], updatedInternals }; } const changes: (NodeDimensionChange | NodePositionChange)[] = []; @@ -242,24 +242,20 @@ export function updateNodeDimensions( handleBounds: undefined, }, }); + updatedInternals = true; } else if (node) { const dimensions = getDimensions(update.nodeElement); + const dimensionChanged = node.measured.width !== dimensions.width || node.measured.height !== dimensions.height; const doUpdate = !!( dimensions.width && dimensions.height && - (node.measured?.width !== dimensions.width || - node.measured?.height !== dimensions.height || - !node.internals.handleBounds || - update.force) + (dimensionChanged || !node.internals.handleBounds || update.force) ); if (doUpdate) { const newNode = { ...node, - measured: { - ...node.measured, - ...dimensions, - }, + measured: dimensions, internals: { ...node.internals, handleBounds: { @@ -270,15 +266,18 @@ export function updateNodeDimensions( }; nodeLookup.set(node.id, newNode); + updatedInternals = true; - changes.push({ - id: newNode.id, - type: 'dimensions', - dimensions, - }); + if (dimensionChanged) { + changes.push({ + id: newNode.id, + type: 'dimensions', + dimensions, + }); - if (newNode.expandParent) { - triggerChangeNodes.push(newNode); + if (newNode.expandParent) { + triggerChangeNodes.push(newNode); + } } } } @@ -289,7 +288,7 @@ export function updateNodeDimensions( changes.push(...parentExpandChanges); } - return changes; + return { changes, updatedInternals }; } export function panBy({