Untitled
unknown
plain_text
18 days ago
3.8 kB
2
Indexable
Never
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;
Leave a Comment