79 lines
2.4 KiB
TypeScript
79 lines
2.4 KiB
TypeScript
|
|
import { useState, useRef, useEffect } from 'react'
|
||
|
|
import { usePokemonList } from '../../hooks/useAdmin'
|
||
|
|
|
||
|
|
interface PokemonSelectorProps {
|
||
|
|
label: string
|
||
|
|
selectedId: number | null
|
||
|
|
initialName?: string
|
||
|
|
onChange: (id: number | null) => void
|
||
|
|
}
|
||
|
|
|
||
|
|
export function PokemonSelector({
|
||
|
|
label,
|
||
|
|
selectedId,
|
||
|
|
initialName,
|
||
|
|
onChange,
|
||
|
|
}: PokemonSelectorProps) {
|
||
|
|
const [search, setSearch] = useState(initialName ?? '')
|
||
|
|
const [open, setOpen] = useState(false)
|
||
|
|
const ref = useRef<HTMLDivElement>(null)
|
||
|
|
const { data } = usePokemonList(search || undefined, 20, 0)
|
||
|
|
const pokemon = data?.items ?? []
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
function handleClick(e: MouseEvent) {
|
||
|
|
if (ref.current && !ref.current.contains(e.target as Node)) {
|
||
|
|
setOpen(false)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
document.addEventListener('mousedown', handleClick)
|
||
|
|
return () => document.removeEventListener('mousedown', handleClick)
|
||
|
|
}, [])
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div ref={ref} className="relative">
|
||
|
|
<label className="block text-sm font-medium mb-1">{label}</label>
|
||
|
|
<input
|
||
|
|
type="text"
|
||
|
|
required={!selectedId}
|
||
|
|
value={search}
|
||
|
|
onChange={(e) => {
|
||
|
|
setSearch(e.target.value)
|
||
|
|
setOpen(true)
|
||
|
|
if (!e.target.value) onChange(null)
|
||
|
|
}}
|
||
|
|
onFocus={() => setOpen(true)}
|
||
|
|
placeholder="Search pokemon..."
|
||
|
|
className="w-full px-3 py-2 border rounded-md dark:bg-gray-700 dark:border-gray-600"
|
||
|
|
/>
|
||
|
|
{selectedId && (
|
||
|
|
<input type="hidden" name={label} value={selectedId} required />
|
||
|
|
)}
|
||
|
|
{open && pokemon.length > 0 && (
|
||
|
|
<ul className="absolute z-10 mt-1 w-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-md shadow-lg max-h-48 overflow-y-auto">
|
||
|
|
{pokemon.map((p) => (
|
||
|
|
<li
|
||
|
|
key={p.id}
|
||
|
|
onClick={() => {
|
||
|
|
onChange(p.id)
|
||
|
|
setSearch(p.name)
|
||
|
|
setOpen(false)
|
||
|
|
}}
|
||
|
|
className={`px-3 py-2 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700 text-sm flex items-center gap-2 ${
|
||
|
|
p.id === selectedId ? 'bg-blue-50 dark:bg-blue-900/30' : ''
|
||
|
|
}`}
|
||
|
|
>
|
||
|
|
{p.spriteUrl && (
|
||
|
|
<img src={p.spriteUrl} alt="" className="w-6 h-6" />
|
||
|
|
)}
|
||
|
|
<span>
|
||
|
|
#{p.nationalDex} {p.name}
|
||
|
|
</span>
|
||
|
|
</li>
|
||
|
|
))}
|
||
|
|
</ul>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
)
|
||
|
|
}
|