@@ -129,103 +97,6 @@ const NodeRenderer = (props: NodeRendererProps) => {
);
};
-const NodeComponentWrapper = memo(function NodeComponentWrapper(props: {
- id: string;
- nodeExtent: NodeRendererProps['nodeExtent'];
- nodeTypes: NodeRendererProps['nodeTypes'];
- nodeOrigin: NodeRendererProps['nodeOrigin'];
- onNodeClick: NodeRendererProps['onNodeClick'];
- onNodeMouseEnter: NodeRendererProps['onNodeMouseEnter'];
- onNodeMouseMove: NodeRendererProps['onNodeMouseMove'];
- onNodeMouseLeave: NodeRendererProps['onNodeMouseLeave'];
- onNodeContextMenu: NodeRendererProps['onNodeContextMenu'];
- onNodeDoubleClick: NodeRendererProps['onNodeDoubleClick'];
- noDragClassName: NodeRendererProps['noDragClassName'];
- noPanClassName: NodeRendererProps['noPanClassName'];
- rfId: NodeRendererProps['rfId'];
- disableKeyboardA11y: NodeRendererProps['disableKeyboardA11y'];
- resizeObserver: ResizeObserver | null;
- nodesDraggable: boolean;
- nodesConnectable: boolean;
- nodesFocusable: boolean;
- elementsSelectable: boolean;
- onError: ReactFlowState['onError'];
-}) {
- const node = useStore((s) => s.nodeLookup.get(props.id));
- if (!node) return null;
-
- let nodeType = node.type || 'default';
-
- if (!props.nodeTypes[nodeType]) {
- props.onError?.('003', errorMessages['error003'](nodeType));
-
- nodeType = 'default';
- }
-
- const NodeComponent = (props.nodeTypes[nodeType] || props.nodeTypes.default) as ComponentType
;
- const isDraggable = !!(node.draggable || (props.nodesDraggable && typeof node.draggable === 'undefined'));
- const isSelectable = !!(node.selectable || (props.elementsSelectable && typeof node.selectable === 'undefined'));
- const isConnectable = !!(node.connectable || (props.nodesConnectable && typeof node.connectable === 'undefined'));
- const isFocusable = !!(node.focusable || (props.nodesFocusable && typeof node.focusable === 'undefined'));
-
- const clampedPosition = props.nodeExtent
- ? clampPosition(node.computed?.positionAbsolute, props.nodeExtent)
- : node.computed?.positionAbsolute;
-
- const posX = clampedPosition?.x ?? 0;
- const posY = clampedPosition?.y ?? 0;
- const posOrigin = getPositionWithOrigin({
- x: posX,
- y: posY,
- width: node.computed?.width ?? node.width ?? 0,
- height: node.computed?.height ?? node.height ?? 0,
- origin: node.origin || props.nodeOrigin,
- });
- const initialized = (!!node.computed?.width && !!node.computed?.height) || (!!node.width && !!node.height);
-
- return (
-
- );
-});
-
NodeRenderer.displayName = 'NodeRenderer';
export default memo(NodeRenderer);
diff --git a/packages/react/src/container/NodeRenderer/useResizeObserver.ts b/packages/react/src/container/NodeRenderer/useResizeObserver.ts
new file mode 100644
index 00000000..d109d798
--- /dev/null
+++ b/packages/react/src/container/NodeRenderer/useResizeObserver.ts
@@ -0,0 +1,44 @@
+import { useEffect, useMemo, useRef } from 'react';
+
+import { ReactFlowState } from '../../types';
+import { useStore } from '../../hooks/useStore';
+
+const selector = (s: ReactFlowState) => s.updateNodeDimensions;
+
+export default function useResizeObserver() {
+ const updateNodeDimensions = useStore(selector);
+ const resizeObserverRef = useRef();
+
+ const resizeObserver = useMemo(() => {
+ if (typeof ResizeObserver === 'undefined') {
+ return null;
+ }
+
+ const observer = new ResizeObserver((entries: ResizeObserverEntry[]) => {
+ const updates = new Map();
+
+ entries.forEach((entry: ResizeObserverEntry) => {
+ const id = entry.target.getAttribute('data-id') as string;
+ updates.set(id, {
+ id,
+ nodeElement: entry.target as HTMLDivElement,
+ forceUpdate: true,
+ });
+ });
+
+ updateNodeDimensions(updates);
+ });
+
+ resizeObserverRef.current = observer;
+
+ return observer;
+ }, []);
+
+ useEffect(() => {
+ return () => {
+ resizeObserverRef?.current?.disconnect();
+ };
+ }, []);
+
+ return resizeObserver;
+}
diff --git a/packages/react/src/hooks/useVisibleNodes.ts b/packages/react/src/hooks/useVisibleNodes.ts
index 56e14674..2f67af02 100644
--- a/packages/react/src/hooks/useVisibleNodes.ts
+++ b/packages/react/src/hooks/useVisibleNodes.ts
@@ -1,31 +1,20 @@
import { getNodesInside } from '@xyflow/system';
-import { createSelector } from 'reselect';
import { shallow } from 'zustand/shallow';
import { useStore } from '../hooks/useStore';
import type { Node, ReactFlowState } from '../types';
+import { useCallback } from 'react';
-const visibleNodeIdsSelector = createSelector(
- [
- (s: ReactFlowState) => s.nodes,
- (s: ReactFlowState) => s.width,
- (s: ReactFlowState) => s.height,
- (s: ReactFlowState) => s.transform,
- (_s: ReactFlowState, onlyRenderVisibleNodes: boolean) => onlyRenderVisibleNodes,
- ],
- (nodes, width, height, transform, onlyRenderVisible) =>
- onlyRenderVisible
- ? getNodesInside(nodes, { x: 0, y: 0, width, height }, transform, true).map((node) => node.id)
- : nodes.map((node) => node.id),
- {
- memoizeOptions: {
- resultEqualityCheck: shallow,
- },
- }
-);
+const selector = (onlyRenderVisible: boolean) => (s: ReactFlowState) => {
+ return onlyRenderVisible
+ ? getNodesInside(s.nodes, { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true).map(
+ (node) => node.id
+ )
+ : Array.from(s.nodeLookup.keys());
+};
function useVisibleNodeIds(onlyRenderVisible: boolean) {
- const nodeIds = useStore((s: ReactFlowState) => visibleNodeIdsSelector(s, onlyRenderVisible));
+ const nodeIds = useStore(useCallback(selector(onlyRenderVisible), [onlyRenderVisible]), shallow);
return nodeIds;
}
diff --git a/packages/react/src/types/nodes.ts b/packages/react/src/types/nodes.ts
index 29548666..b1960f64 100644
--- a/packages/react/src/types/nodes.ts
+++ b/packages/react/src/types/nodes.ts
@@ -1,5 +1,5 @@
import type { CSSProperties, MouseEvent as ReactMouseEvent } from 'react';
-import type { NodeBase, XYPosition } from '@xyflow/system';
+import type { NodeBase } from '@xyflow/system';
export type Node = NodeBase<
NodeData,
@@ -20,11 +20,10 @@ export type WrapNodeProps = Pick<
> &
Required, 'selected' | 'type' | 'zIndex'>> & {
isConnectable: boolean;
- xPos: number;
- yPos: number;
xPosOrigin: number;
yPosOrigin: number;
- positionAbsolute: XYPosition;
+ positionAbsoluteX: number;
+ positionAbsoluteY: number;
initialized: boolean;
isSelectable: boolean;
isDraggable: boolean;
diff --git a/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte b/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte
index d992c5a3..ae894066 100644
--- a/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte
+++ b/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte
@@ -191,7 +191,8 @@
{dragging}
{dragHandle}
isConnectable={connectable}
- positionAbsolute={{ x: positionX, y: positionY }}
+ positionAbsoluteX={positionX}
+ positionAbsoluteY={positionY}
{width}
{height}
/>
diff --git a/packages/system/src/types/nodes.ts b/packages/system/src/types/nodes.ts
index 3839880c..74b294fe 100644
--- a/packages/system/src/types/nodes.ts
+++ b/packages/system/src/types/nodes.ts
@@ -56,7 +56,8 @@ export type NodeProps = {
selected: NodeBase['selected'];
isConnectable: NodeBase['connectable'];
zIndex: NodeBase['zIndex'];
- positionAbsolute: XYPosition;
+ positionAbsoluteX: number;
+ positionAbsoluteY: number;
width?: number;
height?: number;
dragging: boolean;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7e1fcd6e..b39d982e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -235,9 +235,6 @@ importers:
react-dom:
specifier: '>=17'
version: registry.npmjs.org/react-dom@18.2.0(react@18.2.0)
- reselect:
- specifier: ^4.1.8
- version: registry.npmjs.org/reselect@4.1.8
zustand:
specifier: ^4.4.0
version: registry.npmjs.org/zustand@4.4.1(@types/react@18.2.24)(react@18.2.0)
@@ -10036,12 +10033,6 @@ packages:
version: 1.0.0
dev: true
- registry.npmjs.org/reselect@4.1.8:
- resolution: {integrity: sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz}
- name: reselect
- version: 4.1.8
- dev: false
-
registry.npmjs.org/resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz}
name: resolve-from