1+ "use client"
2+
3+ import * as React from "react"
4+ import {
5+ ChevronDownIcon ,
6+ ChevronLeftIcon ,
7+ ChevronRightIcon ,
8+ } from "lucide-react"
9+ import { DayButton , DayPicker , getDefaultClassNames } from "react-day-picker"
10+
11+ import { cn } from "@/lib/utils"
12+ import { Button , buttonVariants } from "@/components/retroui/Button"
13+
14+ function Calendar ( {
15+ className,
16+ classNames,
17+ showOutsideDays = true ,
18+ captionLayout = "label" ,
19+ buttonVariant = "ghost" ,
20+ formatters,
21+ components,
22+ ...props
23+ } : React . ComponentProps < typeof DayPicker > & {
24+ buttonVariant ?: React . ComponentProps < typeof Button > [ "variant" ]
25+ } ) {
26+ const defaultClassNames = getDefaultClassNames ( )
27+
28+ return (
29+ < DayPicker
30+ showOutsideDays = { showOutsideDays }
31+ className = { cn (
32+ "bg-background w-full outline-2 shadow-md group/calendar p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent" ,
33+ String . raw `rtl:**:[.rdp-button\_next>svg]:rotate-180` ,
34+ String . raw `rtl:**:[.rdp-button\_previous>svg]:rotate-180` ,
35+ className
36+ ) }
37+ captionLayout = { captionLayout }
38+ formatters = { {
39+ formatMonthDropdown : ( date ) =>
40+ date . toLocaleString ( "default" , { month : "short" } ) ,
41+ ...formatters ,
42+ } }
43+ classNames = { {
44+ root : cn ( "w-fit" , defaultClassNames . root ) ,
45+ months : cn (
46+ "flex gap-4 flex-col md:flex-row relative" ,
47+ defaultClassNames . months
48+ ) ,
49+ month : cn ( "flex flex-col w-full gap-4 font-head" , defaultClassNames . month ) ,
50+ nav : cn (
51+ "flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between" ,
52+ defaultClassNames . nav
53+ ) ,
54+ button_previous : cn (
55+ buttonVariants ( { variant : buttonVariant } ) ,
56+ "size-8 p-2 border-2 rounded select-none" ,
57+ defaultClassNames . button_previous
58+ ) ,
59+ button_next : cn (
60+ buttonVariants ( { variant : buttonVariant } ) ,
61+ "size-8 p-2 border-2 rounded select-none" ,
62+ defaultClassNames . button_next
63+ ) ,
64+ month_caption : cn (
65+ "flex items-center justify-center h-(--cell-size) w-full px-(--cell-size)" ,
66+ defaultClassNames . month_caption
67+ ) ,
68+ dropdowns : cn (
69+ "w-full flex items-center text-sm font-medium justify-center h-(--cell-size) gap-1.5" ,
70+ defaultClassNames . dropdowns
71+ ) ,
72+ dropdown_root : cn (
73+ "relative has-focus:outline-ring outline outline-input has-focus:ring-ring/50 has-focus:ring-[3px] rounded" ,
74+ defaultClassNames . dropdown_root
75+ ) ,
76+ dropdown : cn (
77+ "absolute bg-popover inset-0 opacity-0" ,
78+ defaultClassNames . dropdown
79+ ) ,
80+ caption_label : cn (
81+ "select-none font-medium" ,
82+ captionLayout === "label"
83+ ? "text-base"
84+ : "rounded-none pl-2 pr-1 flex items-center gap-1 text-sm h-8 [&>svg]:text-muted-foreground [&>svg]:size-3.5" ,
85+ defaultClassNames . caption_label
86+ ) ,
87+ table : "w-full outline-collapse" ,
88+ weekdays : cn ( "flex" , defaultClassNames . weekdays ) ,
89+ weekday : cn (
90+ "flex-1 font-normal text-sm select-none" ,
91+ defaultClassNames . weekday
92+ ) ,
93+ week : cn ( "flex w-full mt-2" , defaultClassNames . week ) ,
94+ week_number_header : cn (
95+ "select-none w-(--cell-size)" ,
96+ defaultClassNames . week_number_header
97+ ) ,
98+ week_number : cn (
99+ "text-[0.8rem] select-none text-muted-foreground" ,
100+ defaultClassNames . week_number
101+ ) ,
102+ day : cn (
103+ "relative w-full h-full p-0 text-center [&:last-child[data-selected=true]_button]:rounded-r group/day aspect-square select-none" ,
104+ props . showWeekNumber
105+ ? "[&:nth-child(2)[data-selected=true]_button]:rounded-l"
106+ : "[&:first-child[data-selected=true]_button]:rounded-l" ,
107+ defaultClassNames . day
108+ ) ,
109+ today : cn (
110+ "bg-accent text-accent-foreground rounded data-[selected=true]:rounded-none" ,
111+ defaultClassNames . today
112+ ) ,
113+ outside : cn (
114+ "text-muted-foreground aria-selected:text-muted-foreground opacity-80" ,
115+ defaultClassNames . outside
116+ ) ,
117+ disabled : cn (
118+ "text-muted-foreground opacity-50" ,
119+ defaultClassNames . disabled
120+ ) ,
121+ hidden : cn ( "invisible" , defaultClassNames . hidden ) ,
122+ ...classNames ,
123+ } }
124+ components = { {
125+ Root : ( { className, rootRef, ...props } ) => {
126+ return (
127+ < div
128+ data-slot = "calendar"
129+ ref = { rootRef }
130+ className = { cn ( className ) }
131+ { ...props }
132+ />
133+ )
134+ } ,
135+ Chevron : ( { className, orientation, ...props } ) => {
136+ if ( orientation === "left" ) {
137+ return (
138+ < ChevronLeftIcon className = { cn ( "size-4" , className ) } { ...props } />
139+ )
140+ }
141+
142+ if ( orientation === "right" ) {
143+ return (
144+ < ChevronRightIcon
145+ className = { cn ( "size-4" , className ) }
146+ { ...props }
147+ />
148+ )
149+ }
150+
151+ return (
152+ < ChevronDownIcon className = { cn ( "size-4" , className ) } { ...props } />
153+ )
154+ } ,
155+ DayButton : CalendarDayButton ,
156+ WeekNumber : ( { children, ...props } ) => {
157+ return (
158+ < td { ...props } >
159+ < div className = "flex size-(--cell-size) items-center justify-center text-center" >
160+ { children }
161+ </ div >
162+ </ td >
163+ )
164+ } ,
165+ ...components ,
166+ } }
167+ { ...props }
168+ />
169+ )
170+ }
171+
172+ function CalendarDayButton ( {
173+ className,
174+ day,
175+ modifiers,
176+ ...props
177+ } : React . ComponentProps < typeof DayButton > ) {
178+ const defaultClassNames = getDefaultClassNames ( )
179+
180+ const ref = React . useRef < HTMLButtonElement > ( null )
181+ React . useEffect ( ( ) => {
182+ if ( modifiers . focused ) ref . current ?. focus ( )
183+ } , [ modifiers . focused ] )
184+
185+ return (
186+ < Button
187+ ref = { ref }
188+ variant = "ghost"
189+ size = "icon"
190+ data-day = { day . date . toLocaleDateString ( ) }
191+ data-selected-single = {
192+ modifiers . selected &&
193+ ! modifiers . range_start &&
194+ ! modifiers . range_end &&
195+ ! modifiers . range_middle
196+ }
197+ data-range-start = { modifiers . range_start }
198+ data-range-end = { modifiers . range_end }
199+ data-range-middle = { modifiers . range_middle }
200+ className = { cn (
201+ "font-sans flex justify-center items-center data-[selected-single=true]:shadow-md data-[selected-single=true]:outline-2 outline-border data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-secondary data-[range-middle=true]:hover:text-secondary-foreground data-[range-middle=true]:text-secondary-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring-1 group-data-[focused=true]/day:ring-ring/50 dark:hover:text-accent-foreground flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[2px] data-[range-end=true]:rounded-none data-[range-end=true]:rounded-none data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-none [&>span]:text-xs" ,
202+ defaultClassNames . day ,
203+ className
204+ ) }
205+ { ...props }
206+ />
207+ )
208+ }
209+
210+ export { Calendar , CalendarDayButton }
0 commit comments