From bee2f4aa057b884d2ace2c134e1871950792767e Mon Sep 17 00:00:00 2001 From: moklick Date: Thu, 17 Apr 2025 12:54:47 +0200 Subject: [PATCH 1/5] feat(xy-resizer): add resize direction #2986 --- .../NodeResizer/BottomRightResizer.tsx | 21 +++++++++++++++++++ .../react/src/examples/NodeResizer/index.tsx | 9 ++++++++ .../NodeResizer/NodeResizeControl.tsx | 2 ++ .../NodeResizer/types.ts | 6 ++++++ packages/system/src/xyresizer/XYResizer.ts | 18 +++++++++++++--- packages/system/src/xyresizer/types.ts | 6 ++++++ 6 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 examples/react/src/examples/NodeResizer/BottomRightResizer.tsx diff --git a/examples/react/src/examples/NodeResizer/BottomRightResizer.tsx b/examples/react/src/examples/NodeResizer/BottomRightResizer.tsx new file mode 100644 index 00000000..ca860fdf --- /dev/null +++ b/examples/react/src/examples/NodeResizer/BottomRightResizer.tsx @@ -0,0 +1,21 @@ +import { memo, FC } from 'react'; +import { Handle, Position, NodeProps, NodeResizeControl, ResizeControlVariant } from '@xyflow/react'; + +const CustomResizerNode: FC = ({ data }) => { + return ( + <> + + +
{data.label}
+ + + ); +}; + +export default memo(CustomResizerNode); diff --git a/examples/react/src/examples/NodeResizer/index.tsx b/examples/react/src/examples/NodeResizer/index.tsx index 397c1f22..ce6faf3b 100644 --- a/examples/react/src/examples/NodeResizer/index.tsx +++ b/examples/react/src/examples/NodeResizer/index.tsx @@ -15,12 +15,14 @@ import DefaultResizer from './DefaultResizer'; import CustomResizer from './CustomResizer'; import VerticalResizer from './VerticalResizer'; import HorizontalResizer from './HorizontalResizer'; +import BottomRightResizer from './BottomRightResizer'; const nodeTypes = { defaultResizer: DefaultResizer, customResizer: CustomResizer, verticalResizer: VerticalResizer, horizontalResizer: HorizontalResizer, + bottomRightResizer: BottomRightResizer, }; const nodeStyle = { @@ -166,6 +168,13 @@ const initialNodes: Node[] = [ expandParent: true, style: { ...nodeStyle }, }, + { + id: '6', + type: 'bottomRightResizer', + data: { label: 'Bottom Right with horizontal direction' }, + position: { x: 500, y: 500 }, + style: { ...nodeStyle }, + }, ]; const CustomNodeFlow = () => { diff --git a/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx b/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx index 0748ffa6..1b6393c6 100644 --- a/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx +++ b/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx @@ -32,6 +32,7 @@ function ResizeControl({ maxWidth = Number.MAX_VALUE, maxHeight = Number.MAX_VALUE, keepAspectRatio = false, + resizeDirection, shouldResize, onResizeStart, onResize, @@ -166,6 +167,7 @@ function ResizeControl({ maxHeight, }, keepAspectRatio, + resizeDirection, onResizeStart, onResize, onResizeEnd, diff --git a/packages/react/src/additional-components/NodeResizer/types.ts b/packages/react/src/additional-components/NodeResizer/types.ts index e34329b1..6ca29494 100644 --- a/packages/react/src/additional-components/NodeResizer/types.ts +++ b/packages/react/src/additional-components/NodeResizer/types.ts @@ -3,6 +3,7 @@ import type { ControlPosition, ControlLinePosition, ResizeControlVariant, + ResizeControlDirection, ShouldResize, OnResizeStart, OnResize, @@ -97,6 +98,11 @@ export type ResizeControlProps = Pick< * @example ResizeControlVariant.Handle, ResizeControlVariant.Line */ variant?: ResizeControlVariant; + /** + * The direction the user can resize the node. + * If not provided, the user can resize in any direction. + */ + resizeDirection?: ResizeControlDirection; className?: string; style?: CSSProperties; children?: ReactNode; diff --git a/packages/system/src/xyresizer/XYResizer.ts b/packages/system/src/xyresizer/XYResizer.ts index f4cd150b..03a0675e 100644 --- a/packages/system/src/xyresizer/XYResizer.ts +++ b/packages/system/src/xyresizer/XYResizer.ts @@ -12,7 +12,15 @@ import type { Transform, XYPosition, } from '../types'; -import type { OnResize, OnResizeEnd, OnResizeStart, ResizeDragEvent, ShouldResize, ControlPosition } from './types'; +import type { + OnResize, + OnResizeEnd, + OnResizeStart, + ResizeDragEvent, + ShouldResize, + ControlPosition, + ResizeControlDirection, +} from './types'; const initPrevValues = { width: 0, height: 0, x: 0, y: 0 }; @@ -60,6 +68,7 @@ type XYResizerUpdateParams = { maxHeight: number; }; keepAspectRatio: boolean; + resizeDirection?: ResizeControlDirection; onResizeStart: OnResizeStart | undefined; onResize: OnResize | undefined; onResizeEnd: OnResizeEnd | undefined; @@ -99,6 +108,7 @@ export function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }: X controlPosition, boundaries, keepAspectRatio, + resizeDirection, onResizeStart, onResize, onResizeEnd, @@ -251,8 +261,10 @@ export function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }: X } if (isWidthChange || isHeightChange) { - change.width = isWidthChange ? width : prevValues.width; - change.height = isHeightChange ? height : prevValues.height; + change.width = + isWidthChange && (!resizeDirection || resizeDirection === 'horizontal') ? width : prevValues.width; + change.height = + isHeightChange && (!resizeDirection || resizeDirection === 'vertical') ? height : prevValues.height; prevValues.width = change.width; prevValues.height = change.height; } diff --git a/packages/system/src/xyresizer/types.ts b/packages/system/src/xyresizer/types.ts index 34cad9e8..1d16eb3a 100644 --- a/packages/system/src/xyresizer/types.ts +++ b/packages/system/src/xyresizer/types.ts @@ -35,6 +35,12 @@ export enum ResizeControlVariant { Handle = 'handle', } +/** + * The direction the user can resize the node. + * @public + */ +export type ResizeControlDirection = 'horizontal' | 'vertical'; + export const XY_RESIZER_HANDLE_POSITIONS: ControlPosition[] = ['top-left', 'top-right', 'bottom-left', 'bottom-right']; export const XY_RESIZER_LINE_POSITIONS: ControlLinePosition[] = ['top', 'right', 'bottom', 'left']; From cda61ff243e8300350bd83514a50e0f1b5e401bb Mon Sep 17 00:00:00 2001 From: moklick Date: Thu, 17 Apr 2025 13:00:17 +0200 Subject: [PATCH 2/5] chore(ResizeControlLine): cleanup type --- packages/react/src/additional-components/NodeResizer/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/additional-components/NodeResizer/types.ts b/packages/react/src/additional-components/NodeResizer/types.ts index 6ca29494..e6dd9d36 100644 --- a/packages/react/src/additional-components/NodeResizer/types.ts +++ b/packages/react/src/additional-components/NodeResizer/types.ts @@ -111,6 +111,6 @@ export type ResizeControlProps = Pick< /** * @expand */ -export type ResizeControlLineProps = ResizeControlProps & { +export type ResizeControlLineProps = Omit & { position?: ControlLinePosition; }; From a7d10ffce5a0195471681980f97b1b5f6c448f35 Mon Sep 17 00:00:00 2001 From: moklick Date: Thu, 17 Apr 2025 13:01:48 +0200 Subject: [PATCH 3/5] chore(changeset): add --- .changeset/brave-cherries-hunt.md | 5 +++++ .changeset/two-pugs-drive.md | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 .changeset/brave-cherries-hunt.md create mode 100644 .changeset/two-pugs-drive.md diff --git a/.changeset/brave-cherries-hunt.md b/.changeset/brave-cherries-hunt.md new file mode 100644 index 00000000..952ac029 --- /dev/null +++ b/.changeset/brave-cherries-hunt.md @@ -0,0 +1,5 @@ +--- +'@xyflow/system': patch +--- + +add resizeDirection for XYResizer diff --git a/.changeset/two-pugs-drive.md b/.changeset/two-pugs-drive.md new file mode 100644 index 00000000..480c18f0 --- /dev/null +++ b/.changeset/two-pugs-drive.md @@ -0,0 +1,5 @@ +--- +'@xyflow/react': patch +--- + +Add resizeDirection prop for the NodeResizeControl component From 02a3916d47ea5afca30972d428f5935dc067bb4d Mon Sep 17 00:00:00 2001 From: Moritz Klack Date: Thu, 17 Apr 2025 13:04:19 +0200 Subject: [PATCH 4/5] chore(changeset): update --- .changeset/two-pugs-drive.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/two-pugs-drive.md b/.changeset/two-pugs-drive.md index 480c18f0..6d58d227 100644 --- a/.changeset/two-pugs-drive.md +++ b/.changeset/two-pugs-drive.md @@ -1,5 +1,5 @@ --- -'@xyflow/react': patch +'@xyflow/react': minor --- -Add resizeDirection prop for the NodeResizeControl component +Add `resizeDirection` prop for the `NodeResizeControl` component From cb6f0736dda6ef775b2129c2ff5e9f751a5886fd Mon Sep 17 00:00:00 2001 From: moklick Date: Thu, 17 Apr 2025 13:22:56 +0200 Subject: [PATCH 5/5] refactor(changes): allow set attributes to be width or height --- .../NodeResizer/NodeResizeControl.tsx | 3 ++- packages/react/src/utils/changes.ts | 8 ++++++-- packages/system/src/types/changes.ts | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx b/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx index 1b6393c6..beaeb527 100644 --- a/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx +++ b/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx @@ -118,11 +118,12 @@ function ResizeControl({ } if (change.width !== undefined && change.height !== undefined) { + const setAttributes = !resizeDirection ? true : resizeDirection === 'horizontal' ? 'width' : 'height'; const dimensionChange: NodeDimensionChange = { id, type: 'dimensions', resizing: true, - setAttributes: true, + setAttributes, dimensions: { width: change.width, height: change.height, diff --git a/packages/react/src/utils/changes.ts b/packages/react/src/utils/changes.ts index f1494bf4..f2d9bdd3 100644 --- a/packages/react/src/utils/changes.ts +++ b/packages/react/src/utils/changes.ts @@ -130,8 +130,12 @@ function applyChange(change: any, element: any): any { element.measured.height = change.dimensions.height; if (change.setAttributes) { - element.width = change.dimensions.width; - element.height = change.dimensions.height; + if (change.setAttributes === true || change.setAttributes === 'width') { + element.width = change.dimensions.width; + } + if (change.setAttributes === true || change.setAttributes === 'height') { + element.height = change.dimensions.height; + } } } diff --git a/packages/system/src/types/changes.ts b/packages/system/src/types/changes.ts index 8ab81a37..999f2cf2 100644 --- a/packages/system/src/types/changes.ts +++ b/packages/system/src/types/changes.ts @@ -7,7 +7,7 @@ export type NodeDimensionChange = { /* if this is true, the node is currently being resized via the NodeResizer */ resizing?: boolean; /* if this is true, we will set width and height of the node and not just the measured dimensions */ - setAttributes?: boolean; + setAttributes?: boolean | 'width' | 'height'; }; export type NodePositionChange = {