137 lines
4.5 KiB
TypeScript
137 lines
4.5 KiB
TypeScript
"use client";
|
|
|
|
import type { LucideIcon } from "lucide-react";
|
|
import { cn } from "@/lib/utils";
|
|
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-md shadow-blue-600/20"
|
|
: "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 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-8 w-8 shrink-0 place-items-center text-slate-600 transition duration-150",
|
|
MAP_ICON_CELL_RADIUS_CLASS_NAME,
|
|
"focus-visible:outline focus-visible:outline-2 focus-visible:outline-slate-900 focus-visible:outline-offset-2",
|
|
item.active
|
|
? "bg-blue-600 text-white shadow-sm"
|
|
: "hover:bg-blue-50 hover:text-blue-700",
|
|
isDanger && "hover:bg-red-50 hover:text-red-700",
|
|
item.disabled && "cursor-not-allowed 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>
|
|
);
|
|
}
|