UNPKG

react-native-calendars-monthly-view

Version:
248 lines (218 loc) 7.51 kB
import _ from 'lodash'; import PropTypes from 'prop-types'; import XDate from 'xdate'; import React, {Component, Fragment} from 'react'; import {ActivityIndicator, Platform, View, Text, TouchableOpacity, Image} from 'react-native'; import {shouldUpdate} from '../../component-updater'; import {weekDayNames} from '../../dateutils'; import { CHANGE_MONTH_LEFT_ARROW, CHANGE_MONTH_RIGHT_ARROW, HEADER_DAY_NAMES, HEADER_LOADING_INDICATOR, HEADER_MONTH_NAME } from '../../testIDs'; import styleConstructor from './style'; class CalendarHeader extends Component { static displayName = 'IGNORE'; static propTypes = { theme: PropTypes.object, firstDay: PropTypes.number, displayLoadingIndicator: PropTypes.bool, showWeekNumbers: PropTypes.bool, month: PropTypes.instanceOf(XDate), addMonth: PropTypes.func, /** Month format in the title. Formatting values: http://arshaw.com/xdate/#Formatting */ monthFormat: PropTypes.string, /** Hide day names. Default = false */ hideDayNames: PropTypes.bool, /** Hide month navigation arrows. Default = false */ hideArrows: PropTypes.bool, /** Replace default arrows with custom ones (direction can be 'left' or 'right') */ renderArrow: PropTypes.func, /** 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 right. It receive a callback can go next month */ onPressArrowRight: PropTypes.func, /** Disable left arrow. Default = false */ disableArrowLeft: PropTypes.bool, /** Disable right arrow. Default = false */ disableArrowRight: PropTypes.bool, /** Apply custom disable color to selected day indexes */ disabledDaysIndexes: PropTypes.arrayOf(PropTypes.number), /** Replace default month and year title with custom one. the function receive a date as parameter. */ renderHeader: PropTypes.any, /** Provide aria-level for calendar heading for proper accessibility when used with web (react-native-web) */ webAriaLevel: PropTypes.number }; static defaultProps = { monthFormat: 'MMMM yyyy', webAriaLevel: 1 }; constructor(props) { super(props); this.style = styleConstructor(props.theme); } shouldComponentUpdate(nextProps) { if (nextProps.month.toString('yyyy MM') !== this.props.month.toString('yyyy MM')) { return true; } return shouldUpdate(this.props, nextProps, [ 'displayLoadingIndicator', 'hideDayNames', 'firstDay', 'showWeekNumbers', 'monthFormat', 'renderArrow', 'disableArrowLeft', 'disableArrowRight' ]); } addMonth = () => { const {addMonth} = this.props; addMonth(1); }; subtractMonth = () => { const {addMonth} = this.props; addMonth(-1); }; onPressLeft = () => { const {onPressArrowLeft, month} = this.props; if (typeof onPressArrowLeft === 'function') { return onPressArrowLeft(this.subtractMonth, month); } return this.subtractMonth(); }; onPressRight = () => { const {onPressArrowRight, month} = this.props; if (typeof onPressArrowRight === 'function') { return onPressArrowRight(this.addMonth, month); } return this.addMonth(); }; renderWeekDays = weekDaysNames => { const {disabledDaysIndexes} = this.props; return weekDaysNames.map((day, idx) => { const dayStyle = [this.style.dayHeader]; if (_.includes(disabledDaysIndexes, idx)) { dayStyle.push(this.style.disabledDayHeader); } return ( <Text allowFontScaling={false} key={idx} style={dayStyle} numberOfLines={1} accessibilityLabel={''}> {day} </Text> ); }); }; renderHeader = () => { const {renderHeader, month, monthFormat, testID, webAriaLevel} = this.props; const webProps = Platform.OS === 'web' ? {'aria-level': webAriaLevel} : {}; if (renderHeader) { return renderHeader(month); } return ( <Fragment> <Text allowFontScaling={false} style={this.style.monthText} testID={testID ? `${HEADER_MONTH_NAME}-${testID}` : HEADER_MONTH_NAME} {...webProps} > {month.toString(monthFormat)} </Text> </Fragment> ); }; renderArrow(direction) { const {hideArrows, disableArrowLeft, disableArrowRight, renderArrow, testID} = this.props; if (hideArrows) { return <View />; } const isLeft = direction === 'left'; const id = isLeft ? CHANGE_MONTH_LEFT_ARROW : CHANGE_MONTH_RIGHT_ARROW; const testId = testID ? `${id}-${testID}` : id; const onPress = isLeft ? this.onPressLeft : this.onPressRight; const imageSource = isLeft ? require('../img/previous.png') : require('../img/next.png'); const renderArrowDirection = isLeft ? 'left' : 'right'; const shouldDisable = isLeft ? disableArrowLeft : disableArrowRight; return ( <TouchableOpacity onPress={!shouldDisable ? onPress : undefined} disabled={shouldDisable} style={this.style.arrow} hitSlop={{left: 20, right: 20, top: 20, bottom: 20}} testID={testId} > {renderArrow ? ( renderArrow(renderArrowDirection) ) : ( <Image source={imageSource} style={shouldDisable ? this.style.disabledArrowImage : this.style.arrowImage} /> )} </TouchableOpacity> ); } renderIndicator() { const {displayLoadingIndicator, theme, testID} = this.props; if (displayLoadingIndicator) { return ( <ActivityIndicator color={theme && theme.indicatorColor} testID={testID ? `${HEADER_LOADING_INDICATOR}-${testID}` : HEADER_LOADING_INDICATOR} /> ); } } renderDayNames() { const {firstDay, hideDayNames, showWeekNumbers, testID} = this.props; const weekDaysNames = weekDayNames(firstDay); if (!hideDayNames) { return ( <View style={this.style.week} testID={testID ? `${HEADER_DAY_NAMES}-${testID}` : HEADER_DAY_NAMES}> {showWeekNumbers && <Text allowFontScaling={false} style={this.style.dayHeader}></Text>} {this.renderWeekDays(weekDaysNames)} </View> ); } } render() { const {style, testID} = this.props; return ( <View testID={testID} style={style} accessible accessibilityRole={'adjustable'} accessibilityActions={[ {name: 'increment', label: 'increment'}, {name: 'decrement', label: 'decrement'} ]} onAccessibilityAction={this.onAccessibilityAction} accessibilityElementsHidden={this.props.accessibilityElementsHidden} // iOS importantForAccessibility={this.props.importantForAccessibility} // Android > <View style={this.style.header}> {this.renderArrow('left')} <View style={this.style.headerContainer}> {this.renderHeader()} {this.renderIndicator()} </View> {this.renderArrow('right')} </View> {this.renderDayNames()} </View> ); } onAccessibilityAction = event => { switch (event.nativeEvent.actionName) { case 'decrement': this.onPressLeft(); break; case 'increment': this.onPressRight(); break; default: break; } }; } export default CalendarHeader;