import * as React from 'react'; import { range, memoize } from 'ramda'; import { ComponentClass, Component } from 'react'; import getPassThrough, { PassTroughFunction } from '../../utils/getPassThrough'; import getFullDayOfWeek from './locale/getFullDayOfWeek'; import getFullMonth from './locale/getFullMonth'; import getMonthMatrix from './getMonthMatrix'; import getMonthAffected from './getMonthAffected'; import { PickerDate, DateChecker } from './types'; import { DayType } from './Day'; export interface MonthTitleProps { viewDate: Date; } export interface WeekdayProps { children: string; weekDay: number; } export interface MonthProps { highlighted?: PickerDate; isDayBlocked: DateChecker; isDayDisabled: DateChecker; locale: string | object; onDayClick(day: Date, event: React.MouseEvent): void; onDayMouseEnter(day: Date, event: React.MouseEvent): void; onDayMouseLeave(day: Date, event: React.MouseEvent): void; selected?: PickerDate; sundayFirstDayOfWeek: boolean; viewDate: Date; } export type MonthNodes = | 'Day' | 'DaysWeek' | 'DaysWrapper' | 'Weekday' | 'MonthTitle' | 'MonthWrapper' | 'WeekDay' | 'WeekdaysWrapper'; export interface MonthFactoryArgs { /** * Used to render each Day of the Month. */ Day: DayType; /** * Used wrap each bunch of days that compose a week. */ DaysWeek: ComponentClass; /** * Used to render a wrapper around all weeks.. */ DaysWrapper: ComponentClass; /** * Used to render the month title */ MonthTitle: ComponentClass; /** * Used as a wrapper of the whole month component. */ MonthWrapper: ComponentClass; /** * Use it to render each Weekday */ Weekday: ComponentClass; /** * Used to render a wrapper around all weekdays */ WeekdaysWrapper: ComponentClass; /** * Use it to customize how props are passed around. */ passthrough: PassTroughFunction; } export type MonthType = ComponentClass; export default function monthFactory({ Day, DaysWeek, DaysWrapper, MonthTitle, MonthWrapper, Weekday, WeekdaysWrapper, passthrough, }: MonthFactoryArgs): MonthType { const passProps = getPassThrough(passthrough); return class MonthComponent extends Component { public shouldComponentUpdate(nextProps) { if ( this.props.viewDate.getTime() !== nextProps.viewDate.getTime() || this.props.isDayBlocked !== nextProps.isDayBlocked || this.props.isDayDisabled !== nextProps.isDayDisabled || this.props.onDayClick !== nextProps.onDayClick || this.props.onDayMouseEnter !== nextProps.onDayMouseEnter || this.props.onDayMouseLeave !== nextProps.onDayMouseLeave || this.props.sundayFirstDayOfWeek !== nextProps.sundayFirstDayOfWeek ) { return true; } if ( getMonthAffected(this.props.viewDate, this.props.selected) || getMonthAffected(nextProps.viewDate, nextProps.selected) || getMonthAffected(this.props.viewDate, this.props.highlighted) || getMonthAffected(nextProps.viewDate, nextProps.highlighted) ) { return true; } return false; } private getMonthMatrix = memoize(getMonthMatrix); private renderDays = () => { const { highlighted, isDayBlocked, isDayDisabled, onDayClick, onDayMouseEnter, onDayMouseLeave, selected, sundayFirstDayOfWeek, viewDate, } = this.props; const monthMatrix = this.getMonthMatrix( viewDate.getTime(), sundayFirstDayOfWeek, ); const weeks: JSX.Element[] = []; let days; for (let i = 0; i < monthMatrix.length; i++) { days = []; for (let j = 0; j < 7; j++) { const monthDay = monthMatrix[i][j]; days[j] = ( ); } weeks[i] = ( {days} ); } return weeks; }; private renderWeekDay = weekDay => ( {getFullDayOfWeek(weekDay, this.props.locale)} ); private renderWeekDays = () => { const idxs = range(0, 7); const { sundayFirstDayOfWeek } = this.props; const sortedDaysIdx = sundayFirstDayOfWeek ? idxs : [...idxs.slice(1), idxs[0]]; return sortedDaysIdx.map(this.renderWeekDay); }; public render() { const { highlighted, isDayBlocked, isDayDisabled, locale, onDayClick, onDayMouseEnter, selected, sundayFirstDayOfWeek, viewDate, ...rest, } = this.props; return ( {getFullMonth(viewDate)} {viewDate.getFullYear()} {this.renderWeekDays()} {this.renderDays()} ); } }; }