UNPKG

@capaj/react-datetime

Version:

A lightweight but complete datetime picker React.js component

438 lines 16.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const object_assign_1 = __importDefault(require("object-assign")); const moment_1 = __importDefault(require("moment")); const react_1 = __importDefault(require("react")); const CalendarContainer_1 = __importDefault(require("./CalendarContainer")); const viewModes = Object.freeze({ YEARS: 'years', MONTHS: 'months', DAYS: 'days', TIME: 'time' }); class Datetime extends react_1.default.Component { constructor(props, context) { super(props, context); this.onInputChange = (e) => { const value = e.target === null ? e : e.target.value; const localMoment = this.localMoment(value, this.state.inputFormat); const update = { inputValue: value }; if (localMoment.isValid() && !this.props.value) { update.selectedDate = localMoment; update.viewDate = localMoment.clone().startOf('month'); } else { update.selectedDate = null; } return this.setState(update, function () { return this.props.onChange(localMoment.isValid() ? localMoment : this.state.inputValue); }); }; this.onInputKey = ({ which }) => { if (which === 9 && this.props.closeOnTab) { this.closeCalendar(); } }; this.showView = (view) => { return () => { this.state.currentView !== view && this.props.onViewModeChange(view); this.setState({ currentView: view }); }; }; this.setDate = (type) => { const nextViews = { month: viewModes.DAYS, year: viewModes.MONTHS }; return ({ target }) => { this.setState({ viewDate: this.state.viewDate .clone()[type](parseInt(target.getAttribute('data-value'), 10)) .startOf(type), currentView: nextViews[type] }); this.props.onViewModeChange(nextViews[type]); }; }; this.subtractTime = (amount, type, toSelected) => { return () => { this.props.onNavigateBack(amount, type); this.updateTime('subtract', amount, type, toSelected); }; }; this.addTime = (amount, type, toSelected) => { return () => { this.props.onNavigateForward(amount, type); this.updateTime('add', amount, type, toSelected); }; }; this.allowedSetTime = ['hours', 'minutes', 'seconds', 'milliseconds']; this.setTime = (type, value) => { let index = this.allowedSetTime.indexOf(type) + 1; const state = this.state; const date = (state.selectedDate || state.viewDate).clone(); let nextType; date[type](value); for (; index < this.allowedSetTime.length; index++) { nextType = this.allowedSetTime[index]; date[nextType](date[nextType]()); } if (!this.props.value) { this.setState({ selectedDate: date, inputValue: date.format(state.inputFormat) }); } this.props.onChange(date); }; this.updateSelectedDate = (e, close) => { const target = e.target; let modifier = 0; const viewDate = this.state.viewDate; const currentDate = this.state.selectedDate || viewDate; let date; if (target.className.includes('rdtDay')) { if (target.className.includes('rdtNew')) modifier = 1; else if (target.className.includes('rdtOld')) modifier = -1; date = viewDate .clone() .month(viewDate.month() + modifier) .date(parseInt(target.getAttribute('data-value'), 10)); } else if (target.className.includes('rdtMonth')) { date = viewDate .clone() .month(parseInt(target.getAttribute('data-value'), 10)) .date(currentDate.date()); } else if (target.className.includes('rdtYear')) { date = viewDate .clone() .month(currentDate.month()) .date(currentDate.date()) .year(parseInt(target.getAttribute('data-value'), 10)); } date .hours(currentDate.hours()) .minutes(currentDate.minutes()) .seconds(currentDate.seconds()) .milliseconds(currentDate.milliseconds()); if (!this.props.value) { const open = !(this.props.closeOnSelect && close); if (!open) { this.props.onBlur(date); } this.setState({ selectedDate: date, viewDate: date.clone().startOf('month'), inputValue: date.format(this.state.inputFormat), open }); } else { if (this.props.closeOnSelect && close) { this.closeCalendar(); } } this.props.onChange(date); }; this.openCalendar = (e) => { if (!this.state.open) { this.setState({ open: true }, () => { this.props.onFocus && this.props.onFocus(e); }); } }; this.closeCalendar = () => { this.setState({ open: false }, function () { this.props.onBlur(this.state.selectedDate || this.state.inputValue); }); }; this.handleClickOutside = () => { if (this.props.input && this.state.open && !this.props.open && !this.props.disableOnClickOutside) { this.setState({ open: false }, function () { this.props.onBlur(this.state.selectedDate || this.state.inputValue); }); } }; const state = this.getStateFromProps(this.props); this.state = state; } parseDate(date, { datetime }) { let parsedDate; if (date && typeof date === 'string') parsedDate = this.localMoment(date, datetime); else if (date) parsedDate = this.localMoment(date); if (parsedDate && !parsedDate.isValid()) parsedDate = null; return parsedDate; } getStateFromProps(props) { const formats = this.getFormats(props); const date = props.value || props.defaultValue; let selectedDate; let viewDate; let updateOn; let inputValue; let { open } = props; selectedDate = this.parseDate(date, formats); viewDate = this.parseDate(props.viewDate, formats); viewDate = selectedDate ? selectedDate.clone().startOf('month') : viewDate ? viewDate.clone().startOf('month') : this.localMoment().startOf('month'); updateOn = this.getUpdateOn(formats); if (selectedDate) inputValue = selectedDate.format(formats.datetime); else if (date.isValid && !date.isValid()) inputValue = ''; else inputValue = date || ''; if (props.open === undefined) { open = !props.input; } let currentView = props.dateFormat ? props.viewMode || updateOn || viewModes.DAYS : viewModes.TIME; const state = { updateOn, inputFormat: formats.datetime, viewDate, selectedDate, inputValue, open, currentView }; return state; } getUpdateOn({ date }) { if (date.match(/[lLD]/)) { return viewModes.DAYS; } else if (date.includes('M')) { return viewModes.MONTHS; } else if (date.includes('Y')) { return viewModes.YEARS; } return viewModes.DAYS; } getFormats(props) { const formats = { date: props.dateFormat || '', time: props.timeFormat || '' }; const locale = this.localMoment(props.date, null, props).localeData(); if (formats.date === true) { formats.date = locale.longDateFormat('L'); } else if (this.getUpdateOn(formats) !== viewModes.DAYS) { formats.time = ''; } if (formats.time === true) { formats.time = locale.longDateFormat('LT'); } formats.datetime = formats.date && formats.time ? `${formats.date} ${formats.time}` : formats.date || formats.time; return formats; } componentWillReceiveProps(nextProps) { const formats = this.getFormats(nextProps); let updatedState = {}; if (nextProps.value !== this.props.value || formats.datetime !== this.getFormats(this.props).datetime) { updatedState = this.getStateFromProps(nextProps); } if (updatedState.open === undefined) { if (typeof nextProps.open !== 'undefined') { updatedState.open = nextProps.open; } else if (this.props.closeOnSelect && this.state.currentView !== viewModes.TIME) { updatedState.open = false; } else { updatedState.open = this.state.open; } } if (nextProps.viewMode !== this.props.viewMode) { updatedState.currentView = nextProps.viewMode; } if (nextProps.locale !== this.props.locale) { if (this.state.viewDate) { const updatedViewDate = this.state.viewDate .clone() .locale(nextProps.locale); updatedState.viewDate = updatedViewDate; } if (this.state.selectedDate) { const updatedSelectedDate = this.state.selectedDate .clone() .locale(nextProps.locale); updatedState.selectedDate = updatedSelectedDate; updatedState.inputValue = updatedSelectedDate.format(formats.datetime); } } if (nextProps.timezone !== this.props.timezone) { if (nextProps.timezone) { if (this.state.viewDate) updatedState.viewDate = this.state.viewDate .clone() .tz(nextProps.timezone, true); if (this.state.selectedDate) { updatedState.selectedDate = this.state.selectedDate .clone() .tz(nextProps.timezone, true); updatedState.inputValue = updatedState.selectedDate.format(formats.datetime); } } else { if (this.state.viewDate) updatedState.viewDate = this.state.viewDate.clone().local(); if (this.state.selectedDate) { updatedState.selectedDate = this.state.selectedDate.clone().local(); updatedState.inputValue = updatedState.selectedDate.format(formats.datetime); } } } if (nextProps.viewDate !== this.props.viewDate) { updatedState.viewDate = moment_1.default(nextProps.viewDate); } this.setState(updatedState); } updateTime(op, amount, type, toSelected) { const update = {}; const date = toSelected ? 'selectedDate' : 'viewDate'; update[date] = this.state[date].clone()[op](amount, type); this.setState(update); } localMoment(date, format, props) { props = props || this.props; const momentFn = props.timezone ? (time) => moment_1.default(time).tz(props.timezone, true) : moment_1.default; const m = momentFn(date, format, props.strictParsing); if (props.locale) m.locale(props.locale); return m; } getComponentProps() { const componentProps = { fromProps: [ 'value', 'isValidDate', 'renderDay', 'renderMonth', 'renderYear', 'timeConstraints' ], fromState: ['viewDate', 'selectedDate', 'updateOn'], fromThis: [ 'setDate', 'setTime', 'showView', 'addTime', 'subtractTime', 'updateSelectedDate', 'localMoment', 'handleClickOutside' ] }; const formats = this.getFormats(this.props); const props = { dateFormat: formats.date, timeFormat: formats.time }; componentProps.fromProps.forEach((name) => { props[name] = this.props[name]; }); componentProps.fromState.forEach((name) => { props[name] = this.state[name]; }); componentProps.fromThis.forEach((name) => { props[name] = this[name]; }); return props; } render() { let className = `rdt${this.props.className ? Array.isArray(this.props.className) ? ` ${this.props.className.join(' ')}` : ` ${this.props.className}` : ''}`; let children = []; if (this.props.input) { const finalInputProps = object_assign_1.default({ type: 'text', className: 'form-control', onClick: this.openCalendar, onFocus: this.openCalendar, onChange: this.onInputChange, onKeyDown: this.onInputKey, value: this.state && this.state.inputValue }, this.props.inputProps); if (this.props.renderInput) { children = [ react_1.default.createElement("div", { key: "i" }, this.props.renderInput(finalInputProps, this.openCalendar, this.closeCalendar)) ]; } else { children = [ react_1.default.createElement("input", Object.assign({}, object_assign_1.default({ key: 'i' }, finalInputProps))) ]; } } else { className += ' rdtStatic'; } if (this.state.open) className += ' rdtOpen'; return (react_1.default.createElement("div", { className: className }, children.concat(react_1.default.createElement("div", { key: "dt", className: "rdtPicker" }, react_1.default.createElement(CalendarContainer_1.default, { view: this.state.currentView, viewProps: this.getComponentProps(), onClickOutside: this.handleClickOutside }))))); } } Datetime.defaultProps = { className: '', defaultValue: '', inputProps: {}, input: true, onFocus() { }, onBlur() { }, onChange() { }, onViewModeChange() { }, onNavigateBack() { }, onNavigateForward() { }, timeFormat: true, timeConstraints: {}, dateFormat: true, strictParsing: true, closeOnSelect: false, closeOnTab: true, timezone: null }; exports.default = Datetime; //# sourceMappingURL=DateTime.js.map