UNPKG

@sorakrisc/react-native-datetimepicker

Version:

React Native date and time picker that support buddhist year for both ios and android.

445 lines (407 loc) 14.8 kB
import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { Text, TouchableHighlight, View } from 'react-native'; import styles from './styles'; import Moment from 'moment'; import RBSheet from 'react-native-raw-bottom-sheet'; import { Picker } from '@react-native-picker/picker'; import { isValid } from './helpers/dateHelpers'; const _monthList = [ 'January', 'Febuary', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ]; // const monthsWith31Days = ['1', '3', '5', '7', '8', '10', '12']; // const monthsWith30Days = ['4', '6', '9', '11']; class DatePicker extends Component { constructor (props) { super(props); const { selectedDay, selectedMonth, selectedYear, date } = props; // const date = this.props.date ; const parsedDate = this._getDate(date); this.state = { selectedDay: parsedDate.date().toString(), selectedMonth: (parsedDate.month() + 1).toString(), selectedYear: parsedDate.year().toString(), date, pickerDayKey: 0 }; } _getDate (date = this.props.date, format = this.props.format) { const { minDate, maxDate } = this.props; // date默认值 if (!date) { let now = Moment(); if (minDate) { let _minDate = this._getDate(minDate); if (now < _minDate) { return _minDate; } } if (maxDate) { let _maxDate = this._getDate(maxDate); if (now > _maxDate) { return _maxDate; } } return now; } if (date instanceof Date) { return date; } return Moment(date, format); } _getDateStr (date = this.props.date) { const { mode, format = FORMATS[mode] } = this.props; const dateInstance = date instanceof Date ? date : this.getDate(date); if (typeof this.props.getDateStr === 'function') { return this.props.getDateStr(dateInstance); } return Moment(dateInstance).format(format); } selectedMonthHaveDay = (day) => { //every month have at least 28 days if (day <= 28) { return true; } else { //handle case for day 28,29,30,31 const { selectedMonth, selectedYear } = this.state; return isValid({day, month: selectedMonth, year: selectedYear}); } }; getMinDate = (unitName) => { const { minDate } = this.props; if (minDate) { const dateMinDate = Moment(minDate, 'DDMYYYY'); switch (unitName) { case 'Day': return dateMinDate.day(); case 'Month': return dateMinDate.month() + 1; case 'Year': const min = dateMinDate.year(); this.props['min' + unitName] = min; return min; } return null; // this.props['min' + unitName] = minUnitName; // return minUnitName; } else { return this.props['min' + unitName]; } }; getMaxDate = (unitName) => { const { maxDate } = this.props; if (maxDate) { const dateMaxDate = Moment(maxDate, 'DDMYYYY'); switch (unitName) { case 'Day': return dateMaxDate.day(); case 'Month': return dateMaxDate.month() + 1; case 'Year': const max = dateMaxDate.year(); this.props['max' + unitName] = max; return max; } return null; } else { return this.props['max' + unitName]; } }; getYearItems = () => { const items = []; const { yearInterval, yearUnit, calendarType } = this.props; const minYear = this.getMinDate('Year'); const maxYear = this.getMaxDate('Year'); const interval = maxYear / yearInterval; const valueAddOn = calendarType === 'gregorian' ? 0 : 543; for (let i = minYear; i <= interval; i++) { const value = i * yearInterval; const valueString = `${value}`; const item = ( <Picker.Item key={valueString} value={valueString} label={`${value + valueAddOn}` + yearUnit}/> ); items.push(item); } return items; }; getMonthItems = () => { const items = []; const { minMonth, maxMonth, monthInterval, monthUnit, monthList } = this.props; const interval = maxMonth / monthInterval; for (let i = minMonth; i <= interval; i++) { const monthIndex = (i - 1) * monthInterval; const value = monthList[monthIndex]; const item = ( <Picker.Item key={value} value={i.toString()} label={value + monthUnit}/> ); items.push(item); } return items; }; getDayItems = () => { const items = []; const { minDay, maxDay, dayInterval, dayUnit } = this.props; const interval = maxDay / dayInterval; for (let i = minDay; i <= interval; i++) { if (this.selectedMonthHaveDay(i)) { const value = `${i * dayInterval}`; const item = ( <Picker.Item key={value} value={value} label={value + dayUnit}/> ); items.push(item); } } return items; }; onValueChange = (selectedDay, selectedMonth, selectedYear) => { let items = []; const minDate = this._getDate(this.props.minDate); const maxDate = this._getDate(this.props.maxDate); const valueDate = this._getDate( `${selectedDay}/${selectedMonth}/${selectedYear}`, 'D/M/YYYY'); if (this.props.minDate && valueDate.isBefore(minDate)) { this.setState({ selectedDay: minDate.date().toString(), selectedMonth: (minDate.month() + 1).toString(), selectedYear: minDate.year().toString() }); } else if (this.props.maxDate && valueDate.isAfter(maxDate)) { this.setState({ selectedDay: maxDate.date().toString(), selectedMonth: (maxDate.month() + 1).toString(), selectedYear: maxDate.year().toString() }); } else { if (isValid({ day: selectedDay, month: selectedMonth, year: selectedYear})) { this.setState({ selectedDay, selectedMonth, selectedYear }); } else { const correctSelectedDay = Moment(`01/${selectedMonth}/${selectedYear}`, 'DDMYYYY').endOf('month').format('DD'); this.setState({ selectedDay: correctSelectedDay, selectedMonth, selectedYear, pickerDayKey: this.state.pickerDayKey + 1 }) } } }; getDate = () => { const { selectedDay, selectedMonth, selectedYear } = this.state; return Moment(selectedDay + '/' + selectedMonth + '/' + selectedYear, 'DD/MM/YYYY').format(this.props.format); }; onCancel = () => { if (typeof this.props.onCancel === 'function') { this.props.onCancel(this.getDate()); } this.close(); }; onConfirm = () => { const date = this.getDate(); this.setState({ date }, () => { if (typeof this.props.onConfirm === 'function') { this.props.onConfirm(date); } this.close(); }); }; close = () => { this.RBSheet.close(); }; open = () => { const parsedDate = this._getDate(this.props.date); this.setState({ selectedDay: parsedDate.date().toString(), selectedMonth: (parsedDate.month() + 1).toString(), selectedYear: parsedDate.year().toString() }); this.RBSheet.open(); }; renderHeader = () => { const { confirmBtnText, cancelBtnText, textTitle, TouchableComponent } = this.props; return ( <View style={styles.header}> <TouchableComponent underlayColor={'transparent'} onPress={this.onCancel} style={styles.buttonAction}> <Text style={[styles.buttonText]}> {cancelBtnText} </Text> </TouchableComponent> {textTitle && <View style={styles.buttonAction}> <Text style={[ styles.buttonText, { color: 'black', fontWeight: '500' }]}>{textTitle}</Text> </View>} <TouchableComponent underlayColor={'transparent'} onPress={this.onConfirm} style={styles.buttonAction}> <Text style={styles.buttonText}>{confirmBtnText}</Text> </TouchableComponent> </View> ); }; renderBody = () => { const { selectedDay, selectedMonth, selectedYear, pickerDayKey } = this.state; return ( <View style={styles.body}> <Picker key={pickerDayKey} selectedValue={selectedDay} style={styles.picker} itemStyle={this.props.itemStyle} onValueChange={itemValue => this.onValueChange(itemValue, selectedMonth, selectedYear) } > {this.getDayItems()} </Picker> <Picker selectedValue={selectedMonth} style={styles.picker} itemStyle={this.props.itemStyle} onValueChange={itemValue => this.onValueChange(selectedDay, itemValue, selectedYear) } > {this.getMonthItems()} </Picker> <Picker selectedValue={selectedYear} style={styles.picker} itemStyle={this.props.itemStyle} onValueChange={itemValue => this.onValueChange(selectedDay, selectedMonth, itemValue) } > {this.getYearItems()} </Picker> </View> ); }; renderInput = () => { const { inputStyle, textStyle, placeholderTextStyle, iconComponent, TouchableComponent, showInput, inputComponent } = this.props; if (showInput) { if (inputComponent) { return ( <TouchableComponent underlayColor={'transparent'} onPress={() => this.open()} > {inputComponent} </TouchableComponent> ); } else { return ( <TouchableComponent underlayColor={'transparent'} onPress={() => this.open()} > <View style={[styles.input, inputStyle]}> {this.props.date && this.props.date.length > 0 ? <Text style={[ styles.text, textStyle]}>{this.props.date}</Text> : <Text style={[ styles.placeholderText, placeholderTextStyle]}>{this.props.placeholder}</Text>} {iconComponent} </View> </TouchableComponent> ); } } return null; }; render () { return ( <View> <RBSheet ref={ref => { this.RBSheet = ref; }} > {this.renderHeader()} {this.renderBody()} </RBSheet> {this.renderInput()} </View> ); } } DatePicker.propTypes = { minDate: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]), maxDate: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]), minYear: PropTypes.number, minMonth: PropTypes.number, minDay: PropTypes.number, maxDay: PropTypes.number, maxMonth: PropTypes.number, maxYear: PropTypes.number, dayInterval: PropTypes.number, monthInterval: PropTypes.number, dayUnit: PropTypes.string, monthUnit: PropTypes.string, selectedDay: PropTypes.string, selectedYear: PropTypes.string, itemStyle: PropTypes.object, textCancel: PropTypes.string, textConfirm: PropTypes.string, textTitle: PropTypes.string, onCancel: PropTypes.func, onConfirm: PropTypes.func, format: PropTypes.string, inputStyle: PropTypes.object, textStyle: PropTypes.object, placeholderTextStyle: PropTypes.object, iconComponent: PropTypes.element, date: PropTypes.oneOfType( [PropTypes.string, PropTypes.instanceOf(Date), PropTypes.object]), confirmBtnText: PropTypes.string, cancelBtnText: PropTypes.string, placeholder: PropTypes.string, calendarType: PropTypes.oneOf(['gregorian, buddhist']), showInput: PropTypes.boolean, inputComponent: PropTypes.any }; DatePicker.defaultProps = { minYear: 1, minMonth: 1, minDay: 1, maxDay: 31, maxMonth: 12, maxYear: 9999, monthList: _monthList, monthInterval: 1, yearInterval: 1, dayInterval: 1, dayUnit: '', monthUnit: '', yearUnit: '', selectedDay: '28', selectedMonth: '2', selectedYear: '2000', itemStyle: {}, confirmBtnText: 'Done', cancelBtnText: 'Cancel', textTitle: null, format: 'DD/MM/YYYY', TouchableComponent: TouchableHighlight, date: '', placeholder: '', calendarType: 'gregorian', showInput: true }; export default DatePicker;