Files
anouma/components/FormField.tsx
T

63 lines
2.4 KiB
TypeScript

import type { InputHTMLAttributes, ReactNode, TextareaHTMLAttributes } from "react";
/**
* The one field style used by every form on the site (previously each form
* declared its own near-identical `fieldClass` constant with small drifts in
* padding/size/background - see ContactForm, auth/*, meeting/* history).
* Functionality is untouched: both components just spread the native
* input/textarea props through, so controlled and uncontrolled forms,
* validation attributes, autoComplete, etc. all keep working exactly as
* before.
*/
export const fieldClass =
"w-full rounded-2xl border border-anouma-taupe/30 bg-white px-5 py-3.5 text-base text-anouma-plum placeholder:text-anouma-plum/50 focus:border-anouma-mauve-dark focus:outline-none";
function FieldLabel({ label, htmlFor, hidden = false }: { label: string; htmlFor: string; hidden?: boolean }) {
return (
<label htmlFor={htmlFor} className={hidden ? "sr-only" : "mb-2 block text-sm font-medium text-anouma-plum"}>
{label}
</label>
);
}
type TextFieldProps = InputHTMLAttributes<HTMLInputElement> & {
label: string;
id: string;
/** Keeps the label for screen readers only - for compact inline fields identified by a placeholder instead (e.g. ResendVerificationButton's email prompt). */
hideLabel?: boolean;
wrapperClassName?: string;
};
export function TextField({ label, id, hideLabel = false, className = "", wrapperClassName = "", ...inputProps }: TextFieldProps) {
return (
<div className={wrapperClassName}>
<FieldLabel label={label} htmlFor={id} hidden={hideLabel} />
<input id={id} className={`${fieldClass} ${className}`} {...inputProps} />
</div>
);
}
type TextAreaFieldProps = TextareaHTMLAttributes<HTMLTextAreaElement> & {
label: string;
id: string;
wrapperClassName?: string;
};
export function TextAreaField({ label, id, className = "", wrapperClassName = "", ...textareaProps }: TextAreaFieldProps) {
return (
<div className={wrapperClassName}>
<FieldLabel label={label} htmlFor={id} />
<textarea id={id} className={`${fieldClass} ${className}`} {...textareaProps} />
</div>
);
}
/** Shared error-message treatment (`role="alert"`) used below every form field/submit button. */
export function FormError({ children, className = "" }: { children: ReactNode; className?: string }) {
return (
<p role="alert" className={`text-sm text-red-700 ${className}`}>
{children}
</p>
);
}