import React, {
ReactNode,
useEffect,
useRef,
useState,
forwardRef,
} from "react";
import classNames from "classnames";
import ReactDatePicker, { ReactDatePickerProps } from "react-datepicker";
import { addMonths, format, isEqual, isValid, startOfMonth } from "date-fns";
import { Input, INPUT_ICON_POSITION } from "../Input";
import { bemHOF } from "../../utilities/bem";
import { PopoverMenu } from "../PopoverMenu";
import { Text } from "../Text";
import { Box, BoxProps } from "../Box";
import { Heading } from "../Heading";
import { Flex } from "../Flex";
import { Button, BUTTON_VARIANT } from "../Button";
import { ICON_TYPE } from "../Icon";
const cn = bemHOF("DatePicker");
const MONTH_YEAR_FORMAT = "MMMM yyyy";
export const DatePickerInput = forwardRef((props, ref) => (
));
const renderDayContents = (day: number) => {
return (
{day}
);
};
interface CustomHeaderProps {
date: Date;
decreaseMonth: () => void;
increaseMonth: () => void;
prevMonthButtonDisabled: boolean;
nextMonthButtonDisabled: boolean;
}
type HeaderPropsState = CustomHeaderProps | null;
const CustomHeaderButtons = ({
decreaseMonth,
increaseMonth,
prevMonthButtonDisabled,
nextMonthButtonDisabled,
}: Omit) => (
);
const CustomHeader = ({
date,
decreaseMonth,
increaseMonth,
prevMonthButtonDisabled,
nextMonthButtonDisabled,
monthsShown,
setHeaderProps,
}: CustomHeaderProps & {
monthsShown: number;
setHeaderProps: (p: CustomHeaderProps) => void;
}) => {
useEffect(() => {
if (monthsShown > 1) {
setHeaderProps({
date,
decreaseMonth,
increaseMonth,
prevMonthButtonDisabled,
nextMonthButtonDisabled,
});
}
}, [
monthsShown,
date,
decreaseMonth,
increaseMonth,
prevMonthButtonDisabled,
nextMonthButtonDisabled,
setHeaderProps,
]);
if (monthsShown > 1) {
return null;
}
return (
{format(date, MONTH_YEAR_FORMAT)}
);
};
const generateCustomHeaderRenderer = ({
monthsShown,
setHeaderProps,
}: {
monthsShown: number;
setHeaderProps: (p: CustomHeaderProps) => void;
}) => {
return (customHeaderProps: CustomHeaderProps) => {
return (
);
};
};
const checkHeaderPropsEquality = (
prev: HeaderPropsState,
next: CustomHeaderProps,
) => {
if (!prev) return false;
if (!isEqual(prev.date, next.date)) return false;
if (prev.prevMonthButtonDisabled !== next.prevMonthButtonDisabled)
return false;
if (prev.nextMonthButtonDisabled !== next.nextMonthButtonDisabled)
return false;
return true;
};
const DAY_ABBREVIATIONS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
const DayNames = () => (
{DAY_ABBREVIATIONS.map((abv) => (
{abv}
))}
);
const MultiMonthHeader = ({
headerProps,
monthsShown,
inline,
calInstance,
}: {
headerProps: HeaderPropsState;
monthsShown: number;
inline: boolean;
calInstance?: ReactDatePicker | null;
}) => {
if (!headerProps) return null;
const { date, ...restHeaderProps } = headerProps;
const dateStartOfMonth = startOfMonth(headerProps.date);
const getMonthSelectedIn = () => {
if (!calInstance) return 0;
const { state } = calInstance;
if (
state.hasOwnProperty("monthSelectedIn") &&
typeof (state as any).monthSelectedIn === "number"
) {
return (state as any).monthSelectedIn as number;
}
return 0;
};
const monthSelectedIn = getMonthSelectedIn();
return (
{[...Array(monthsShown)].map((_, i) => {
const dateThisMonth = addMonths(dateStartOfMonth, i - monthSelectedIn);
const displayDate = format(dateThisMonth, MONTH_YEAR_FORMAT);
const showButtons = !inline && monthsShown === i + 1;
return (
{displayDate}
{showButtons && }
);
})}
);
};
const getPopoverMenuWithMultiMonthHeader = ({
headerProps,
monthsShown,
inline,
calInstance,
}: {
headerProps: HeaderPropsState;
monthsShown: number;
inline: boolean;
calInstance: ReactDatePicker | null;
}) => {
return ({
children,
className,
}: {
children: ReactNode;
className?: string;
}) => {
return (
{children}
);
};
};
const getCalendarContainer = ({
inline,
monthsShown,
headerProps,
calInstance,
}: {
inline: boolean;
monthsShown: number;
headerProps: HeaderPropsState;
calInstance: ReactDatePicker | null;
}) => {
if (inline) return undefined;
if (monthsShown > 1)
return getPopoverMenuWithMultiMonthHeader({
headerProps,
monthsShown,
inline,
calInstance,
});
return ({ children, className }: BoxProps) => (
{children}
);
};
const UnwrappedDatePicker = ({
className,
monthsShown = 1,
calendarClassName,
customInput = ,
onHeaderPropsUpdate,
inline = false,
popperModifiers,
headerProps,
rdpRef,
...rest
}: ReactDatePickerProps & {
onHeaderPropsUpdate: (props: CustomHeaderProps) => void;
headerProps: HeaderPropsState;
rdpRef: React.RefObject;
}) => {
const isMultiMonth = monthsShown > 1;
const calInstance = rdpRef.current;
return (
);
};
export const DatePicker = ({
selected: selectedDateProp,
...props
}: ReactDatePickerProps) => {
const [headerProps, setHeaderProps] = useState(null);
const isMultiMonth = !!props.monthsShown && props.monthsShown > 1;
const rdpRef = useRef(null);
const calInstance = rdpRef.current;
let selected = selectedDateProp;
if (selected) {
selected = isValid(selected) ? selected : null;
}
const handleHeaderPropsUpdate = (newHeaderProps: CustomHeaderProps) => {
const equal = checkHeaderPropsEquality(headerProps, newHeaderProps);
if (equal) return;
setHeaderProps(newHeaderProps);
};
return isMultiMonth && props.inline ? (
{headerProps && (
)}
{headerProps && (
)}
) : (
);
};
DatePicker.Input = DatePickerInput;