62 lines
1.7 KiB
TypeScript
62 lines
1.7 KiB
TypeScript
"use client";
|
|
|
|
import { useState, type ReactNode } from "react";
|
|
import { ChevronDown } from "lucide-react";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
interface ResearchMapSectionProps {
|
|
title: string;
|
|
count?: number;
|
|
defaultOpen?: boolean;
|
|
collapsible?: boolean;
|
|
children: ReactNode;
|
|
className?: string;
|
|
}
|
|
|
|
export function ResearchMapSection({
|
|
title,
|
|
count,
|
|
defaultOpen = false,
|
|
collapsible = true,
|
|
children,
|
|
className,
|
|
}: ResearchMapSectionProps) {
|
|
const [open, setOpen] = useState(defaultOpen || !collapsible);
|
|
const label = count !== undefined ? `${title} · ${count}` : title;
|
|
|
|
if (!collapsible) {
|
|
return (
|
|
<div className={cn("tool-section overflow-hidden", className)}>
|
|
<div className="tool-section-header">
|
|
<span>{label}</span>
|
|
</div>
|
|
<div className="border-t border-border px-3 py-2.5">{children}</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className={cn("tool-section overflow-hidden", className)}>
|
|
<button
|
|
type="button"
|
|
onClick={() => setOpen((v) => !v)}
|
|
className="tool-section-header w-full text-left hover:bg-accent/50"
|
|
>
|
|
<span>{label}</span>
|
|
<ChevronDown
|
|
className={cn("h-3.5 w-3.5 shrink-0 transition-transform", open && "rotate-180")}
|
|
/>
|
|
</button>
|
|
{open && <div className="border-t border-border px-3 py-2.5">{children}</div>}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function MapField({ label, children }: { label: string; children: ReactNode }) {
|
|
return (
|
|
<div className="rounded-md border border-border bg-muted/40 px-3 py-2.5">
|
|
<p className="tool-kv-label">{label}</p>
|
|
<div className="tool-kv-value">{children}</div>
|
|
</div>
|
|
);
|
|
} |