teqo-backup/src/components/CitySelect.tsx
Francisco Ignácio Fontoura Solla 712649eecf Replace state/city selects with type-to-filter comboboxes
Turn StateSelect and CitySelect into autocomplete comboboxes so long
lists no longer bias people toward the top option. A shared FormCombobox
wraps shadcn's (Base UI) Combobox and bridges it to react-hook-form via
useController, storing the state code / city name while displaying a
label. Matching is accent-insensitive and prefix-based; the list opens
after 1 character and results are alphabetical. State searches by both
full name and initials (new StateNames map).

Adds @base-ui/react + shadcn combobox/input-group primitives, and
documents our export-const and named-hook-import conventions in the
components rule (with the src/components/ui/* shadcn carve-out).
2026-07-15 14:41:58 -03:00

43 lines
1.1 KiB
TypeScript

import { CitiesByState } from '@/lib/cities'
import { useEffect, useMemo } from 'react'
import { useFormContext, useWatch } from 'react-hook-form'
import { FormCombobox, type ComboboxOption } from './FormCombobox'
interface CitySelectProps {
placeholder?: string
}
export const CitySelect = ({ placeholder = 'Selecione uma cidade' }: CitySelectProps) => {
const { control, setValue } = useFormContext()
const state: keyof typeof CitiesByState | undefined = useWatch({
control,
name: 'state',
})
const options = useMemo<ComboboxOption[]>(
() =>
state
? CitiesByState[state]
.map((city) => ({ value: city, label: city }))
.sort((a, b) => a.label.localeCompare(b.label, 'pt-BR'))
: [],
[state],
)
useEffect(() => {
setValue('city', '')
}, [state, setValue])
return (
<FormCombobox
name="city"
id="city"
options={options}
placeholder={placeholder}
minChars={1}
disabled={!options.length}
required
emptyMessage="Nenhuma cidade encontrada."
/>
)
}