← back to Stayclaim
src/app/map/MapWithLayers.tsx
86 lines
'use client';
import { useMemo, useState } from 'react';
import { InteractiveMapMount, type MapPin } from '@/components/InteractiveMapMount';
type FilmingPin = {
slug: string;
title: string;
lat: number;
lng: number;
productions: { slug: string; title: string; year: number | null }[];
};
export function MapWithLayers({
archivePins,
filmingPins,
}: {
archivePins: MapPin[];
filmingPins: FilmingPin[];
}) {
const [showArchive, setShowArchive] = useState(true);
const [showFilming, setShowFilming] = useState(true);
const pins: MapPin[] = useMemo(() => {
// Dedupe by slug across layers; filming overlay wins when both layers
// have a pin for the same address (coral overrides moss).
const bySlug = new Map<string, MapPin>();
const slugless: MapPin[] = [];
if (showArchive) {
for (const p of archivePins) {
if (p.slug) bySlug.set(p.slug, { ...p, featured: false });
else slugless.push({ ...p, featured: false });
}
}
if (showFilming) {
for (const f of filmingPins) {
const titles = f.productions.slice(0, 3).map(p => `${p.title}${p.year != null ? ` (${p.year})` : ''}`).join(' · ');
const more = f.productions.length > 3 ? ` +${f.productions.length - 3} more` : '';
bySlug.set(f.slug, {
slug: f.slug,
title: f.title,
subtitle: `Filmed: ${titles}${more}`,
lat: f.lat,
lng: f.lng,
featured: true,
});
}
}
return [...bySlug.values(), ...slugless];
}, [archivePins, filmingPins, showArchive, showFilming]);
return (
<div className="space-y-4">
<div className="flex items-center gap-3 flex-wrap">
<span className="text-[10px] uppercase tracking-[0.15em] text-ink/45">Layers</span>
<button
type="button"
aria-pressed={showArchive}
onClick={() => setShowArchive(s => !s)}
className={`inline-flex items-center gap-2 px-3 py-1 text-[11px] uppercase tracking-[0.15em] border transition ${
showArchive
? 'bg-moss text-sand border-moss'
: 'border-ink/20 text-ink/55 hover:border-ink/45'
}`}
>
<span className="inline-block w-2 h-2 rounded-full bg-moss" />
Archive ({archivePins.length})
</button>
<button
type="button"
aria-pressed={showFilming}
onClick={() => setShowFilming(s => !s)}
className={`inline-flex items-center gap-2 px-3 py-1 text-[11px] uppercase tracking-[0.15em] border transition ${
showFilming
? 'bg-ink text-sand border-ink'
: 'border-ink/20 text-ink/55 hover:border-ink/45'
}`}
>
<span className="inline-block w-2 h-2 rounded-full bg-coral" />
Filming ({filmingPins.length})
</button>
</div>
<InteractiveMapMount pins={pins} height={680} variant="voyager" />
</div>
);
}