remix-demo
react router (remix) demo
git clone https://9o.is/git/remix-demo.git
InputCombobox.tsx
(1865B)
1 import { useControlField, useField } from "remix-validated-form";
2 import {
3 Combobox,
4 ComboboxInput,
5 ComboboxPopover,
6 ComboboxList,
7 ComboboxOption,
8 } from "@reach/combobox";
9 import { getInputStyles } from "./Input";
10 import InputError from "./InputError";
11
12 interface InputComboboxProps {
13 label: string;
14 name: string;
15 options: string[];
16 onInputChange?: (input: string) => void;
17 onSelect?: (input: string) => void;
18 required?: boolean;
19 }
20
21 export default function InputCombobox(props: InputComboboxProps) {
22 const { label, name, options, onInputChange, onSelect, required } = props;
23 const { error, getInputProps, validate } = useField(name);
24 const [, setValue] = useControlField<string>(name);
25
26 function handleChange(value: string) {
27 if (onInputChange) onInputChange(value);
28 setValue(value);
29 validate();
30 }
31
32 function handleSelect(value: string) {
33 if (onSelect) onSelect(value);
34 setValue(value);
35 validate();
36 }
37
38 return (
39 <div>
40 <label htmlFor={name} className={getInputStyles("label", error)}>
41 {label}
42 </label>
43 <Combobox onSelect={handleSelect} aria-label={`choose a ${label}`}>
44 <ComboboxInput
45 {...getInputProps({
46 type: "text",
47 onChange: (e) => handleChange(e.target.value),
48 })}
49 id={name}
50 name={name}
51 required={required}
52 className={getInputStyles("input", error)}
53 aria-invalid={Boolean(error)}
54 aria-describedby={`${name}-error`}
55 />
56 <ComboboxPopover className="rounded">
57 <ComboboxList>
58 {options.map((option) => (
59 <ComboboxOption key={option} value={option} />
60 ))}
61 </ComboboxList>
62 </ComboboxPopover>
63 </Combobox>
64 <InputError error={error} id={`${name}-error`} />
65 </div>
66 );
67 }