|
|
@@ -0,0 +1,141 @@ |
|
|
/** |
|
|
/DOBPicker.tsx |
|
|
**/ |
|
|
|
|
|
'use client'; |
|
|
|
|
|
import * as React from 'react'; |
|
|
import { format, subYears } from 'date-fns'; |
|
|
import { cn } from '@/lib/utils'; |
|
|
import { Button } from '@/components/ui/button'; |
|
|
import { Calendar } from '@/components/ui/calendar'; |
|
|
import { buttonVariants } from '@/components/ui/button'; |
|
|
|
|
|
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; |
|
|
|
|
|
export function DOBPicker({ dob, setDOB }: { dob: Date | undefined; setDOB: React.Dispatch<React.SetStateAction<Date | undefined>> }) { |
|
|
/** |
|
|
* The maximum date that can be selected is 18 years ago |
|
|
*/ |
|
|
const maxDate = subYears(new Date(), 18); |
|
|
|
|
|
return ( |
|
|
<div className="w-full"> |
|
|
<Popover> |
|
|
<PopoverTrigger asChild> |
|
|
<Button |
|
|
variant={'default'} |
|
|
className={cn('w-full justify-start font-mono text-left font-normal bg-white/5', !dob && 'text-muted-foreground')} |
|
|
> |
|
|
<svg className="w-4 h-4 mr-2" fill="none" viewBox="0 0 24 24"> |
|
|
<path |
|
|
stroke="currentColor" |
|
|
stroke-linecap="round" |
|
|
stroke-linejoin="round" |
|
|
stroke-width="1.5" |
|
|
d="M4.75 8.75C4.75 7.64543 5.64543 6.75 6.75 6.75H17.25C18.3546 6.75 19.25 7.64543 19.25 8.75V17.25C19.25 18.3546 18.3546 19.25 17.25 19.25H6.75C5.64543 19.25 4.75 18.3546 4.75 17.25V8.75Z" |
|
|
></path> |
|
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M8 4.75V8.25"></path> |
|
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M16 4.75V8.25"></path> |
|
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M7.75 10.75H16.25"></path> |
|
|
</svg> |
|
|
|
|
|
{dob ? format(dob, 'PPP') : <span>Date of Birth</span>} |
|
|
</Button> |
|
|
</PopoverTrigger> |
|
|
<PopoverContent className="w-full p-0 font-mono text-white bg-black border border-white/10"> |
|
|
<Calendar |
|
|
classNames={{ |
|
|
cell: 'text-center text-sm p-0 relative [&:has([aria-selected])]:bg-white/5 first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20', |
|
|
day: cn( |
|
|
buttonVariants({ variant: 'ghost' }), |
|
|
'h-9 w-9 p-0 font-normal aria-selected:opacity-100 hover:bg-languid-lavendar/10 hover:text-white/40' |
|
|
), |
|
|
day_selected: 'rounded-full bg-languid-lavendar hover:bg-white/10 text-primary-foreground', |
|
|
caption_label: 'hidden', |
|
|
caption_dropdowns: 'flex w-full items-center justify-center space-x-2', |
|
|
}} |
|
|
mode="single" |
|
|
selected={dob} |
|
|
fromDate={subYears(new Date(), 100)} |
|
|
toDate={maxDate} |
|
|
captionLayout="dropdown" |
|
|
onSelect={setDOB} |
|
|
defaultMonth={new Date(dob?.getFullYear() ?? maxDate.getFullYear(), dob?.getMonth() ?? maxDate.getMonth(), 1)} |
|
|
required |
|
|
initialFocus |
|
|
className="border rounded-md border-white/10" |
|
|
/> |
|
|
</PopoverContent> |
|
|
</Popover> |
|
|
</div> |
|
|
); |
|
|
} |
|
|
|
|
|
export default DOBPicker; |
|
|
|
|
|
/** |
|
|
/ui/calendar.tsx |
|
|
**/ |
|
|
|
|
|
'use client'; |
|
|
|
|
|
import * as React from 'react'; |
|
|
import { ChevronLeft, ChevronRight } from 'lucide-react'; |
|
|
import { DayPicker } from 'react-day-picker'; |
|
|
|
|
|
import { cn } from '@/lib/utils'; |
|
|
import { buttonVariants } from '@/components/ui/button'; |
|
|
|
|
|
export type CalendarProps = React.ComponentProps<typeof DayPicker>; |
|
|
|
|
|
function Calendar({ className, classNames, showOutsideDays = true, ...props }: CalendarProps) { |
|
|
return ( |
|
|
<DayPicker |
|
|
showOutsideDays={showOutsideDays} |
|
|
className={cn('p-3', className)} |
|
|
classNames={{ |
|
|
months: 'flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0', |
|
|
month: 'space-y-4', |
|
|
caption: 'flex justify-center pt-1 relative items-center', |
|
|
caption_label: 'text-sm font-medium hidden', |
|
|
nav: 'space-x-1 flex items-center', |
|
|
nav_button: cn(buttonVariants({ variant: 'outline' }), 'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100'), |
|
|
nav_button_previous: 'absolute left-1 border border-white/20', |
|
|
nav_button_next: 'absolute right-1 border border-white/20', |
|
|
table: 'w-full border-collapse space-y-1', |
|
|
head_row: 'flex', |
|
|
head_cell: 'text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]', |
|
|
row: 'flex w-full mt-2', |
|
|
cell: 'text-center text-sm p-0 relative [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20', |
|
|
day: cn(buttonVariants({ variant: 'ghost' }), 'h-9 w-9 p-0 font-normal aria-selected:opacity-100'), |
|
|
day_selected: |
|
|
'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground', |
|
|
day_today: 'bg-accent text-accent-foreground', |
|
|
day_outside: 'text-muted-foreground opacity-50', |
|
|
day_disabled: 'text-muted-foreground opacity-50', |
|
|
day_range_middle: 'aria-selected:bg-accent aria-selected:text-accent-foreground', |
|
|
day_hidden: 'invisible', |
|
|
...classNames, |
|
|
}} |
|
|
components={{ |
|
|
Dropdown: ({ ...props }) => ( |
|
|
<div className="w-full"> |
|
|
<select |
|
|
autoFocus |
|
|
{...props} |
|
|
className="w-full px-1 py-2 space-x-4 text-sm text-white transition-all duration-300 rounded-md outline-none bg-white/5 hover:bg-white/10 focus-outline:none" |
|
|
/> |
|
|
</div> |
|
|
), |
|
|
IconLeft: ({ ...props }) => <ChevronLeft className="w-4 h-4" />, |
|
|
IconRight: ({ ...props }) => <ChevronRight className="w-4 h-4" />, |
|
|
}} |
|
|
{...props} |
|
|
/> |
|
|
); |
|
|
} |
|
|
Calendar.displayName = 'Calendar'; |
|
|
|
|
|
export { Calendar }; |
|
|
|