Files
next-tjwater-drainage-frontend/features/map/core/components/layer-control.tsx
T

104 lines
3.5 KiB
TypeScript

import { Eye, EyeOff, Layers3, Lock } from "lucide-react";
import { cn } from "@/lib/utils";
import {
MAP_COMPACT_RADIUS_CLASS_NAME,
MAP_ICON_CELL_RADIUS_CLASS_NAME,
MAP_MAJOR_PANEL_RADIUS_CLASS_NAME,
MAP_TOOL_PANEL_SURFACE_CLASS_NAME
} from "./map-control-styles";
export type MapLayerControlItem = {
id: string;
label: string;
description?: string;
visible: boolean;
disabled?: boolean;
locked?: boolean;
};
type MapLayerControlProps = {
title?: string;
items: MapLayerControlItem[];
onToggleLayer: (id: string, visible: boolean) => void;
className?: string;
};
export function MapLayerControl({
title = "图层",
items,
onToggleLayer,
className = ""
}: MapLayerControlProps) {
return (
<section
aria-label={title}
className={cn(
"pointer-events-auto w-[276px] p-3",
MAP_MAJOR_PANEL_RADIUS_CLASS_NAME,
MAP_TOOL_PANEL_SURFACE_CLASS_NAME,
className
)}
>
<div className="flex items-center justify-between gap-3 px-1">
<div className="flex min-w-0 items-center gap-2">
<span className={cn("grid h-8 w-8 shrink-0 place-items-center bg-blue-50 text-blue-700", MAP_ICON_CELL_RADIUS_CLASS_NAME)}>
<Layers3 size={17} aria-hidden="true" />
</span>
<div className="min-w-0">
<h2 className="truncate text-sm font-semibold leading-tight text-slate-950">{title}</h2>
<p className="mt-0.5 text-xs text-slate-500">
{items.filter((item) => item.visible).length}/{items.length} 可见
</p>
</div>
</div>
</div>
<div className="mt-3 space-y-1.5">
{items.map((item) => {
const Icon = item.visible ? Eye : EyeOff;
const disabled = item.disabled || item.locked;
return (
<button
key={item.id}
type="button"
disabled={disabled}
aria-pressed={item.visible}
onClick={() => onToggleLayer(item.id, !item.visible)}
className={cn(
"flex min-h-12 w-full items-center gap-3 border px-2.5 text-left text-sm transition duration-150",
MAP_COMPACT_RADIUS_CLASS_NAME,
item.visible
? "border-blue-100 bg-[var(--glass-menu)] text-slate-900 shadow-md shadow-blue-950/10"
: "border-transparent bg-[var(--glass-readable)] text-slate-500 hover:border-slate-200 hover:bg-white",
disabled && "cursor-not-allowed opacity-60 hover:bg-transparent"
)}
>
<span
className={cn(
"grid h-8 w-8 shrink-0 place-items-center",
MAP_ICON_CELL_RADIUS_CLASS_NAME,
item.visible ? "bg-blue-600 text-white" : "bg-slate-100 text-slate-400"
)}
>
{item.locked ? <Lock size={15} aria-hidden="true" /> : <Icon size={15} aria-hidden="true" />}
</span>
<span className="min-w-0 flex-1">
<span className="block truncate font-semibold">{item.label}</span>
{item.description ? <span className="block truncate text-xs text-slate-500">{item.description}</span> : null}
</span>
<span
className={cn(
"h-2.5 w-2.5 rounded-full",
item.visible ? "bg-blue-600" : "bg-slate-300"
)}
aria-hidden="true"
/>
</button>
);
})}
</div>
</section>
);
}