feat(canvas): add shared glowing canvas handle
This commit is contained in:
85
components/canvas/canvas-handle.tsx
Normal file
85
components/canvas/canvas-handle.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
"use client";
|
||||
|
||||
import { Handle, useConnection } from "@xyflow/react";
|
||||
|
||||
import { HANDLE_SNAP_RADIUS_PX } from "@/components/canvas/canvas-connection-magnetism";
|
||||
import { useCanvasConnectionMagnetism } from "@/components/canvas/canvas-connection-magnetism-context";
|
||||
import {
|
||||
canvasHandleAccentColor,
|
||||
canvasHandleAccentColorWithAlpha,
|
||||
} from "@/lib/canvas-utils";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type ReactFlowHandleProps = React.ComponentProps<typeof Handle>;
|
||||
|
||||
type CanvasHandleProps = Omit<ReactFlowHandleProps, "id"> & {
|
||||
nodeId: string;
|
||||
nodeType?: string;
|
||||
id?: string;
|
||||
};
|
||||
|
||||
function normalizeHandleId(value: string | undefined): string | undefined {
|
||||
return value === "" ? undefined : value;
|
||||
}
|
||||
|
||||
export default function CanvasHandle({
|
||||
nodeId,
|
||||
nodeType,
|
||||
id,
|
||||
type,
|
||||
className,
|
||||
style,
|
||||
...rest
|
||||
}: CanvasHandleProps) {
|
||||
const connection = useConnection();
|
||||
const { activeTarget } = useCanvasConnectionMagnetism();
|
||||
|
||||
const handleId = normalizeHandleId(id);
|
||||
const targetHandleId = normalizeHandleId(activeTarget?.handleId);
|
||||
const isActiveTarget =
|
||||
connection.inProgress &&
|
||||
activeTarget !== null &&
|
||||
activeTarget.nodeId === nodeId &&
|
||||
activeTarget.handleType === type &&
|
||||
targetHandleId === handleId;
|
||||
|
||||
const glowState: "idle" | "near" | "snapped" = isActiveTarget
|
||||
? activeTarget.distancePx <= HANDLE_SNAP_RADIUS_PX
|
||||
? "snapped"
|
||||
: "near"
|
||||
: "idle";
|
||||
|
||||
const accentColor = canvasHandleAccentColor({
|
||||
nodeType,
|
||||
handleId,
|
||||
handleType: type,
|
||||
});
|
||||
const glowAlpha = glowState === "snapped" ? 0.62 : glowState === "near" ? 0.4 : 0;
|
||||
const ringAlpha = glowState === "snapped" ? 0.34 : glowState === "near" ? 0.2 : 0;
|
||||
const glowSize = glowState === "snapped" ? 14 : glowState === "near" ? 10 : 0;
|
||||
const ringSize = glowState === "snapped" ? 6 : glowState === "near" ? 4 : 0;
|
||||
|
||||
return (
|
||||
<Handle
|
||||
{...rest}
|
||||
id={id}
|
||||
type={type}
|
||||
className={cn(
|
||||
"!h-3 !w-3 !border-2 !border-background transition-[box-shadow,background-color] duration-150",
|
||||
className,
|
||||
)}
|
||||
style={{
|
||||
...style,
|
||||
backgroundColor: accentColor,
|
||||
boxShadow:
|
||||
glowState === "idle"
|
||||
? undefined
|
||||
: `0 0 0 ${ringSize}px ${canvasHandleAccentColorWithAlpha({ nodeType, handleId, handleType: type }, ringAlpha)}, 0 0 ${glowSize}px ${canvasHandleAccentColorWithAlpha({ nodeType, handleId, handleType: type }, glowAlpha)}`,
|
||||
}}
|
||||
data-node-id={nodeId}
|
||||
data-handle-id={id ?? ""}
|
||||
data-handle-type={type}
|
||||
data-glow-state={glowState}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user