From f776239ecbc9217135bc2e82a79c4463b8b2266d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=B6ller?= Date: Tue, 22 Nov 2022 16:13:23 +0100 Subject: [PATCH] feat(resize-node): example implementation of resize component --- .../vite-app/src/examples/Basic/index.tsx | 2 +- .../core/src/components/Nodes/NodeResizer.tsx | 137 ++++++++++++++++++ .../core/src/components/Nodes/wrapNode.tsx | 3 + 3 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 packages/core/src/components/Nodes/NodeResizer.tsx 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} /> +
); };