108 lines
2.8 KiB
TypeScript
108 lines
2.8 KiB
TypeScript
"use client";
|
|
|
|
import { Handle, Position, type NodeProps } from "@xyflow/react";
|
|
import type { CampaignNodeType } from "@/lib/types/campaign";
|
|
import { useCampaignStore } from "@/lib/stores/campaign-store";
|
|
|
|
interface BaseNodeProps {
|
|
id: string;
|
|
label: string;
|
|
type: CampaignNodeType;
|
|
icon: React.ReactNode;
|
|
color: string;
|
|
hasInput?: boolean;
|
|
hasOutput?: boolean;
|
|
hasTrueOutput?: boolean;
|
|
hasFalseOutput?: boolean;
|
|
children?: React.ReactNode;
|
|
}
|
|
|
|
export function BaseNode({
|
|
id,
|
|
label,
|
|
icon,
|
|
color,
|
|
hasInput = true,
|
|
hasOutput = true,
|
|
hasTrueOutput = false,
|
|
hasFalseOutput = false,
|
|
children,
|
|
}: BaseNodeProps) {
|
|
const selectedNodeId = useCampaignStore((s) => s.selectedNodeId);
|
|
const isSelected = selectedNodeId === id;
|
|
|
|
return (
|
|
<div
|
|
className={`
|
|
relative min-w-[180px] rounded-xl border-2 bg-card shadow-lg
|
|
transition-all duration-150
|
|
${isSelected ? "ring-2 ring-primary/50 scale-[1.02]" : "hover:shadow-xl"}
|
|
`}
|
|
style={{ borderColor: color }}
|
|
>
|
|
{hasInput && (
|
|
<Handle
|
|
type="target"
|
|
position={Position.Left}
|
|
className="!w-3 !h-3 !border-2 !border-card"
|
|
style={{ background: color }}
|
|
/>
|
|
)}
|
|
|
|
<div className="px-4 py-3">
|
|
<div className="flex items-center gap-2 mb-1">
|
|
<div
|
|
className="flex items-center justify-center w-7 h-7 rounded-lg"
|
|
style={{ backgroundColor: color + "20", color }}
|
|
>
|
|
{icon}
|
|
</div>
|
|
<span className="text-sm font-semibold text-foreground">{label}</span>
|
|
</div>
|
|
{children && (
|
|
<div className="text-xs text-muted-foreground mt-1">{children}</div>
|
|
)}
|
|
</div>
|
|
|
|
{hasOutput && !hasTrueOutput && (
|
|
<Handle
|
|
type="source"
|
|
position={Position.Right}
|
|
className="!w-3 !h-3 !border-2 !border-card"
|
|
style={{ background: color }}
|
|
/>
|
|
)}
|
|
|
|
{hasTrueOutput && (
|
|
<>
|
|
<Handle
|
|
type="source"
|
|
position={Position.Right}
|
|
id="true"
|
|
className="!w-3 !h-3 !border-2 !border-card"
|
|
style={{ background: "#22c55e", top: "35%" }}
|
|
/>
|
|
<span className="absolute right-5 text-[10px] text-emerald-500 font-medium" style={{ top: "27%" }}>
|
|
true
|
|
</span>
|
|
</>
|
|
)}
|
|
|
|
{hasFalseOutput && (
|
|
<>
|
|
<Handle
|
|
type="source"
|
|
position={Position.Right}
|
|
id="false"
|
|
className="!w-3 !h-3 !border-2 !border-card"
|
|
style={{ background: "#ef4444", top: "65%" }}
|
|
/>
|
|
<span className="absolute right-5 text-[10px] text-red-500 font-medium" style={{ top: "62%" }}>
|
|
false
|
|
</span>
|
|
</>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|