Untitled
unknown
plain_text
a year ago
3.8 kB
9
Indexable
import React, { useEffect } from 'react';
import { Input } from './input';
import { useForm, Control, Controller, FieldErrors, useWatch } from 'react-hook-form';
import { differenceInYears, parse, isValid } from 'date-fns';
interface DateOfBirthProps {
control: Control<any>;
setAge: React.Dispatch<React.SetStateAction<number | null>>;
errors: FieldErrors;
}
export const AgeLabel: React.FC<{ age: number | null; year: string }> = ({ age, year }) => {
if (age === null || year.length !== 4) return null;
return (
<span className="text-gray-500">
<span> · {age} year</span>
<span>{age > 1 ? 's' : ''}</span>
<span> old</span>
</span>
);
};
const DateOfBirth: React.FC<DateOfBirthProps> = ({ control, setAge, errors }) => {
const { clearErrors, setError } = useForm();
const inputs = [
{ name: 'bday-day', placeholder: 'DD', maxLength: 2, label: 'Day' },
{ name: 'bday-month', placeholder: 'MM', maxLength: 2, label: 'Month' },
{ name: 'bday-year', placeholder: 'YYYY', maxLength: 4, label: 'Year' },
];
const { 'bday-day': day, 'bday-month': month, 'bday-year': year } = useWatch({ control });
useEffect(() => {
if (day && month && year) {
const date = parse(`${year}-${month}-${day}`, 'yyyy-MM-dd', new Date());
if (isValid(date)) {
const age = differenceInYears(new Date(), date);
if (age < 0) {
setError('dob', { type: 'manual', message: 'Date of birth cannot be in the future' });
setAge(null);
} else {
clearErrors('dob');
setAge(age);
}
} else {
setError('dob', { type: 'manual', message: 'Invalid date' });
setAge(null);
}
} else {
clearErrors('dob');
setAge(null);
}
}, [day, month, year, setAge, setError, clearErrors]);
return (
<div className="grid grid-cols-3">
{inputs.map(({ name, placeholder, maxLength, label }, idx) => (
<Controller
key={name}
name={name}
control={control}
render={({ field }) => (
<div>
<label htmlFor={name} className="sr-only">
{label}
</label>
<Input
{...field}
id={name}
placeholder={placeholder}
maxLength={maxLength}
type="text"
inputMode="numeric"
aria-label={label}
aria-invalid={errors[name] ? 'true' : 'false'}
aria-describedby={errors[name] ? `${name}-error` : undefined}
className={`relative w-full
${errors[name] ? '!border-danger-500 hover:!border-danger-500 focus:!border-danger-500 focus:!ring-danger-500/20' : ''}
${idx > 0 && errors[inputs[idx - 1].name] ? 'border-l-danger-500' : ''}
${idx < 2 && errors[inputs[idx + 1].name] ? 'border-r-danger-500' : ''}
tabular-nums placeholder:text-neutral-400
${
idx === 0
? '!rounded-r-none rounded-l-md'
: idx === 2
? '!rounded-l-none rounded-r-md'
: '!rounded-none'
}
${idx === 1 ? '-ml-px' : idx === 2 ? 'ml-[-2px] w-[calc(100%+2px)]' : ''}
hover:z-10 focus:z-20`}
/>
{errors[name] && (
<span
id={`${name}-error`}
className="sr-only text-sm text-danger-500"
aria-live="assertive"
>
{errors[name]?.message as string}
</span>
)}
</div>
)}
/>
))}
</div>
);
};
export default DateOfBirth;
Editor is loading...
Leave a Comment