fix: node resize

This commit is contained in:
2026-03-11 00:38:15 +01:00
parent 2873f13875
commit c330713fa7
2 changed files with 44 additions and 19 deletions

View File

@@ -1,10 +1,14 @@
import type { ComponentProps, ReactNode } from "react";
import { NodeResizeControl } from "@xyflow/react";
import { NodeResizer } from "@xyflow/react";
import { cn } from "@/lib/utils";
/** Default min size for resizable nodes (used by NodeResizer). */
export const RESIZE_MIN_WIDTH = 120;
export const RESIZE_MIN_HEIGHT = 80;
export type BaseNodeProps = ComponentProps<"div"> & {
/** When true, shows a bottom-right resize handle. Requires nodeId when used inside React Flow. */
/** When true, shows resize handles on corners/edges (via NodeResizer). Requires nodeId when used inside React Flow. */
resizable?: boolean;
/** Node id for the resize control (required when resizable is true). */
nodeId?: string;
@@ -14,6 +18,8 @@ export type BaseNodeProps = ComponentProps<"div"> & {
dimensions?: { width: number; height: number };
/** When true, node is selected (from React Flow NodeProps). Used for visible selected state. */
selected?: boolean;
/** Optional min/max for NodeResizer. Omit to use RESIZE_MIN_WIDTH / RESIZE_MIN_HEIGHT. */
resizeConstraints?: { minWidth?: number; minHeight?: number; maxWidth?: number; maxHeight?: number };
};
export function BaseNode({
@@ -25,6 +31,7 @@ export function BaseNode({
handles,
children,
selected,
resizeConstraints,
...props
}: BaseNodeProps) {
const hasSize =
@@ -46,6 +53,9 @@ export function BaseNode({
? { ...style, display: "flex", flexDirection: "column" as const }
: undefined;
const minW = resizeConstraints?.minWidth ?? RESIZE_MIN_WIDTH;
const minH = resizeConstraints?.minHeight ?? RESIZE_MIN_HEIGHT;
return (
<div
className={cn(
@@ -66,19 +76,16 @@ export function BaseNode({
{children}
</div>
{resizable && nodeId && (
<NodeResizeControl
<NodeResizer
nodeId={nodeId}
position="bottom-right"
minWidth={120}
minHeight={80}
className={cn(
"!border-0 !bg-transparent !rounded-none !p-0",
"absolute bottom-0 right-0 w-8 h-8 cursor-se-resize select-none",
"nodrag nopan touch-none",
)}
style={{ margin: 0, touchAction: "none", userSelect: "none" }}
>
</NodeResizeControl>
isVisible={selected}
minWidth={minW}
minHeight={minH}
maxWidth={resizeConstraints?.maxWidth}
maxHeight={resizeConstraints?.maxHeight}
color="transparent"
handleClassName="base-node-resize-handle nodrag nopan"
/>
)}
{handles}
</div>

View File

@@ -142,18 +142,36 @@ body {
}
}
/* Resize control: snappy drag, no transition or selection delay */
.react-flow__resize-control.bottom.right {
cursor: se-resize;
/* NodeResizer: snappy drag; no border/background on handles; keep resize cursors. */
.react-flow__resize-control {
touch-action: none;
user-select: none;
}
.react-flow__resize-control.bottom.right,
.react-flow__resize-control.bottom.right * {
.react-flow__resize-control,
.react-flow__resize-control * {
transition: none;
}
/* Subtle resize handles so they dont compete with the selected-node ring (primary). Shown only when node is selected. */
/* Single bottom-right resize handle: no border, transparent so only the corner grab area remains. */
/* Handles: invisible (no border/background); cursor unchanged so resize cursors stay. */
.base-node-resize-handle,
.base-node-resize-handle * {
border: none !important;
background: transparent !important;
}
/* Resize cursor per position (keep cursor style change on hover). */
.react-flow__resize-control.top.left { cursor: nw-resize; }
.react-flow__resize-control.top.right { cursor: ne-resize; }
.react-flow__resize-control.bottom.left { cursor: sw-resize; }
.react-flow__resize-control.bottom.right { cursor: se-resize; }
.react-flow__resize-control.top { cursor: n-resize; }
.react-flow__resize-control.right { cursor: e-resize; }
.react-flow__resize-control.bottom { cursor: s-resize; }
.react-flow__resize-control.left { cursor: w-resize; }
/* Small helper for monospace pre output */
pre {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, 'Roboto Mono', 'Courier New', monospace;