This commit is contained in:
2026-03-07 10:43:26 +01:00
parent 7e42e74a1e
commit 59224880d2
7 changed files with 164 additions and 76 deletions

View File

@@ -1,8 +1,31 @@
import type { ComponentProps } from "react";
import type { ComponentProps, ReactNode } from "react";
import { NodeResizeControl } from "@xyflow/react";
import { Expand } from "lucide-react";
import { cn } from "@/lib/utils";
export function BaseNode({ className, ...props }: ComponentProps<"div">) {
export type BaseNodeProps = ComponentProps<"div"> & {
/** When true, shows a bottom-right resize handle. Requires nodeId when used inside React Flow. */
resizable?: boolean;
/** Node id for the resize control (required when resizable is true). */
nodeId?: string;
/** Connection handles (InputHandle, OutputHandle). Rendered outside the overflow layer so they stay visible. */
handles?: ReactNode;
};
export function BaseNode({
className,
style,
resizable,
nodeId,
handles,
children,
...props
}: BaseNodeProps) {
const appliedStyle = style
? { ...style, display: "flex", flexDirection: "column" as const }
: undefined;
return (
<div
className={cn(
@@ -17,9 +40,35 @@ export function BaseNode({ className, ...props }: ComponentProps<"div">) {
"[.react-flow\\_\\_node.selected_&]:shadow-lg",
className,
)}
style={appliedStyle}
tabIndex={0}
{...props}
/>
>
<div className="min-h-0 flex-1 flex flex-col overflow-hidden">{children}</div>
{resizable && nodeId && (
<div
className="flex shrink-0 items-center justify-end border-t border-border bg-muted/30 px-2 py-1.5"
data-slot="base-node-resize-footer"
>
<NodeResizeControl
nodeId={nodeId}
position="bottom-right"
minWidth={120}
minHeight={80}
className="!border-0 !bg-transparent !rounded-none"
style={{ padding: 0 }}
>
<span
className="flex size-6 items-center justify-center rounded border border-border bg-muted/80 text-muted-foreground shadow-sm nodrag nopan cursor-se-resize hover:bg-muted"
title="Drag to resize"
>
<Expand className="size-3.5" />
</span>
</NodeResizeControl>
</div>
)}
{handles}
</div>
);
}
@@ -35,7 +84,7 @@ export function BaseNodeHeader({
<header
{...props}
className={cn(
"mx-0 my-0 -mb-1 flex flex-row items-center justify-between gap-2 px-3 py-2",
"shrink-0 mx-0 my-0 -mb-1 flex flex-row items-center justify-between gap-2 px-3 py-2",
// Remove or modify these classes if you modify the padding in the
// `<BaseNode />` component.
className,
@@ -68,7 +117,7 @@ export function BaseNodeContent({
return (
<div
data-slot="base-node-content"
className={cn("flex flex-col gap-y-2 pt-1", className)}
className={cn("min-h-0 flex-1 flex flex-col gap-y-2 overflow-auto pt-1", className)}
{...props}
/>
);
@@ -79,7 +128,7 @@ export function BaseNodeFooter({ className, ...props }: ComponentProps<"div">) {
<div
data-slot="base-node-footer"
className={cn(
"flex flex-col items-center gap-y-2 border-t px-3 pt-2 pb-3",
"shrink-0 flex flex-col items-center gap-y-2 border-t px-3 pt-2 pb-3",
className,
)}
{...props}