63 lines
2.4 KiB
TypeScript
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>
|
|
);
|
|
}
|