Skip to content

Instantly share code, notes, and snippets.

@sytabaresa
Created June 22, 2024 01:41
Show Gist options
  • Save sytabaresa/5acbb5aac6c9ce362571508fb0946d31 to your computer and use it in GitHub Desktop.
Save sytabaresa/5acbb5aac6c9ce362571508fb0946d31 to your computer and use it in GitHub Desktop.
Tailwindcss - daisyUI calendar component with dayjs as datetime library
import { useTranslation } from "@refinedev/core"
import { FaChevronLeft, FaChevronRight } from "react-icons/fa6"
import dayjs from "dayjs";
import weekday from "dayjs/plugin/weekday";
import weekOfYear from "dayjs/plugin/weekOfYear";
import { HTMLAttributes, useEffect, useState } from "react";
dayjs.extend(weekOfYear);
function getNumberOfDaysInMonth(year: string, month: string) {
return dayjs(`${year}-${month}-01`).daysInMonth();
}
function getWeekday(date: dayjs.Dayjs | Date | string) {
return dayjs(date).day();
}
type days = {
date: string,
dayOfMonth: number,
isCurrentMonth: Boolean
}
function createDaysForCurrentMonth(year: string, month: string): days[] {
return [...Array(getNumberOfDaysInMonth(year, month))].map((day, index) => {
return {
date: dayjs(`${year}-${month}-${index + 1}`).format("YYYY-MM-DD"),
dayOfMonth: index + 1,
isCurrentMonth: true
};
});
}
function createDaysForPreviousMonth(currentMonthDays: days[], year: string, month: string) {
const firstDayOfTheMonthWeekday = getWeekday(currentMonthDays[0].date);
const previousMonth = dayjs(`${year}-${month}-01`).subtract(1, "month");
// Cover first day of the month being sunday (firstDayOfTheMonthWeekday === 0)
const visibleNumberOfDaysFromPreviousMonth = firstDayOfTheMonthWeekday
? firstDayOfTheMonthWeekday - 1
: 6;
const previousMonthLastMondayDayOfMonth = dayjs(currentMonthDays[0].date)
.subtract(visibleNumberOfDaysFromPreviousMonth, "day")
.date();
return [...Array(visibleNumberOfDaysFromPreviousMonth)].map((day, index) => {
return {
date: dayjs(
`${previousMonth.year()}-${previousMonth.month() + 1}-${previousMonthLastMondayDayOfMonth + index
}`
).format("YYYY-MM-DD"),
dayOfMonth: previousMonthLastMondayDayOfMonth + index,
isCurrentMonth: false
};
});
}
function createDaysForNextMonth(currentMonthDays: days[], year: string, month: string) {
const lastDayOfTheMonthWeekday = getWeekday(
`${year}-${month}-${currentMonthDays.length}`
);
const nextMonth = dayjs(`${year}-${month}-01`).add(1, "month");
const visibleNumberOfDaysFromNextMonth = lastDayOfTheMonthWeekday
? 7 - lastDayOfTheMonthWeekday
: lastDayOfTheMonthWeekday;
return [...Array(visibleNumberOfDaysFromNextMonth)].map((day, index) => {
return {
date: dayjs(
`${nextMonth.year()}-${nextMonth.month() + 1}-${index + 1}`
).format("YYYY-MM-DD"),
dayOfMonth: index + 1,
isCurrentMonth: false
};
});
}
export interface CalendarProps extends HTMLAttributes<HTMLDivElement> {
value?: dayjs.Dayjs | Date | string
onChange?: (value: any) => void
}
export const Calendar = ({ value = dayjs(), onChange, ...rest }: CalendarProps) => {
const { translate } = useTranslation()
const [currentDate, setCurrentDate] = useState(value)
// <!-- Calendar -->
useEffect(() => {
setCurrentDate(value)
}, [value])
const originalDate = dayjs(value).format("YYYY-MM-DD")
const _date = dayjs(currentDate)
const year = _date.format("YYYY");
const month = _date.format("M");
const fdate = _date.format("YYYY-MM-DD")
const currentMonthDays = createDaysForCurrentMonth(year, month);
const previousMonthDays = createDaysForPreviousMonth(currentMonthDays, year, month);
const nextMonthDays = createDaysForNextMonth(currentMonthDays, year, month);
const days = [...previousMonthDays, ...currentMonthDays, ...nextMonthDays];
// console.log(days);
return <div {...rest}>
<div className="flex gap-4 items-center">
<span className="flex-1">{`${_date.format("MM")}-${year}`}</span>
<span className="btn btn-sm btn-ghost" onClick={() => setCurrentDate(dayjs(currentDate).subtract(1, 'month'))}><FaChevronLeft /></span>
<span className="btn btn-sm btn-ghost" onClick={() => setCurrentDate(dayjs(currentDate).add(1, 'month'))}><FaChevronRight /></span>
</div>
<div className="grid grid-cols-7 grid-rows-7 gap-4">
{[...Array(7).keys()].map((d) =>
<div className="w-8 text-center font-semibold">{translate(`common.daysWeekShorter.${(d + 1) % 7}`)}</div>
)}
{days.map(d =>
<div className={['w-8 h-8 flex items-center justify-center rounded-full hover:bg-primary hover:bg-opacity-50',
originalDate == fdate && fdate == d.date ? 'bg-primary ' : '',
d.isCurrentMonth ? '' : 'text-base-300'].join(' ')}
onClick={() => onChange?.(d.date)}>{d.dayOfMonth}</div>
)}
</div>
</div>
}
@sytabaresa
Copy link
Author

Use:

<div className="dropdown">
   <input value={value} onChange={onChange} tabIndex={0} type="text" className="grow" />
   <Calendar value={value} onChange={onChange} tabIndex={0} className="dropdown-content z-[1] bg-base-100 p-2 w-72 shadow-lg" />
 </div>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment