Files
heritage-globe/components/map/MapControls.tsx
T
freedakgmail a550d6d0fd feat: Heritage Globe - 中国文物全球分布地图
- Next.js + MapLibre GL 3D 地球
- CARTO dark-matter vector 底图
- 动态星空背景(闪烁、漂移、流星)
- 经纬网格线(graticule)
- 文物数据图层与朝代/类别筛选
- 中国风信息弹窗
- 中英双语 i18n
- LayerPanel + MapControls 浮动面板
2026-07-01 01:37:18 +08:00

104 lines
3.9 KiB
TypeScript

'use client';
import { useState } from 'react';
import { useTranslations } from 'next-intl';
import { Search, Target, MapPin, Plus, Minus, X } from 'lucide-react';
interface MapControlsProps {
onSearch?: (query: string) => void;
onZoomIn?: () => void;
onZoomOut?: () => void;
onLocate?: () => void;
onSpotlight?: () => void;
}
export function MapControls({
onSearch,
onZoomIn,
onZoomOut,
onLocate,
onSpotlight,
}: MapControlsProps) {
const t = useTranslations('map');
const [searchOpen, setSearchOpen] = useState(false);
const [query, setQuery] = useState('');
const submitSearch = () => {
if (onSearch && query.trim()) {
onSearch(query.trim());
}
};
return (
<div className="absolute top-16 left-4 z-10 flex flex-col gap-2">
{/* Search */}
<div className="flex flex-col gap-2">
<button
onClick={() => setSearchOpen(!searchOpen)}
className="w-9 h-9 rounded-lg bg-[#06060c]/90 backdrop-blur-md border border-[var(--map-border)] flex items-center justify-center text-[var(--map-text-muted)] hover:text-white hover:bg-white/10 transition-colors shadow-lg"
aria-label="Open search"
>
{searchOpen ? <X className="w-4 h-4" /> : <Search className="w-4 h-4" />}
</button>
{searchOpen && (
<div className="flex items-center gap-1 p-1 rounded-lg bg-[#06060c]/90 backdrop-blur-md border border-[var(--map-border)] shadow-lg">
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && submitSearch()}
placeholder={t('search')}
className="w-48 px-2 py-1.5 bg-transparent text-sm text-[var(--map-text)] placeholder:text-[var(--map-text-muted)] focus:outline-none"
autoFocus
/>
<button
onClick={submitSearch}
className="w-7 h-7 rounded-md bg-[var(--map-panel)] hover:bg-[var(--map-panel-hover)] flex items-center justify-center text-[var(--map-text-muted)] hover:text-white transition-colors"
>
<Search className="w-3.5 h-3.5" />
</button>
</div>
)}
</div>
{/* Spotlight selection */}
<button
onClick={onSpotlight}
className="w-9 h-9 rounded-lg bg-[#06060c]/90 backdrop-blur-md border border-[var(--map-border)] flex items-center justify-center text-[var(--map-text-muted)] hover:text-white hover:bg-white/10 transition-colors shadow-lg"
aria-label="Enable spotlight selection"
>
<Target className="w-4 h-4" />
</button>
{/* Zoom to my location */}
<button
onClick={onLocate}
className="w-9 h-9 rounded-lg bg-[#06060c]/90 backdrop-blur-md border border-[var(--map-border)] flex items-center justify-center text-[var(--map-text-muted)] hover:text-white hover:bg-white/10 transition-colors shadow-lg"
aria-label="Zoom to my location"
>
<MapPin className="w-4 h-4" />
</button>
{/* Zoom in/out */}
<div className="flex flex-col rounded-lg bg-[#06060c]/90 backdrop-blur-md border border-[var(--map-border)] shadow-lg overflow-hidden">
<button
onClick={onZoomIn}
className="w-9 h-9 flex items-center justify-center text-[var(--map-text-muted)] hover:text-white hover:bg-white/10 transition-colors"
aria-label={t('zoomIn')}
>
<Plus className="w-4 h-4" />
</button>
<div className="h-px bg-[var(--map-border)]" />
<button
onClick={onZoomOut}
className="w-9 h-9 flex items-center justify-center text-[var(--map-text-muted)] hover:text-white hover:bg-white/10 transition-colors"
aria-label={t('zoomOut')}
>
<Minus className="w-4 h-4" />
</button>
</div>
</div>
);
}