91 lines
3.1 KiB
TypeScript
91 lines
3.1 KiB
TypeScript
"use client";
|
|
|
|
import { memo, useCallback, type MouseEvent } from "react";
|
|
import { Handle, Position, type NodeProps } from "reactflow";
|
|
import type { GraphNodeData } from "@/types/mindmap";
|
|
|
|
function MindmapNodeCard({ data }: NodeProps<GraphNodeData>) {
|
|
const isRoot = data.level === 0;
|
|
const canOpenChat = typeof data.onOpenChat === "function";
|
|
|
|
const handleToggleClick = useCallback(
|
|
(event: MouseEvent<HTMLButtonElement>) => {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
data.onToggle?.();
|
|
},
|
|
[data],
|
|
);
|
|
|
|
const handleContentDoubleClick = useCallback(
|
|
(event: MouseEvent<HTMLButtonElement>) => {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
data.onOpenChat?.();
|
|
},
|
|
[data],
|
|
);
|
|
|
|
return (
|
|
<div
|
|
className={`relative flex h-[84px] w-[220px] items-center justify-center rounded-[18px] border px-5 py-4 ${
|
|
isRoot
|
|
? "border-blue-500 bg-blue-600 text-white shadow-[0_18px_40px_rgba(37,99,235,0.24)]"
|
|
: "border-slate-200 bg-white text-slate-900 shadow-[0_12px_28px_rgba(15,23,42,0.08)]"
|
|
} cursor-default`}
|
|
>
|
|
<Handle
|
|
type="target"
|
|
position={Position.Left}
|
|
className={`!pointer-events-none !left-[-7px] !h-3 !w-3 !border ${
|
|
isRoot ? "!border-blue-200 !bg-blue-100" : "!border-slate-300 !bg-white"
|
|
}`}
|
|
/>
|
|
|
|
<button
|
|
type="button"
|
|
onDoubleClick={handleContentDoubleClick}
|
|
disabled={!canOpenChat}
|
|
title={canOpenChat ? "双击发起聊天" : undefined}
|
|
className={`nodrag nopan max-w-[156px] select-none rounded-xl px-2 py-1 text-center text-sm font-semibold leading-6 transition-colors ${
|
|
canOpenChat
|
|
? isRoot
|
|
? "cursor-pointer hover:bg-white/10"
|
|
: "cursor-pointer hover:bg-slate-50"
|
|
: "cursor-default"
|
|
} disabled:pointer-events-none disabled:opacity-100`}
|
|
>
|
|
{data.label}
|
|
</button>
|
|
|
|
{data.hasChildren ? (
|
|
<>
|
|
<Handle
|
|
type="source"
|
|
position={Position.Right}
|
|
className={`!pointer-events-none !right-[-7px] !h-3 !w-3 !border ${
|
|
isRoot ? "!border-blue-200 !bg-blue-100" : "!border-blue-300 !bg-white"
|
|
}`}
|
|
/>
|
|
<button
|
|
type="button"
|
|
aria-label={data.isExpanded ? "收起子节点" : "展开子节点"}
|
|
onClick={handleToggleClick}
|
|
className="nodrag nopan absolute right-0 top-1/2 z-20 flex h-9 w-9 translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full border border-blue-200 bg-white text-lg font-semibold text-blue-700 shadow-[0_8px_18px_rgba(37,99,235,0.14)] transition-all hover:border-blue-300 hover:bg-blue-50 active:scale-95"
|
|
>
|
|
{data.isExpanded ? "-" : "+"}
|
|
</button>
|
|
</>
|
|
) : (
|
|
<span
|
|
className={`pointer-events-none absolute -right-[7px] top-1/2 h-3 w-3 -translate-y-1/2 rounded-full border ${
|
|
isRoot ? "border-blue-200 bg-blue-100" : "border-slate-300 bg-white"
|
|
}`}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default memo(MindmapNodeCard);
|