Files
next-tjwater-frontend/src/features/map/core/components/zoom.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

132 lines
3.3 KiB
TypeScript

import type { Map as MapLibreMap } from "maplibre-gl";
import { Home, Minus, Plus, type LucideIcon } from "lucide-react";
import { type RefObject, useCallback } from "react";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger
} from "@/shared/ui/tooltip";
import { cn } from "@/shared/ui/cn";
import {
MAP_CONTROL_HOVER_CLASS_NAME,
MAP_CONTROL_RADIUS_CLASS_NAME,
MAP_ICON_CELL_RADIUS_CLASS_NAME,
MAP_CONTROL_SURFACE_CLASS_NAME
} from "./map-control-styles";
type MapZoomProps = {
mapRef: RefObject<MapLibreMap | null>;
mapReady: boolean;
onHome: () => void;
className?: string;
};
export function MapZoom({ mapRef, mapReady, onHome, className = "" }: MapZoomProps) {
const handleZoomIn = useCallback(() => {
mapRef.current?.zoomIn({ duration: 260 });
}, [mapRef]);
const handleZoomOut = useCallback(() => {
mapRef.current?.zoomOut({ duration: 260 });
}, [mapRef]);
return (
<TooltipProvider delayDuration={180}>
<div
role="group"
aria-label="地图缩放"
className={cn(
"pointer-events-auto flex w-12 flex-col gap-2",
className
)}
>
<div
className={cn(
"flex flex-col p-1",
MAP_CONTROL_RADIUS_CLASS_NAME,
MAP_CONTROL_SURFACE_CLASS_NAME
)}
>
<ControlButton
icon={Plus}
label="放大"
disabled={!mapReady}
onClick={handleZoomIn}
/>
<Divider />
<ControlButton
icon={Minus}
label="缩小"
disabled={!mapReady}
onClick={handleZoomOut}
/>
</div>
<div
className={cn(
"p-1",
MAP_CONTROL_RADIUS_CLASS_NAME,
MAP_CONTROL_SURFACE_CLASS_NAME
)}
>
<ControlButton
icon={Home}
label="缩放到全局"
disabled={!mapReady}
onClick={onHome}
/>
</div>
</div>
</TooltipProvider>
);
}
type ControlButtonProps = {
icon: LucideIcon;
label: string;
disabled: boolean;
onClick: () => void;
};
function ControlButton({ icon: Icon, label, disabled, onClick }: ControlButtonProps) {
return (
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
aria-label={label}
disabled={disabled}
onClick={onClick}
className={cn(
"grid h-10 w-10 place-items-center border border-transparent",
MAP_ICON_CELL_RADIUS_CLASS_NAME,
"bg-transparent text-slate-700",
MAP_CONTROL_HOVER_CLASS_NAME,
disabled &&
"text-slate-400 opacity-45 hover:bg-transparent hover:text-slate-400"
)}
>
<Icon size={18} strokeWidth={2.2} aria-hidden="true" />
</button>
</TooltipTrigger>
<TooltipContent
side="left"
sideOffset={8}
className="border border-slate-200 bg-white px-2.5 py-1.5 text-xs font-medium text-slate-800 shadow-lg"
>
{label}
</TooltipContent>
</Tooltip>
);
}
function Divider() {
return (
<div
className="my-0.5 h-px w-3.5 self-center bg-slate-300/70"
aria-hidden="true"
/>
);
}