Files
next-tjwater-frontend/src/features/map/core/components/toolbar.tsx
T
jiang 0ae0a41e61
Generic Container CI/CD / test-build-publish (push) Successful in 2m7s
Frontend CI/CD / build-test-publish-and-deploy (push) Successful in 2m8s
feat(charts): manage investigation charts in a scrolling workspace
2026-11-10 17:56:23 +08:00

125 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { LucideIcon } from "lucide-react";
import { cn } from "@/shared/ui/cn";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger
} from "@/shared/ui/tooltip";
import {
MAP_CONTROL_HOVER_CLASS_NAME,
mapOptionInteractionVariants,
MAP_CONTROL_RADIUS_CLASS_NAME,
MAP_ICON_CELL_RADIUS_CLASS_NAME,
MAP_CONTROL_SURFACE_CLASS_NAME
} from "./map-control-styles";
export type MapToolbarItem = {
id: string;
label: string;
description?: string;
icon: LucideIcon;
active?: boolean;
disabled?: boolean;
badge?: string | number;
onClick?: () => void;
};
type MapToolbarProps = {
items: MapToolbarItem[];
label?: string;
orientation?: "vertical" | "horizontal";
className?: string;
};
export function MapToolbar({
items,
label = "地图工具",
orientation = "vertical",
className = ""
}: MapToolbarProps) {
const isVertical = orientation === "vertical";
return (
<TooltipProvider delayDuration={180}>
<nav
aria-label={label}
className={cn(
"pointer-events-auto p-1",
MAP_CONTROL_RADIUS_CLASS_NAME,
MAP_CONTROL_SURFACE_CLASS_NAME,
isVertical ? "flex w-fit flex-col" : "inline-flex",
className
)}
>
{items.map((tool, index) => {
const Icon = tool.icon;
const title = tool.description ? `${tool.label}:${tool.description}` : tool.label;
return (
<div key={tool.id} className={cn(isVertical ? "contents" : "flex items-center")}>
{index > 0 ? <ToolbarDivider vertical={isVertical} /> : null}
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
aria-label={title}
aria-pressed={tool.active}
disabled={tool.disabled}
onClick={tool.onClick}
className={cn(
"relative grid h-10 w-10 shrink-0 place-items-center border border-transparent text-slate-700",
MAP_ICON_CELL_RADIUS_CLASS_NAME,
tool.active
? cn("border-[var(--action-blue)] bg-[var(--action-blue)] text-white shadow-[var(--action-shadow-primary)]", mapOptionInteractionVariants({ state: "selected" }))
: MAP_CONTROL_HOVER_CLASS_NAME,
tool.disabled && "text-[var(--ink-muted)] opacity-45"
)}
>
<Icon size={20} strokeWidth={2.15} aria-hidden="true" />
<span className="sr-only">{tool.label}</span>
{tool.badge ? (
<span className="status-tone-danger absolute -right-1 -top-1 grid min-w-4 place-items-center rounded-full bg-[var(--status-mark)] px-1 text-xs font-semibold leading-4 text-white ring-2 ring-white">
{tool.badge}
</span>
) : null}
</button>
</TooltipTrigger>
<TooltipContent
side={isVertical ? "left" : "top"}
sideOffset={8}
className="border border-[var(--surface-divider)] bg-[var(--surface-reading)] px-2.5 py-1.5 text-xs font-medium text-slate-800 shadow-lg"
>
{title}
</TooltipContent>
</Tooltip>
</div>
);
})}
</nav>
</TooltipProvider>
);
}
function ToolbarDivider({ vertical }: { vertical: boolean }) {
if (vertical) {
return (
<div
className="my-0.5 flex h-px w-full items-center justify-center"
aria-hidden="true"
>
<span className="block h-px w-3.5 bg-slate-300/70" />
</div>
);
}
return (
<div
className="mx-0.5 flex h-full w-px items-center justify-center"
aria-hidden="true"
>
<span className="block h-3.5 w-px bg-slate-300/70" />
</div>
);
}