import * as React from 'react'; import { F, memoize } from 'ramda'; import { ComponentClass, MouseEvent, Component } from 'react'; import { isSameMonth, isToday } from 'date-fns'; import getPassThrough, { PassTroughFunction } from '../../utils/getPassThrough'; import { DateChecker, PickerDate, SelectedSource } from './types'; import getSelectionMatch, { equalSelectionMatch } from './getSelectionMatch'; export interface DayNodeProps { blocked: boolean; disabled: boolean; highlighted: boolean; inRange: boolean; onClick(event: MouseEvent): void; onMouseEnter(event: MouseEvent): void; onMouseLeave(event: MouseEvent): void; outOfMonth: boolean; selected: boolean; selectedSource: SelectedSource; today: boolean; } export interface DayFactoryArgs { DayNode: ComponentClass; passthrough: PassTroughFunction; } export interface DayProps { day: Date; highlighted?: PickerDate; isDayBlocked: DateChecker; isDayDisabled: DateChecker; onClick(day: Date, event: MouseEvent): void; onMouseEnter(day: Date, event: MouseEvent): void; onMouseLeave(day: Date, event: MouseEvent): void; selected?: PickerDate; viewDate: Date; } export type DayType = ComponentClass; export default function dayFactory({ DayNode, passthrough, }: DayFactoryArgs): DayType { const passProps = getPassThrough(passthrough); return class DayComponent extends Component { public static defaultProps = { isDayBlocked: F, isDayDisabled: F, }; public shouldComponentUpdate(nextProps) { if ( nextProps.isDayBlocked !== this.props.isDayBlocked || nextProps.isDayDisabled !== this.props.isDayDisabled || nextProps.day.getTime() !== this.props.day.getTime() || nextProps.viewDate.getTime() !== this.props.viewDate.getTime() ) { return true; } if ( (this.props.selected || nextProps.selected) && !equalSelectionMatch( this.getSelectedMatch( nextProps.day, this.props.selected, this.props.viewDate, ), this.getSelectedMatch( nextProps.day, nextProps.selected, nextProps.viewDate, ), ) ) { return true; } if ( (this.props.highlighted || nextProps.highlighted) && !equalSelectionMatch( this.getHighlightedMatch( nextProps.day, this.props.highlighted, this.props.viewDate, ), this.getHighlightedMatch( nextProps.day, nextProps.highlighted, nextProps.viewDate, ), ) ) { return true; } return false; } private getSelectedMatch = memoize(getSelectionMatch); private getHighlightedMatch = memoize(getSelectionMatch); private handleClick = event => { const { day, isDayDisabled, onClick, viewDate } = this.props; if (isSameMonth(day, viewDate) && !isDayDisabled(day)) { onClick(day, event); } }; private handleMouseEnter = event => { const { day, isDayDisabled, onMouseEnter, viewDate } = this.props; if (isSameMonth(day, viewDate) && !isDayDisabled(day)) { onMouseEnter(day, event); } }; private handleMouseLeave = event => { const { day, isDayDisabled, onMouseLeave, viewDate } = this.props; if (isSameMonth(day, viewDate) && !isDayDisabled(day)) { onMouseLeave(day, event); } }; public render() { const { day, highlighted, isDayBlocked, isDayDisabled, selected, viewDate, ...rest, } = this.props; const selectedMatch = this.getSelectedMatch(day, selected, viewDate); const highlightedMatch = this.getHighlightedMatch( day, highlighted, viewDate, ); const isHighlighted = highlightedMatch.selected || highlightedMatch.inRange; return ( {day.getDate()} ); } }; }