import { useState, useCallback, useRef, useEffect } from 'react'; import type { PostcodeGeometry } from '../../types'; import { authHeaders } from '../../lib/api'; import { useIsMobile } from '../../hooks/useIsMobile'; import { useLocationSearch, type SearchResult } from '../../hooks/useLocationSearch'; import { PlaceSearchInput } from '../ui/PlaceSearchInput'; import { SearchIcon } from '../ui/icons/SearchIcon'; export interface SearchedLocation { postcode: string; geometry: PostcodeGeometry; } const ZOOM_FOR_TYPE: Record = { city: 10, borough: 12, town: 13, suburb: 14, quarter: 14, neighbourhood: 14, village: 14, station: 15, island: 12, locality: 14, hamlet: 15, isolated_dwelling: 16, }; export default function LocationSearch({ onFlyTo, onLocationSearched, onMouseEnter, }: { onFlyTo: (lat: number, lng: number, zoom: number) => void; onLocationSearched?: (postcode: SearchedLocation | null) => void; onMouseEnter?: () => void; }) { const search = useLocationSearch(); const [error, setError] = useState(null); const [loading, setLoading] = useState(false); const [expanded, setExpanded] = useState(false); const isMobile = useIsMobile(); const containerRef = useRef(null); const inputRef = useRef(null); // Close on outside click useEffect(() => { const handler = (e: MouseEvent) => { if (containerRef.current && !containerRef.current.contains(e.target as Node)) { search.close(); if (isMobile) setExpanded(false); } }; document.addEventListener('mousedown', handler); return () => document.removeEventListener('mousedown', handler); }, [isMobile, search.close]); // Focus input when expanding on mobile useEffect(() => { if (isMobile && expanded) { inputRef.current?.focus(); } }, [isMobile, expanded]); const selectResult = useCallback( async (result: SearchResult) => { if (result.type === 'place') { const zoom = ZOOM_FOR_TYPE[result.place_type] ?? 14; onFlyTo(result.lat, result.lon, zoom); onLocationSearched?.(null); search.clear(); if (isMobile) setExpanded(false); return; } // Postcode — fetch geometry setError(null); setLoading(true); search.close(); try { const res = await fetch(`/api/postcode/${encodeURIComponent(result.label)}`, authHeaders()); if (!res.ok) { setError('Postcode not found'); return; } const json: { postcode: string; latitude: number; longitude: number; geometry: PostcodeGeometry; } = await res.json(); onFlyTo(json.latitude, json.longitude, 16); onLocationSearched?.({ postcode: json.postcode, geometry: json.geometry }); search.clear(); if (isMobile) setExpanded(false); } catch { setError('Lookup failed'); } finally { setLoading(false); } }, [onFlyTo, onLocationSearched, isMobile, search] ); // Mobile collapsed state: just a search icon button if (isMobile && !expanded) { return ( ); } return (
setError(null)} />
{error && ( {error} )}
); }