Files
next-tjwater-frontend/src/features/map/core/components/draw-toolbar.tsx
T
jiang f4318a9b9c feat: refine workbench visuals and map controls
Unify the Agent history extension with the header acrylic surface, preserve the full conversation body, and consolidate shared control and status styling.

Restore map flow and SCADA controller behavior, remove obsolete rendering paths, and extend regression coverage. Button press coverage now releases outside the target so state assertions cannot accidentally toggle the control.
2026-07-28 16:39:36 +08:00

134 lines
4.4 KiB
TypeScript

import type { LucideIcon } from "lucide-react";
import { cn } from "@/shared/ui/cn";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger
} from "@/shared/ui/tooltip";
import {
MAP_COMPACT_RADIUS_CLASS_NAME,
MAP_CONTROL_SURFACE_CLASS_NAME,
MAP_ICON_CELL_RADIUS_CLASS_NAME,
MAP_READABLE_SURFACE_CLASS_NAME
} from "./map-control-styles";
export type MapDrawTool = {
id: string;
label: string;
icon: LucideIcon;
active?: boolean;
disabled?: boolean;
tone?: "default" | "danger";
onClick?: () => void;
};
export type MapDrawToolbarProps = {
items: MapDrawTool[];
label?: string;
className?: string;
variant?: "compact" | "panel";
};
export function MapDrawToolbar({ items, label = "绘制工具", className = "", variant = "compact" }: MapDrawToolbarProps) {
if (variant === "panel") {
return (
<div
role="toolbar"
aria-label={label}
className={cn("pointer-events-auto grid grid-cols-2 gap-1.5", className)}
>
{items.map((item) => {
const Icon = item.icon;
const isDanger = item.tone === "danger";
return (
<button
key={item.id}
type="button"
aria-pressed={item.active}
disabled={item.disabled}
onClick={item.onClick}
className={cn(
"flex min-h-10 items-center gap-2 border px-2.5 py-2 text-left text-xs font-semibold transition duration-150",
MAP_COMPACT_RADIUS_CLASS_NAME,
item.active
? "border-blue-600 bg-blue-600 text-white shadow-[var(--action-shadow-primary)]"
: "text-slate-700 hover:border-blue-100 hover:bg-white hover:text-blue-700",
!item.active && MAP_READABLE_SURFACE_CLASS_NAME,
isDanger && "hover:border-red-100 hover:text-red-700",
item.disabled &&
"cursor-not-allowed opacity-55 hover:border-white/70 hover:bg-white/95 hover:text-slate-700"
)}
>
<span
className={cn(
"grid h-7 w-7 shrink-0 place-items-center",
MAP_ICON_CELL_RADIUS_CLASS_NAME,
item.active ? "bg-white/16 text-white" : "bg-slate-100 text-slate-500",
isDanger && "bg-red-50 text-red-600"
)}
>
<Icon size={15} aria-hidden="true" />
</span>
<span className="min-w-0 truncate">{item.label}</span>
</button>
);
})}
</div>
);
}
return (
<TooltipProvider delayDuration={180}>
<div
role="toolbar"
aria-label={label}
className={cn(
"pointer-events-auto inline-flex gap-1 p-1",
MAP_COMPACT_RADIUS_CLASS_NAME,
MAP_CONTROL_SURFACE_CLASS_NAME,
className
)}
>
{items.map((item) => {
const Icon = item.icon;
const isDanger = item.tone === "danger";
return (
<Tooltip key={item.id}>
<TooltipTrigger asChild>
<button
type="button"
aria-label={item.label}
aria-pressed={item.active}
disabled={item.disabled}
onClick={item.onClick}
className={cn(
"grid h-10 w-10 shrink-0 place-items-center border border-transparent text-slate-600",
MAP_ICON_CELL_RADIUS_CLASS_NAME,
item.active
? "border-blue-600 bg-blue-600 text-white shadow-[var(--action-shadow-primary)]"
: "hover:bg-blue-50 hover:text-blue-700",
isDanger && "hover:bg-red-50 hover:text-red-700",
item.disabled && "opacity-45 hover:bg-transparent hover:text-slate-600"
)}
>
<Icon size={16} aria-hidden="true" />
</button>
</TooltipTrigger>
<TooltipContent
side="top"
sideOffset={7}
className="border border-slate-200 bg-white px-2.5 py-1.5 text-xs font-medium text-slate-800 shadow-lg"
>
{item.label}
</TooltipContent>
</Tooltip>
);
})}
</div>
</TooltipProvider>
);
}