import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/solid"; import dayjs from "dayjs"; import isToday from "dayjs/plugin/isToday"; import { useMemo, useState } from "react"; import classNames from "@calcom/lib/classNames"; import { daysInMonth, yyyymmdd } from "@calcom/lib/date-fns"; import { weekdayNames } from "@calcom/lib/weekday"; import { SkeletonContainer } from "@calcom/ui/skeleton"; dayjs.extend(isToday); export type DatePickerProps = { /** which day of the week to render the calendar. Usually Sunday (=0) or Monday (=1) - default: Sunday */ weekStart?: 0 | 1 | 2 | 3 | 4 | 5 | 6; /** Fires whenever a selected date is changed. */ onChange: (date: Date) => void; /** Fires when the month is changed. */ onMonthChange?: (date: Date) => void; /** which date is currently selected (not tracked from here) */ selected?: Date; /** defaults to current date. */ minDate?: Date; /** Furthest date selectable in the future, default = UNLIMITED */ maxDate?: Date; /** locale, any IETF language tag, e.g. "hu-HU" - defaults to Browser settings */ locale: string; /** Defaults to [], which dates are not bookable. Array of valid dates like: ["2022-04-23", "2022-04-24"] */ excludedDates?: string[]; /** defaults to all, which dates are bookable (inverse of excludedDates) */ includedDates?: string[]; /** allows adding classes to the container */ className?: string; /** Shows a small loading spinner next to the month name */ isLoading?: boolean; }; export const Day = ({ date, active, ...props }: JSX.IntrinsicElements["button"] & { active: boolean; date: Date }) => { return ( ); }; const Days = ({ minDate, excludedDates = [], includedDates, browsingDate, weekStart, DayComponent = Day, selected, ...props }: Omit & { DayComponent?: React.FC>; browsingDate: Date; weekStart: number; }) => { // Create placeholder elements for empty days in first week const weekdayOfFirst = new Date(new Date(browsingDate).setDate(1)).getDay(); // memoize to prevent a flicker on redraw on the current day const minDateValueOf = useMemo(() => { return minDate?.valueOf() || new Date().valueOf(); }, [minDate]); const days: (Date | null)[] = Array((weekdayOfFirst - weekStart + 7) % 7).fill(null); for (let day = 1, dayCount = daysInMonth(browsingDate); day <= dayCount; day++) { const date = new Date(new Date(browsingDate).setDate(day)); days.push(date); } return ( <> {days.map((day, idx) => day === null ? (
) : (
{day === null ? (
) : props.isLoading ? ( ) : ( { props.onChange(day); window.scrollTo({ top: 360, behavior: "smooth", }); }} disabled={ (includedDates && !includedDates.includes(yyyymmdd(day))) || excludedDates.includes(yyyymmdd(day)) || day.valueOf() < minDateValueOf } active={selected ? yyyymmdd(selected) === yyyymmdd(day) : false} /> )}
) )} ); }; const DatePicker = ({ weekStart = 0, className, locale, selected, onMonthChange, ...passThroughProps }: DatePickerProps & Partial>) => { const [month, setMonth] = useState(selected ? selected.getMonth() : new Date().getMonth()); const changeMonth = (newMonth: number) => { setMonth(newMonth); if (onMonthChange) { const d = new Date(); d.setMonth(newMonth, 1); onMonthChange(d); } }; return (
{new Date(new Date().setMonth(month)).toLocaleString(locale, { month: "long" })} {" "} {new Date(new Date().setMonth(month)).getFullYear()}
{weekdayNames(locale, weekStart, "short").map((weekDay) => (
{weekDay}
))}
); }; export default DatePicker;