Good changes

This commit is contained in:
Andras Schmelczer 2026-02-10 22:09:46 +00:00
parent d39d1b15fd
commit 1f68ca0512
23 changed files with 670 additions and 289 deletions

View file

@ -1,6 +1,8 @@
import { useState, useCallback } from 'react';
import { useState, useCallback, useRef, useEffect } from 'react';
import type { PostcodeGeometry } from '../../types';
import { authHeaders } from '../../lib/api';
import { useIsMobile } from '../../hooks/useIsMobile';
import { SearchIcon } from '../ui/icons/SearchIcon';
export interface SearchedPostcode {
postcode: string;
@ -17,6 +19,29 @@ export default function PostcodeSearch({
const [query, setQuery] = useState('');
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
const [expanded, setExpanded] = useState(false);
const isMobile = useIsMobile();
const formRef = useRef<HTMLFormElement>(null);
const inputRef = useRef<HTMLInputElement>(null);
// Close on outside click (mobile only)
useEffect(() => {
if (!isMobile || !expanded) return;
const handler = (e: MouseEvent) => {
if (formRef.current && !formRef.current.contains(e.target as Node)) {
setExpanded(false);
}
};
document.addEventListener('mousedown', handler);
return () => document.removeEventListener('mousedown', handler);
}, [isMobile, expanded]);
// Focus input when expanding on mobile
useEffect(() => {
if (isMobile && expanded) {
inputRef.current?.focus();
}
}, [isMobile, expanded]);
const handleSubmit = useCallback(
async (e: React.FormEvent) => {
@ -41,19 +66,39 @@ export default function PostcodeSearch({
onFlyTo(json.latitude, json.longitude, 16);
onPostcodeSearched?.({ postcode: json.postcode, geometry: json.geometry });
setQuery('');
if (isMobile) setExpanded(false);
} catch {
setError('Lookup failed');
} finally {
setLoading(false);
}
},
[query, onFlyTo, onPostcodeSearched]
[query, onFlyTo, onPostcodeSearched, isMobile]
);
// Mobile collapsed state: just a search icon button
if (isMobile && !expanded) {
return (
<button
type="button"
onClick={() => setExpanded(true)}
className="absolute top-3 left-3 z-10 p-2 bg-white dark:bg-warm-800 rounded shadow-lg"
aria-label="Search postcode"
>
<SearchIcon className="w-5 h-5 text-warm-600 dark:text-warm-300" />
</button>
);
}
return (
<form onSubmit={handleSubmit} className="absolute top-3 left-3 z-10 flex flex-col gap-1">
<form
ref={formRef}
onSubmit={handleSubmit}
className="absolute top-3 left-3 z-10 flex flex-col gap-1"
>
<div className="flex shadow-lg rounded overflow-hidden">
<input
ref={inputRef}
type="text"
value={query}
onChange={(e) => {