Good changes
This commit is contained in:
parent
d39d1b15fd
commit
1f68ca0512
23 changed files with 670 additions and 289 deletions
|
|
@ -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) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue