64 lines
1.7 KiB
TypeScript
64 lines
1.7 KiB
TypeScript
|
|
import type { InputHTMLAttributes, ReactNode, TextareaHTMLAttributes } from 'react';
|
||
|
|
|
||
|
|
interface BaseProps {
|
||
|
|
label: string;
|
||
|
|
htmlFor: string;
|
||
|
|
error?: string | null;
|
||
|
|
hint?: string;
|
||
|
|
required?: boolean;
|
||
|
|
children: ReactNode;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function FormField({ label, htmlFor, error, hint, required, children }: BaseProps): JSX.Element {
|
||
|
|
return (
|
||
|
|
<div className="flex flex-col gap-xs">
|
||
|
|
<label htmlFor={htmlFor} className="text-[14px] font-medium text-ink">
|
||
|
|
{label}
|
||
|
|
{required ? <span className="text-bloom-deep ml-1">*</span> : null}
|
||
|
|
</label>
|
||
|
|
{children}
|
||
|
|
{error ? (
|
||
|
|
<span role="alert" className="text-[12px] text-bloom-deep">
|
||
|
|
{error}
|
||
|
|
</span>
|
||
|
|
) : hint ? (
|
||
|
|
<span className="text-[12px] text-graphite">{hint}</span>
|
||
|
|
) : null}
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
export function TextInput(props: InputHTMLAttributes<HTMLInputElement>): JSX.Element {
|
||
|
|
return <input {...props} className={`text-input ${props.className ?? ''}`} />;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function TextArea(props: TextareaHTMLAttributes<HTMLTextAreaElement>): JSX.Element {
|
||
|
|
return (
|
||
|
|
<textarea
|
||
|
|
{...props}
|
||
|
|
className={`text-input min-h-[112px] py-sm ${props.className ?? ''}`}
|
||
|
|
/>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
interface SelectOption {
|
||
|
|
value: string;
|
||
|
|
label: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
interface SelectProps extends Omit<InputHTMLAttributes<HTMLSelectElement>, 'children'> {
|
||
|
|
options: SelectOption[];
|
||
|
|
}
|
||
|
|
|
||
|
|
export function Select({ options, className, ...rest }: SelectProps): JSX.Element {
|
||
|
|
return (
|
||
|
|
<select {...rest} className={`text-input ${className ?? ''}`}>
|
||
|
|
{options.map((o) => (
|
||
|
|
<option key={o.value} value={o.value}>
|
||
|
|
{o.label}
|
||
|
|
</option>
|
||
|
|
))}
|
||
|
|
</select>
|
||
|
|
);
|
||
|
|
}
|