35 lines
1.3 KiB
TypeScript
35 lines
1.3 KiB
TypeScript
import { Input, Label, Text, TextField as AriaTextField } from 'react-aria-components';
|
|
|
|
import field from './Field.module.css';
|
|
import styles from './TextField.module.css';
|
|
|
|
interface Props {
|
|
label: string;
|
|
value: string;
|
|
onChange: (value: string) => void;
|
|
/**
|
|
* The hint in an empty field; it has a job of its own — to say what happens if the field is left
|
|
* untouched.
|
|
*/
|
|
placeholder?: string;
|
|
/** The line under the field: what exactly an empty field will end in, not general advice. */
|
|
hint?: string;
|
|
}
|
|
|
|
// An ordinary text field of a form. It differs from FilterField in role, not in look: a filter has
|
|
// no label and sends nothing, here a label is mandatory — this is a form.
|
|
export function TextField({ label, value, onChange, placeholder, hint }: Props) {
|
|
return (
|
|
<AriaTextField className={styles.field} value={value} onChange={onChange}>
|
|
<Label className={styles.label}>{label}</Label>
|
|
<Input className={`${field.input} ${field.framed}`} placeholder={placeholder} />
|
|
{/* slot="description" is not decoration: the library hangs `aria-describedby` on it, and
|
|
without that the hint is not heard by the one who needs it most of all. */}
|
|
{hint && (
|
|
<Text className={styles.hint} slot="description">
|
|
{hint}
|
|
</Text>
|
|
)}
|
|
</AriaTextField>
|
|
);
|
|
}
|