33import * as React from "react" ;
44import { ChevronLeftIcon , ChevronRightIcon } from "@heroicons/react/20/solid" ;
55import { format } from "date-fns" ;
6- import { DayPicker } from "react-day-picker" ;
6+ import { DayPicker , useDayPicker } from "react-day-picker" ;
77import { cn } from "~/utils/cn" ;
88
99export type CalendarProps = React . ComponentProps < typeof DayPicker > ;
1010
11+ const navButtonClass =
12+ "size-7 rounded-[3px] bg-secondary border border-charcoal-600 text-text-bright hover:bg-charcoal-600 hover:border-charcoal-550 transition inline-flex items-center justify-center" ;
13+
14+ function CustomMonthCaption ( { calendarMonth } : { calendarMonth : { date : Date } } ) {
15+ const { goToMonth, nextMonth, previousMonth } = useDayPicker ( ) ;
16+
17+ return (
18+ < div className = "flex w-full items-center justify-between px-1" >
19+ < button
20+ type = "button"
21+ className = { navButtonClass }
22+ disabled = { ! previousMonth }
23+ onClick = { ( ) => previousMonth && goToMonth ( previousMonth ) }
24+ aria-label = "Go to previous month"
25+ >
26+ < ChevronLeftIcon className = "size-4" />
27+ </ button >
28+ < div className = "flex items-center gap-2" >
29+ < select
30+ className = "rounded border border-charcoal-600 bg-charcoal-750 px-2 py-1 text-sm text-text-bright focus:border-charcoal-500 focus:outline-none"
31+ value = { calendarMonth . date . getMonth ( ) }
32+ onChange = { ( e ) => {
33+ const newDate = new Date ( calendarMonth . date ) ;
34+ newDate . setMonth ( parseInt ( e . target . value ) ) ;
35+ goToMonth ( newDate ) ;
36+ } }
37+ >
38+ { Array . from ( { length : 12 } , ( _ , i ) => (
39+ < option key = { i } value = { i } >
40+ { format ( new Date ( 2000 , i ) , "MMM" ) }
41+ </ option >
42+ ) ) }
43+ </ select >
44+ < select
45+ className = "rounded border border-charcoal-600 bg-charcoal-750 px-2 py-1 text-sm text-text-bright focus:border-charcoal-500 focus:outline-none"
46+ value = { calendarMonth . date . getFullYear ( ) }
47+ onChange = { ( e ) => {
48+ const newDate = new Date ( calendarMonth . date ) ;
49+ newDate . setFullYear ( parseInt ( e . target . value ) ) ;
50+ goToMonth ( newDate ) ;
51+ } }
52+ >
53+ { Array . from ( { length : 100 } , ( _ , i ) => {
54+ const year = new Date ( ) . getFullYear ( ) - 50 + i ;
55+ return (
56+ < option key = { year } value = { year } >
57+ { year }
58+ </ option >
59+ ) ;
60+ } ) }
61+ </ select >
62+ </ div >
63+ < button
64+ type = "button"
65+ className = { navButtonClass }
66+ disabled = { ! nextMonth }
67+ onClick = { ( ) => nextMonth && goToMonth ( nextMonth ) }
68+ aria-label = "Go to next month"
69+ >
70+ < ChevronRightIcon className = "size-4" />
71+ </ button >
72+ </ div >
73+ ) ;
74+ }
75+
1176export function Calendar ( {
1277 className,
1378 classNames,
@@ -17,24 +82,21 @@ export function Calendar({
1782 return (
1883 < DayPicker
1984 showOutsideDays = { showOutsideDays }
85+ weekStartsOn = { 1 }
2086 className = { cn ( "p-3" , className ) }
2187 classNames = { {
2288 months : "flex flex-col sm:flex-row gap-2" ,
2389 month : "flex flex-col gap-4" ,
2490 month_caption : "flex justify-center pt-1 relative items-center w-full" ,
2591 caption_label : "sr-only" ,
26- nav : "flex items-center justify-between w-full absolute inset-x-0 top-1 px-3" ,
27- button_previous :
28- "size-6 rounded-[3px] bg-secondary border border-charcoal-600 text-text-bright hover:bg-charcoal-600 hover:border-charcoal-550 transition inline-flex items-center justify-center" ,
29- button_next :
30- "size-6 rounded-[3px] bg-secondary border border-charcoal-600 text-text-bright hover:bg-charcoal-600 hover:border-charcoal-550 transition inline-flex items-center justify-center" ,
92+ nav : "hidden" ,
3193 month_grid : "w-full border-collapse" ,
3294 weekdays : "flex" ,
3395 weekday : "text-text-dimmed rounded-md w-8 font-normal text-[0.8rem]" ,
3496 week : "flex w-full mt-2" ,
3597 day : "relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-charcoal-700 [&:has([aria-selected].day-outside)]:bg-charcoal-700/50 [&:has([aria-selected].day-range-end)]:rounded-r-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md" ,
3698 day_button : cn (
37- "size-8 p-0 font-normal text-text-bright" ,
99+ "size-8 p-0 font-normal text-text-bright rounded-md " ,
38100 "hover:bg-charcoal-700 hover:text-text-bright" ,
39101 "focus:bg-charcoal-700 focus:text-text-bright focus:outline-none" ,
40102 "aria-selected:opacity-100"
@@ -55,15 +117,7 @@ export function Calendar({
55117 ...classNames ,
56118 } }
57119 components = { {
58- Chevron : ( { orientation } ) => {
59- if ( orientation === "left" ) {
60- return < ChevronLeftIcon className = "size-4" /> ;
61- }
62- return < ChevronRightIcon className = "size-4" /> ;
63- } ,
64- } }
65- formatters = { {
66- formatMonthDropdown : ( date ) => format ( date , "MMM" ) ,
120+ MonthCaption : CustomMonthCaption ,
67121 } }
68122 { ...props }
69123 />
0 commit comments