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
JavaScript
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;