UNPKG

react-datepicker-enhanced

Version:

react datepicker component. (include persian jalaali calendar)

327 lines (285 loc) 9.9 kB
import React, { Component } from 'react'; import PropTypes from 'prop-types'; import momentJalaali from 'moment-jalaali'; import TetherComponent from 'react-tether'; import classnames from 'classnames'; import Calendar from './Calendar'; import MyTimePicker from './CustomTimePicker'; const outsideClickIgnoreClass = 'ignore--click--outside'; export default class DatePicker extends Component { static propTypes = { value: PropTypes.oneOfType([PropTypes.object, PropTypes.string]), defaultValue: PropTypes.object, onChange: PropTypes.func, onFocus: PropTypes.func, onBlur: PropTypes.func, children: PropTypes.node, min: PropTypes.object, max: PropTypes.object, defaultMonth: PropTypes.object, inputFormat: PropTypes.string, inputJalaaliFormat: PropTypes.string, removable: PropTypes.bool, styles: PropTypes.object, calendarStyles: PropTypes.object, calendarContainerProps: PropTypes.object, isGregorian: PropTypes.bool, // jalaali or gregorian timePicker: PropTypes.bool, calendarClass: PropTypes.string, datePickerClass: PropTypes.string, tetherAttachment: PropTypes.string, inputReadOnly: PropTypes.bool, ranges: PropTypes.array, showToggleButton: PropTypes.bool, toggleButtonText: PropTypes.any, showTodayButton: PropTypes.bool, placeholder: PropTypes.string }; static defaultProps = { styles: undefined, calendarContainerProps: {}, isGregorian: true, timePicker: true, showTodayButton: true, placeholder: '' }; constructor(props) { super(props); // create a ref to store the textInput DOM element this.textInput = React.createRef(); this.state = { isOpen: false, momentValue: this.props.defaultValue || null, inputValue: this.getValue( this.props.defaultValue, this.props.isGregorian, this.props.timePicker ), inputJalaaliFormat: this.props.inputJalaaliFormat || this.getInputFormat(false, this.props.timePicker), inputFormat: this.props.inputFormat || this.getInputFormat(true, this.props.timePicker), isGregorian: this.props.isGregorian, timePicker: this.props.timePicker, timePickerComponent: this.props.timePicker ? MyTimePicker : undefined }; } getInputFormat(isGregorian, timePicker) { if (timePicker) return isGregorian ? 'YYYY/M/D HH:mm' : 'jYYYY/jM/jD HH:mm'; return isGregorian ? 'YYYY/M/D' : 'jYYYY/jM/jD'; } getValue(inputValue, isGregorian, timePicker) { if (!inputValue) return ''; let { inputFormat } = this.state; let { inputJalaaliFormat } = this.state; if (!inputFormat) inputFormat = this.getInputFormat(isGregorian, timePicker); if (!inputJalaaliFormat) inputJalaaliFormat = this.getInputFormat(isGregorian, timePicker); return isGregorian ? inputValue.locale('es').format(inputFormat) : inputValue.locale('fa').format(inputJalaaliFormat); } setOpen = isOpen => { this.setState({ isOpen }); if (this.props.onOpen) { this.props.onOpen(isOpen); } }; UNSAFE_componentWillMount() { if (this.props.value) { this.setMomentValue(this.props.value); } } UNSAFE_componentWillReceiveProps(nextProps) { if ('value' in nextProps && nextProps.value !== this.props.value) { this.setMomentValue(nextProps.value); } if ('isGregorian' in nextProps && nextProps.isGregorian !== this.props.isGregorian) { const { inputFormat: nextPropsInputFormat } = nextProps; const { inputJalaaliFormat: nextPropsInputJalaaliFormat } = nextProps; this.setState({ isGregorian: nextProps.isGregorian, inputValue: this.getValue(nextProps.value, nextProps.isGregorian, nextProps.timePicker), inputFormat: nextPropsInputFormat || this.state.inputFormat, inputJalaaliFormat: nextPropsInputJalaaliFormat || this.state.inputJalaaliFormat }); } if ('timePicker' in nextProps && nextProps.timePicker !== this.props.timePicker) { this.setState({ timePicker: nextProps.timePicker, timePickerComponent: this.props.timePicker ? MyTimePicker : undefined }); } } toggleMode = () => { const isGregorian = !this.state.isGregorian; const { inputFormat: nextPropsInputFormat } = this.props; const { inputJalaaliFormat: nextPropsInputJalaaliFormat } = this.props; this.setState({ isGregorian: isGregorian, inputValue: this.getValue(this.props.value, isGregorian, this.props.timePicker) }); }; setMomentValue(momentValue) { const { inputFormat, isGregorian, timePicker } = this.state; if (this.props.onChange) { this.props.onChange(momentValue); } const inputValue = this.getValue(momentValue, isGregorian, timePicker); this.setState({ momentValue, inputValue }); } handleFocus = () => { this.setOpen(true); }; handleClickOutsideCalendar() { this.setOpen(false); } toEnglishDigits(str) { if (!str) return str; const regex1 = /[\u0660-\u0669]/g; const regex2 = /[\u06f0-\u06f9]/g; return str .replace(regex1, function (c) { return c.charCodeAt(0) - 0x0660; }) .replace(regex2, function (c) { return c.charCodeAt(0) - 0x06f0; }); } toPersianDigits(str) { if (!str) return str; const regex = /[0-9]/g; const id = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹']; return str.replace(regex, function (w) { return id[+w]; }); } handleSelectDay(selectedDay) { const { momentValue: oldValue } = this.state; let momentValue = selectedDay.clone(); if (oldValue) { momentValue = momentValue.set({ hour: oldValue.hours(), minute: oldValue.minutes(), second: oldValue.seconds() }); } this.setOpen(false); this.setMomentValue(momentValue); } handleInputChange(event) { const { inputFormat, inputJalaaliFormat, isGregorian } = this.state; const inputValue = this.toEnglishDigits(event.target.value); const currentInputFormat = isGregorian ? inputFormat : inputJalaaliFormat; const momentValue = momentJalaali(inputValue, currentInputFormat, true); console.log(inputValue); console.log(currentInputFormat); if (momentValue.isValid()) { this.setState({ momentValue }); console.log("true"); } const isUserClearInput = inputValue === ''; if (this.props.onChange) { if (isUserClearInput) { this.props.onChange(''); } } this.setState({ inputValue }); } handleInputClick() { if (!this.props.disabled) { this.setOpen(true); } } renderInput = ref => { const { isOpen, inputValue, isGregorian } = this.state; const className = classnames(this.props.className, { [outsideClickIgnoreClass]: isOpen }); return ( <div ref={ref}> <input placeholder={this.props.placeholder} className={`datepicker-input ${className}`} type="text" ref={inst => { this.input = inst; }} onFocus={this.handleFocus.bind(this)} onChange={this.handleInputChange.bind(this)} onClick={this.handleInputClick.bind(this)} value={isGregorian ? inputValue : this.toPersianDigits(inputValue)} readOnly={this.props.inputReadOnly === true} disabled={this.props.disabled} /> </div> ); }; renderCalendar = ref => { const { momentValue, isGregorian, timePickerComponent: TimePicker } = this.state; const { onChange, min, max, defaultMonth, styles, calendarContainerProps, ranges } = this.props; return ( <div ref={ref}> <Calendar toggleMode={this.toggleMode} ranges={ranges} min={min} max={max} selectedDay={momentValue} defaultMonth={defaultMonth} onSelect={this.handleSelectDay.bind(this)} onClickOutside={this.handleClickOutsideCalendar.bind(this)} outsideClickIgnoreClass={outsideClickIgnoreClass} styles={styles} containerProps={calendarContainerProps} isGregorian={isGregorian} calendarClass={this.props.calendarClass ? this.props.calendarClass : ''} showToggleButton={this.props.showToggleButton} toggleButtonText={this.props.toggleButtonText} showTodayButton={this.props.showTodayButton} timePicker={ TimePicker ? ( <TimePicker outsideClickIgnoreClass={outsideClickIgnoreClass} isGregorian={isGregorian} min={min} max={max} momentValue={momentValue} setMomentValue={this.setMomentValue.bind(this)} /> ) : null } /> </div> ); }; removeDate() { const { onChange } = this.props; if (onChange) { onChange(''); } this.setState({ input: '', inputValue: '' }); } render() { const { isOpen } = this.state; return ( <TetherComponent ref={tether => (this.tether = tether)} attachment={this.props.tetherAttachment ? this.props.tetherAttachment : 'top center'} constraints={[ { to: 'window', attachment: 'together' } ]} offset="-10px -10px" onResize={() => this.tether && this.tether.position()} /* renderTarget: This is what the item will be tethered to, make sure to attach the ref */ renderTarget={ref => this.renderInput(ref)} /* renderElement: If present, this item will be tethered to the the component returned by renderTarget */ renderElement={ref => isOpen && this.renderCalendar(ref)} /> ); } }