UNPKG

react-native-calendar-picker

Version:

Calendar Picker Component for React Native

264 lines (243 loc) 9.78 kB
import React from 'react'; import { View, Text, TouchableOpacity } from 'react-native'; import PropTypes from 'prop-types'; import { differenceInDays } from 'date-fns/differenceInDays'; import { isAfter } from 'date-fns/isAfter'; import { isBefore } from 'date-fns/isBefore'; import { isSameDay } from 'date-fns/isSameDay'; import { isWithinInterval } from 'date-fns/isWithinInterval'; import { startOfDay } from 'date-fns/startOfDay'; export default function Day(props) { const { day, month, year, styles, customDatesStyles = [], onPressDay, selectedStartDate, selectedEndDate, allowRangeSelection, allowBackwardRangeSelect, selectedDayStyle: propSelectedDayStyle, selectedDisabledDatesTextStyle, selectedRangeStartStyle, selectedRangeStyle, selectedRangeEndStyle, textStyle, todayTextStyle, selectedDayTextStyle: propSelectedDayTextStyle, selectedRangeStartTextStyle, selectedRangeEndTextStyle, minDate, maxDate, disabledDates, disabledDatesTextStyle, minRangeDuration, maxRangeDuration, enableDateChange, } = props; const thisDay = new Date(year, month, day, 12); const today = new Date(); let dateOutOfRange; let computedSelectedDayStyle = styles.dayButton; // may be overridden depending on state let selectedDayTextStyle = {}; let selectedDayStyle; let overrideOutOfRangeTextStyle; let dateIsBeforeMin = false; let dateIsAfterMax = false; let dateIsDisabled = false; let dateRangeLessThanMin = false; let dateRangeGreaterThanMax = false; // First let's check if date is out of range // Check whether props maxDate / minDate are defined. If not supplied, // don't restrict dates. if (maxDate) { dateIsAfterMax = isAfter(startOfDay(thisDay), startOfDay(maxDate)); } if (minDate) { dateIsBeforeMin = isBefore(startOfDay(thisDay), startOfDay(minDate)); } if (disabledDates) { if (Array.isArray(disabledDates) && disabledDates.indexOf(thisDay.valueOf()) >= 0) { dateIsDisabled = true; } else if (disabledDates instanceof Function) { dateIsDisabled = disabledDates(thisDay); } } if (allowRangeSelection && selectedStartDate && !selectedEndDate) { let daysDiff = differenceInDays(thisDay, selectedStartDate); // may be + or - daysDiff = allowBackwardRangeSelect ? Math.abs(daysDiff) : daysDiff; if (maxRangeDuration) { if (Array.isArray(maxRangeDuration)) { let maxRangeEntry = maxRangeDuration.find(mrd => isSameDay(selectedStartDate, mrd.date)); if (maxRangeEntry && daysDiff > maxRangeEntry.maxDuration) { dateRangeGreaterThanMax = true; } } else if (daysDiff > maxRangeDuration) { dateRangeGreaterThanMax = true; } } if (minRangeDuration) { if (Array.isArray(minRangeDuration)) { let minRangeEntry = minRangeDuration.find(mrd => isSameDay(selectedStartDate, mrd.date)); if (minRangeEntry && daysDiff < minRangeEntry.minDuration) { dateRangeLessThanMin = true; } } else if (daysDiff < minRangeDuration) { dateRangeLessThanMin = true; } } if (!allowBackwardRangeSelect && daysDiff < 0) { dateRangeLessThanMin = true; } } dateOutOfRange = dateIsAfterMax || dateIsBeforeMin || dateIsDisabled || dateRangeLessThanMin || dateRangeGreaterThanMax; let isThisDaySameAsSelectedStart = isSameDay(thisDay, selectedStartDate); let isThisDaySameAsSelectedEnd = isSameDay(thisDay, selectedEndDate); let isThisDateInSelectedRange = selectedStartDate && selectedEndDate && isWithinInterval(thisDay, { start: selectedStartDate, end: selectedEndDate }) // If date is in range let's apply styles if (!dateOutOfRange || isThisDaySameAsSelectedStart || isThisDaySameAsSelectedEnd || isThisDateInSelectedRange) { // set today's style let isToday = isSameDay(thisDay, today); if (isToday) { computedSelectedDayStyle = styles.selectedToday; // todayTextStyle prop overrides selectedDayTextColor (created via makeStyles) selectedDayTextStyle = [todayTextStyle || styles.selectedDayLabel, propSelectedDayTextStyle]; } const custom = getCustomDateStyle({ customDatesStyles, date: thisDay }); if (isToday && custom.style) { // Custom date style overrides 'today' style. It may be reset below // by date selection styling. computedSelectedDayStyle = [styles.selectedToday, custom.style]; } // set selected day style if (!allowRangeSelection && selectedStartDate && isThisDaySameAsSelectedStart) { computedSelectedDayStyle = styles.selectedDay; selectedDayTextStyle = [styles.selectedDayLabel, isToday && todayTextStyle, propSelectedDayTextStyle]; // selectedDayStyle prop overrides selectedDayColor (created via makeStyles) selectedDayStyle = propSelectedDayStyle || styles.selectedDayBackground; } // Set selected ranges styles if (allowRangeSelection) { if (selectedStartDate && selectedEndDate) { // Apply style for start date if (isThisDaySameAsSelectedStart) { computedSelectedDayStyle = [styles.startDayWrapper, selectedRangeStyle, selectedRangeStartStyle]; selectedDayTextStyle = [styles.selectedDayLabel, propSelectedDayTextStyle, selectedRangeStartTextStyle]; } // Apply style for end date if (isThisDaySameAsSelectedEnd) { computedSelectedDayStyle = [styles.endDayWrapper, selectedRangeStyle, selectedRangeEndStyle]; selectedDayTextStyle = [styles.selectedDayLabel, propSelectedDayTextStyle, selectedRangeEndTextStyle]; } // Apply style if start date is the same as end date if (isThisDaySameAsSelectedEnd && isThisDaySameAsSelectedStart && isSameDay(selectedEndDate, selectedStartDate)) { computedSelectedDayStyle = [styles.selectedDay, styles.selectedDayBackground, selectedRangeStyle]; selectedDayTextStyle = [styles.selectedDayLabel, propSelectedDayTextStyle, selectedRangeStartTextStyle]; } // Apply style for days inside of range, excluding start & end dates. if (!isThisDaySameAsSelectedEnd && !isThisDaySameAsSelectedStart && isWithinInterval(thisDay, { start: selectedStartDate, end: selectedEndDate })) { computedSelectedDayStyle = [styles.inRangeDay, selectedRangeStyle]; selectedDayTextStyle = [styles.selectedDayLabel, propSelectedDayTextStyle]; } } // Apply style if start date has been selected but end date has not if (selectedStartDate && !selectedEndDate && isThisDaySameAsSelectedStart) { computedSelectedDayStyle = [styles.startDayWrapper, selectedRangeStyle, selectedRangeStartStyle]; selectedDayTextStyle = [styles.selectedDayLabel, propSelectedDayTextStyle, selectedRangeStartTextStyle]; // Override out of range start day text style when minRangeDuration = 1. // This allows selected start date's text to be styled by selectedRangeStartTextStyle // even when it's below minRangeDuration. overrideOutOfRangeTextStyle = selectedRangeStartTextStyle; } } if (dateOutOfRange) { // start or end date selected, and this date outside of range. return ( <View style={[styles.dayWrapper, custom.containerStyle]}> <View style={[custom.style, computedSelectedDayStyle, selectedDayStyle]}> <Text style={[styles.dayLabel, textStyle, styles.disabledText, disabledDatesTextStyle, styles.selectedDisabledText, selectedDisabledDatesTextStyle, overrideOutOfRangeTextStyle ]}> {day} </Text> </View> </View> ); } else { return ( <View style={[styles.dayWrapper, custom.containerStyle]}> <TouchableOpacity disabled={!enableDateChange} style={[custom.style, computedSelectedDayStyle, selectedDayStyle]} onPress={() => onPressDay({ year, month, day })}> <Text style={[styles.dayLabel, textStyle, custom.textStyle, selectedDayTextStyle]}> {day} </Text> </TouchableOpacity> </View> ); } } else { // dateOutOfRange = true, and no selected start or end date. const custom = getCustomDateStyle({ customDatesStyles, date: thisDay }); // Allow customDatesStyles to override disabled dates if allowDisabled set if (!custom.allowDisabled) { custom.containerStyle = null; custom.style = null; custom.textStyle = null; } return ( <View style={[styles.dayWrapper, custom.containerStyle]}> <View style={[styles.dayButton, custom.style]}> <Text style={[textStyle, styles.disabledText, disabledDatesTextStyle, custom.textStyle]}> {day} </Text> </View> </View> ); } } function getCustomDateStyle({ customDatesStyles, date }) { if (Array.isArray(customDatesStyles)) { for (let cds of customDatesStyles) { if (isSameDay(date, new Date(cds.date))) { return { ...cds }; } } } else if (customDatesStyles instanceof Function) { let cds = customDatesStyles(date) || {}; return { ...cds }; } return {}; } Day.propTypes = { styles: PropTypes.shape({}), day: PropTypes.number, onPressDay: PropTypes.func, disabledDates: PropTypes.oneOfType([PropTypes.array, PropTypes.func]), minRangeDuration: PropTypes.oneOfType([PropTypes.array, PropTypes.number]), maxRangeDuration: PropTypes.oneOfType([PropTypes.array, PropTypes.number]), };