diff --git a/examples/vite-app/src/examples/Basic/index.tsx b/examples/vite-app/src/examples/Basic/index.tsx
index 92077b35..dfba56ef 100644
--- a/examples/vite-app/src/examples/Basic/index.tsx
+++ b/examples/vite-app/src/examples/Basic/index.tsx
@@ -94,7 +94,7 @@ const BasicFlow = () => {
fitView
defaultEdgeOptions={defaultEdgeOptions}
selectNodesOnDrag={false}
- nodeOrigin={nodeOrigin}
+ // nodeOrigin={nodeOrigin}
>
diff --git a/packages/core/src/components/Nodes/NodeResizer.tsx b/packages/core/src/components/Nodes/NodeResizer.tsx
new file mode 100644
index 00000000..e5e2024c
--- /dev/null
+++ b/packages/core/src/components/Nodes/NodeResizer.tsx
@@ -0,0 +1,137 @@
+import { useRef, useEffect } from 'react';
+import { drag } from 'd3-drag';
+import { select } from 'd3-selection';
+import type { D3DragEvent, SubjectPosition } from 'd3';
+import { useStoreApi } from '../../hooks/useStore';
+import { Dimensions, Node, XYPosition } from '../../types';
+
+type NodeResizerProps = {
+ nodeId: string;
+};
+
+type ResizeHandleProps = {
+ nodeId: string;
+ invertX?: boolean;
+ invertY?: boolean;
+ enableX?: boolean;
+ enableY?: boolean;
+ top?: number | string;
+ left?: number | string;
+};
+
+type ResizeDragEvent = D3DragEvent;
+
+function ResizeHandle({
+ top = 0,
+ left = 0,
+ invertX = false,
+ invertY = false,
+ enableX = false,
+ enableY = false,
+ nodeId,
+}: ResizeHandleProps) {
+ const store = useStoreApi();
+ const resizeHandleRef = useRef(null);
+ const initialDimensionsRef = useRef({ width: 0, height: 0, x: 0, y: 0 });
+ const nodeElementRef = useRef(null);
+
+ useEffect(() => {
+ if (!resizeHandleRef.current) {
+ return;
+ }
+
+ const selection = select(resizeHandleRef.current);
+ const dragHandler = drag()
+ .on('start', () => {
+ const { transform, nodeInternals } = store.getState();
+ const node = nodeInternals.get(nodeId);
+ const nodeElement = document.querySelector(`.react-flow__node[data-id="${nodeId}"]`) as HTMLDivElement;
+ const bbox = nodeElement.getBoundingClientRect();
+ initialDimensionsRef.current = {
+ width: bbox.width / transform[2],
+ height: bbox.height / transform[2],
+ x: node?.position.x || 0,
+ y: node?.position.y || 0,
+ };
+ nodeElementRef.current = nodeElement;
+ })
+ .on('drag', (evt: ResizeDragEvent) => {
+ const { transform, updateNodePositions, nodeInternals } = store.getState();
+
+ const nodeEl = nodeElementRef.current;
+ const node = nodeInternals.get(nodeId);
+
+ if (nodeEl && node) {
+ const moveX = invertX ? -evt.x : evt.x;
+ const moveY = invertY ? -evt.y : evt.y;
+ const distX = enableX ? moveX - evt.subject.x : 0;
+ const distY = enableY ? moveY - evt.subject.y : 0;
+ const dragX = distX / transform[2];
+ const dragY = distY / transform[2];
+
+ const xPos = invertX ? initialDimensionsRef.current.x - dragX : initialDimensionsRef.current.x;
+ const yPos = invertY ? initialDimensionsRef.current.y - dragY : initialDimensionsRef.current.y;
+
+ console.log(dragX);
+
+ nodeEl.style.width = `${initialDimensionsRef.current.width + dragX}px`;
+ nodeEl.style.height = `${initialDimensionsRef.current.height + dragY}px`;
+ nodeEl.style.transform = `translate(${xPos}px, ${yPos}px)`;
+
+ if (invertX || invertY) {
+ const positionUpdate = { x: xPos, y: yPos };
+
+ updateNodePositions(
+ [
+ {
+ id: nodeId,
+ position: positionUpdate,
+ // positionAbsolute: node.positionAbsolute,
+ } as Node,
+ ],
+ true,
+ false
+ );
+ }
+ }
+ });
+
+ selection.call(dragHandler);
+
+ return () => {
+ selection.on('.drag', null);
+ };
+ }, [nodeId]);
+
+ return (
+
+ );
+}
+
+export default function NodeResizer({ nodeId }: NodeResizerProps) {
+ return (
+ <>
+
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/packages/core/src/components/Nodes/wrapNode.tsx b/packages/core/src/components/Nodes/wrapNode.tsx
index afb1f761..ba5daea6 100644
--- a/packages/core/src/components/Nodes/wrapNode.tsx
+++ b/packages/core/src/components/Nodes/wrapNode.tsx
@@ -2,6 +2,8 @@ import { useEffect, useRef, memo } from 'react';
import type { ComponentType, MouseEvent, KeyboardEvent } from 'react';
import cc from 'classcat';
+import NodeResizer from './NodeResizer';
+
import { useStoreApi } from '../../hooks/useStore';
import { Provider } from '../../contexts/NodeIdContext';
import { ARIA_NODE_DESC_KEY } from '../A11yDescriptions';
@@ -218,6 +220,7 @@ export default (NodeComponent: ComponentType) => {
zIndex={zIndex}
/>
+
);
};