UNPKG

react-native-lunar-calendars

Version:

React Native Calendar Components with Lunar Calendar Support - Fork of react-native-calendars with Vietnamese lunar calendar functionality

292 lines (260 loc) 9.71 kB
import React, { Component } from 'react'; import { View, ViewPropTypes } from 'react-native'; import PropTypes from 'prop-types'; import XDate from 'xdate'; import dateutils from '../dateutils'; import { xdateToData, parseDate } from '../interface'; import styleConstructor from './style'; import Day from './day/basic'; import UnitDay from './day/period'; import MultiDotDay from './day/multi-dot'; import MultiPeriodDay from './day/multi-period'; import SingleDay from './day/custom'; import CalendarHeader from './header'; import shouldComponentUpdate from './updater'; //Fallback when RN version is < 0.44 const viewPropTypes = ViewPropTypes || View.propTypes; const EmptyArray = []; class Calendar extends Component { static propTypes = { // Specify theme properties to override specific styles for calendar parts. Default = {} theme: PropTypes.object, // Collection of dates that have to be marked. Default = {} markedDates: PropTypes.object, // Specify style for calendar container element. Default = {} style: viewPropTypes.style, // Initially visible month. Default = Date() current: PropTypes.any, // Minimum date that can be selected, dates before minDate will be grayed out. Default = undefined minDate: PropTypes.any, // Maximum date that can be selected, dates after maxDate will be grayed out. Default = undefined maxDate: PropTypes.any, // If firstDay=1 week starts from Monday. Note that dayNames and dayNamesShort should still start from Sunday. firstDay: PropTypes.number, // Date marking style [simple/period/multi-dot/multi-period]. Default = 'simple' markingType: PropTypes.string, // Hide month navigation arrows. Default = false hideArrows: PropTypes.bool, // Display loading indicador. Default = false displayLoadingIndicator: PropTypes.bool, // Do not show days of other months in month page. Default = false hideExtraDays: PropTypes.bool, // Handler which gets executed on day press. Default = undefined onDayPress: PropTypes.func, // Handler which gets executed on day long press. Default = undefined onDayLongPress: PropTypes.func, // Handler which gets executed when visible month changes in calendar. Default = undefined onMonthChange: PropTypes.func, onVisibleMonthsChange: PropTypes.func, // Replace default arrows with custom ones (direction can be 'left' or 'right') renderArrow: PropTypes.func, // Provide custom day rendering component dayComponent: PropTypes.any, // Month format in calendar title. Formatting values: http://arshaw.com/xdate/#Formatting monthFormat: PropTypes.string, // Disables changing month when click on days of other months (when hideExtraDays is false). Default = false disableMonthChange: PropTypes.bool, // Hide day names. Default = false hideDayNames: PropTypes.bool, // Disable days by default. Default = false disabledByDefault: PropTypes.bool, // Show week numbers. Default = false showWeekNumbers: PropTypes.bool, // Handler which gets executed when press arrow icon left. It receive a callback can go back month onPressArrowLeft: PropTypes.func, // Handler which gets executed when press arrow icon left. It receive a callback can go next month onPressArrowRight: PropTypes.func }; constructor(props) { super(props); this.style = styleConstructor(this.props.theme); let currentMonth; if (props.current) { currentMonth = parseDate(props.current); } else { currentMonth = XDate(); } this.state = { currentMonth }; this.updateMonth = this.updateMonth.bind(this); this.addMonth = this.addMonth.bind(this); this.pressDay = this.pressDay.bind(this); this.longPressDay = this.longPressDay.bind(this); this.shouldComponentUpdate = shouldComponentUpdate; } componentWillReceiveProps(nextProps) { const current = parseDate(nextProps.current); if (current && current.toString('yyyy MM') !== this.state.currentMonth.toString('yyyy MM')) { this.setState({ currentMonth: current.clone() }); } } getDayComponent() { if (this.props.dayComponent) { return this.props.dayComponent; } // return MultiPeriodDay; switch (this.props.markingType) { case 'period': return UnitDay; case 'multi-dot': return MultiDotDay; case 'multi-period': return MultiPeriodDay; case 'custom': return SingleDay; default: return Day; } } getDateMarking(day) { if (!this.props.markedDates) { return false; } const dates = this.props.markedDates[day.toString('yyyy-MM-dd')] || EmptyArray; if (dates.length || dates) { return dates; } else { return false; } } updateMonth(day, doNotTriggerListeners) { if (day.toString('yyyy MM') === this.state.currentMonth.toString('yyyy MM')) { return; } this.setState({ currentMonth: day.clone() }, () => { if (!doNotTriggerListeners) { const currMont = this.state.currentMonth.clone(); if (this.props.onMonthChange) { this.props.onMonthChange(xdateToData(currMont)); } if (this.props.onVisibleMonthsChange) { this.props.onVisibleMonthsChange([xdateToData(currMont)]); } } }); } _handleDayInteraction(date, interaction) { const day = parseDate(date); const minDate = parseDate(this.props.minDate); const maxDate = parseDate(this.props.maxDate); if (!(minDate && !dateutils.isGTE(day, minDate)) && !(maxDate && !dateutils.isLTE(day, maxDate))) { const shouldUpdateMonth = this.props.disableMonthChange === undefined || !this.props.disableMonthChange; if (shouldUpdateMonth) { this.updateMonth(day); } if (interaction) { interaction(xdateToData(day)); } } } pressDay(date) { this._handleDayInteraction(date, this.props.onDayPress); } longPressDay(date) { this._handleDayInteraction(date, this.props.onDayLongPress); } addMonth(count) { this.updateMonth(this.state.currentMonth.clone().addMonths(count, true)); } renderDay(day, id) { const minDate = parseDate(this.props.minDate); const maxDate = parseDate(this.props.maxDate); let state = ''; if (this.props.disabledByDefault) { state = 'disabled'; } else if ((minDate && !dateutils.isGTE(day, minDate)) || (maxDate && !dateutils.isLTE(day, maxDate))) { state = 'disabled'; } else if (!dateutils.sameMonth(day, this.state.currentMonth)) { state = 'disabled'; } else if (dateutils.sameDate(day, XDate())) { state = 'today'; } state = 'disabled'; let dayComp; if (!dateutils.sameMonth(day, this.state.currentMonth) && this.props.hideExtraDays) { if (['period', 'multi-period'].includes(this.props.markingType)) { dayComp = (<View key={id} style={{ flex: 1 }} />); } else { dayComp = (<View key={id} style={this.style.dayContainer} />); } } else { const DayComp = this.getDayComponent(); const date = day.getDate(); dayComp = ( <DayComp key={id} state={state} theme={this.props.theme} onPress={this.pressDay} onLongPress={this.longPressDay} date={xdateToData(day)} marking={this.getDateMarking(day)} > {date} </DayComp> ); } return dayComp; } renderWeekNumber(weekNumber) { return <Day key={`week-${weekNumber}`} theme={this.props.theme} marking={{ disableTouchEvent: true }} state='disabled'>{weekNumber}</Day>; } renderWeek(days, id, count) { const week = []; days.forEach((day, id2) => { week.push(this.renderDay(day, id2)); }, this); if (this.props.showWeekNumbers) { week.unshift(this.renderWeekNumber(days[days.length - 1].getWeek())); } return ( <View key={id}> <View style={this.style.week}>{week}</View> {count !== 0 && <View style={this.style.divider} />} </View > ); } render() { const days = dateutils.page(this.state.currentMonth, this.props.firstDay); const weeks = []; while (days.length) { weeks.push(this.renderWeek(days.splice(0, 7), weeks.length, days.length)); } let indicator; const current = parseDate(this.props.current); if (current) { const lastMonthOfDay = current.clone().addMonths(1, true).setDate(1).addDays(-1).toString('yyyy-MM-dd'); if (this.props.displayLoadingIndicator && !(this.props.markedDates && this.props.markedDates[lastMonthOfDay])) { indicator = true; } } return ( <View style={[this.style.container, this.props.style]}> <View style={{ backgroundColor: 'transparent' }}> <CalendarHeader theme={this.props.theme} addMonth={this.addMonth} showIndicator={indicator} firstDay={this.props.firstDay} month={this.state.currentMonth} hideArrows={this.props.hideArrows} headerColor={this.props.headerColor} renderArrow={this.props.renderArrow} monthFormat={this.props.monthFormat} hideDayNames={this.props.hideDayNames} weekNumbers={this.props.showWeekNumbers} onPressArrowLeft={this.props.onPressArrowLeft} onPressArrowRight={this.props.onPressArrowRight} /> </View> <View style={this.style.monthView}>{weeks}</View> </View >); } } export default Calendar;