Skip to content

Commit 52614c9

Browse files
committed
feat: create date-picker ui
1 parent 2ea10ef commit 52614c9

File tree

1 file changed

+120
-0
lines changed

1 file changed

+120
-0
lines changed

src/components/ui/date-picker.tsx

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import * as React from "react";
2+
import { ChevronLeft, ChevronRight } from "lucide-react";
3+
4+
import { cn } from "@/lib/utils";
5+
6+
interface DatePickerProps extends React.HTMLAttributes<HTMLDivElement> {
7+
preselectedDate?: Date;
8+
onDateChange?: (date: Date) => void;
9+
}
10+
11+
const DatePicker = React.forwardRef<HTMLDivElement, DatePickerProps>(
12+
({ className, preselectedDate, onDateChange, ...props }, ref) => {
13+
const [currentDate, setCurrentDate] = React.useState(new Date());
14+
const [selectedDate, setSelectedDate] = React.useState<Date>(preselectedDate || new Date());
15+
16+
const daysInMonth = (month: number, year: number) => {
17+
return new Date(year, month + 1, 0).getDate();
18+
};
19+
20+
const startDayOfMonth = (month: number, year: number) => {
21+
return new Date(year, month, 1).getDay();
22+
};
23+
24+
const handlePrevMonth = () => {
25+
setCurrentDate(
26+
new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1)
27+
);
28+
};
29+
30+
const handleNextMonth = () => {
31+
setCurrentDate(
32+
new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1)
33+
);
34+
};
35+
36+
const handleDateClick = (day: number) => {
37+
const newDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), day);
38+
setSelectedDate(newDate);
39+
if (onDateChange) {
40+
onDateChange(newDate);
41+
}
42+
};
43+
44+
const renderDays = () => {
45+
const days = [];
46+
const daysInCurrentMonth = daysInMonth(
47+
currentDate.getMonth(),
48+
currentDate.getFullYear()
49+
);
50+
const startDay = startDayOfMonth(
51+
currentDate.getMonth(),
52+
currentDate.getFullYear()
53+
);
54+
55+
for (let i = 0; i < startDay; i++) {
56+
days.push(<div key={`empty-${i}`} />);
57+
}
58+
59+
for (let day = 1; day <= daysInCurrentMonth; day++) {
60+
days.push(
61+
<div
62+
key={day}
63+
className={cn(
64+
"grid-item place-self-center cursor-pointer rounded-lg w-[30px] h-[30px] flex items-center justify-center hover:bg-primary/30 transition-colors",
65+
{
66+
"border-0 text-primary-foreground bg-primary/80 hover:bg-primary/80":
67+
selectedDate?.getDate() === day &&
68+
selectedDate?.getMonth() === currentDate.getMonth() &&
69+
selectedDate?.getFullYear() === currentDate.getFullYear(),
70+
}
71+
)}
72+
onClick={() => handleDateClick(day)}
73+
>
74+
{day}
75+
</div>
76+
);
77+
}
78+
79+
return days;
80+
};
81+
82+
return (
83+
<div
84+
ref={ref}
85+
className={cn(
86+
"flex flex-col items-center max-h-[300px] w-full sm:w-lg p-1 border-2 rounded-md text-xs",
87+
className
88+
)}
89+
{...props}
90+
>
91+
<div className="flex flex-row items-center gap-2 mb-2">
92+
<ChevronLeft
93+
onClick={handlePrevMonth}
94+
className="cursor-pointer rounded-md border-2 hover:bg-primary/30 hover:border-0 transition-colors"
95+
/>
96+
<span className="flex text-lg font-bold text-center">
97+
{currentDate.toLocaleString("default", { month: "long" })}{" "}
98+
{currentDate.getFullYear()}
99+
</span>
100+
<ChevronRight
101+
onClick={handleNextMonth}
102+
className="cursor-pointer rounded-md border-2 hover:bg-primary/30 hover:border-0 transition-colors"
103+
/>
104+
</div>
105+
<div className="h-full w-full grid grid-cols-7 gap-1 p-2">
106+
{["S", "M", "T", "W", "T", "F", "S"].map((date, index) => (
107+
<div key={`${date}-${index}`} className="grid-item place-self-center">
108+
{date}
109+
</div>
110+
))}
111+
{renderDays()}
112+
</div>
113+
</div>
114+
);
115+
}
116+
);
117+
118+
DatePicker.displayName = "DatePicker";
119+
120+
export default DatePicker;

0 commit comments

Comments
 (0)