UNPKG

@nateradebaugh/react-datetime

Version:

A lightweight but complete datetime picker React.js component

1,705 lines (1,479 loc) 57.6 kB
import React, { Component, createElement } from 'react'; import { findDOMNode } from 'react-dom'; import addDays from 'date-fns/add_days'; import format from 'date-fns/format'; import getDaysInMonth from 'date-fns/get_days_in_month'; import getMonth from 'date-fns/get_month'; import getYear from 'date-fns/get_year'; import startOfWeek from 'date-fns/start_of_week'; import isSameDay from 'date-fns/is_same_day'; import isToday from 'date-fns/is_today'; import _setDate from 'date-fns/set_date'; import subMonths from 'date-fns/sub_months'; import getDate from 'date-fns/get_date'; import setMonth from 'date-fns/set_month'; import setYear from 'date-fns/set_year'; import getDaysInYear from 'date-fns/get_days_in_year'; import setDayOfYear from 'date-fns/set_day_of_year'; import getHours from 'date-fns/get_hours'; import startOfMonth from 'date-fns/start_of_month'; import isDate from 'date-fns/is_date'; import isValidDate from 'date-fns/is_valid'; import parse from 'date-fns/parse'; import startOfDay from 'date-fns/start_of_day'; import startOfYear from 'date-fns/start_of_year'; import addMonths from 'date-fns/add_months'; import addYears from 'date-fns/add_years'; import setHours from 'date-fns/set_hours'; import setMinutes from 'date-fns/set_minutes'; import setSeconds from 'date-fns/set_seconds'; import setMilliseconds from 'date-fns/set_milliseconds'; import getMinutes from 'date-fns/get_minutes'; import getSeconds from 'date-fns/get_seconds'; import getMilliseconds from 'date-fns/get_milliseconds'; function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } /** * Check whether some DOM node is our Component's node. */ function isNodeFound(current, componentNode, ignoreClass) { if (current === componentNode) { return true; } // SVG <use/> elements do not technically reside in the rendered DOM, so // they do not have classList directly, but they offer a link to their // corresponding element, which can have classList. This extra check is for // that case. // See: http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGUseElement // Discussion: https://github.com/Pomax/react-onclickoutside/pull/17 if (current.correspondingElement) { return current.correspondingElement.classList.contains(ignoreClass); } return current.classList.contains(ignoreClass); } /** * Try to find our node in a hierarchy of nodes, returning the document * node as highest node if our node is not found in the path up. */ function findHighest(current, componentNode, ignoreClass) { if (current === componentNode) { return true; } // If source=local then this event came from 'somewhere' // inside and should be ignored. We could handle this with // a layered approach, too, but that requires going back to // thinking in terms of Dom node nesting, running counter // to React's 'you shouldn't care about the DOM' philosophy. while (current.parentNode) { if (isNodeFound(current, componentNode, ignoreClass)) { return true; } current = current.parentNode; } return current; } /** * Check if the browser scrollbar was clicked */ function clickedScrollbar(evt) { return document.documentElement.clientWidth <= evt.clientX || document.documentElement.clientHeight <= evt.clientY; } // ideally will get replaced with external dep // when rafrex/detect-passive-events#4 and rafrex/detect-passive-events#5 get merged in var testPassiveEventSupport = function testPassiveEventSupport() { if (typeof window === 'undefined' || typeof window.addEventListener !== 'function') { return; } var passive = false; var options = Object.defineProperty({}, 'passive', { get: function get() { passive = true; } }); var noop = function noop() {}; window.addEventListener('testPassiveEventSupport', noop, options); window.removeEventListener('testPassiveEventSupport', noop, options); return passive; }; function autoInc(seed) { if (seed === void 0) { seed = 0; } return function () { return ++seed; }; } var uid = autoInc(); var passiveEventSupport; var handlersMap = {}; var enabledInstances = {}; var touchEvents = ['touchstart', 'touchmove']; var IGNORE_CLASS_NAME = 'ignore-react-onclickoutside'; /** * Options for addEventHandler and removeEventHandler */ function getEventHandlerOptions(instance, eventName) { var handlerOptions = null; var isTouchEvent = touchEvents.indexOf(eventName) !== -1; if (isTouchEvent && passiveEventSupport) { handlerOptions = { passive: !instance.props.preventDefault }; } return handlerOptions; } /** * This function generates the HOC function that you'll use * in order to impart onOutsideClick listening to an * arbitrary component. It gets called at the end of the * bootstrapping code to yield an instance of the * onClickOutsideHOC function defined inside setupHOC(). */ function onClickOutsideHOC(WrappedComponent, config) { var _class, _temp; return _temp = _class = /*#__PURE__*/ function (_Component) { _inheritsLoose(onClickOutside, _Component); function onClickOutside(props) { var _this; _this = _Component.call(this, props) || this; _this.__outsideClickHandler = function (event) { if (typeof _this.__clickOutsideHandlerProp === 'function') { _this.__clickOutsideHandlerProp(event); return; } var instance = _this.getInstance(); if (typeof instance.props.handleClickOutside === 'function') { instance.props.handleClickOutside(event); return; } if (typeof instance.handleClickOutside === 'function') { instance.handleClickOutside(event); return; } throw new Error('WrappedComponent lacks a handleClickOutside(event) function for processing outside click events.'); }; _this.enableOnClickOutside = function () { if (typeof document === 'undefined' || enabledInstances[_this._uid]) { return; } if (typeof passiveEventSupport === 'undefined') { passiveEventSupport = testPassiveEventSupport(); } enabledInstances[_this._uid] = true; var events = _this.props.eventTypes; if (!events.forEach) { events = [events]; } handlersMap[_this._uid] = function (event) { if (_this.props.disableOnClickOutside) return; if (_this.componentNode === null) return; if (_this.props.preventDefault) { event.preventDefault(); } if (_this.props.stopPropagation) { event.stopPropagation(); } if (_this.props.excludeScrollbar && clickedScrollbar(event)) return; var current = event.target; if (findHighest(current, _this.componentNode, _this.props.outsideClickIgnoreClass) !== document) { return; } _this.__outsideClickHandler(event); }; events.forEach(function (eventName) { document.addEventListener(eventName, handlersMap[_this._uid], getEventHandlerOptions(_this, eventName)); }); }; _this.disableOnClickOutside = function () { delete enabledInstances[_this._uid]; var fn = handlersMap[_this._uid]; if (fn && typeof document !== 'undefined') { var events = _this.props.eventTypes; if (!events.forEach) { events = [events]; } events.forEach(function (eventName) { return document.removeEventListener(eventName, fn, getEventHandlerOptions(_this, eventName)); }); delete handlersMap[_this._uid]; } }; _this.getRef = function (ref) { return _this.instanceRef = ref; }; _this._uid = uid(); return _this; } /** * Access the WrappedComponent's instance. */ var _proto = onClickOutside.prototype; _proto.getInstance = function getInstance() { if (!WrappedComponent.prototype.isReactComponent) { return this; } var ref = this.instanceRef; return ref.getInstance ? ref.getInstance() : ref; }; /** * Add click listeners to the current document, * linked to this component's state. */ _proto.componentDidMount = function componentDidMount() { // If we are in an environment without a DOM such // as shallow rendering or snapshots then we exit // early to prevent any unhandled errors being thrown. if (typeof document === 'undefined' || !document.createElement) { return; } var instance = this.getInstance(); if (config && typeof config.handleClickOutside === 'function') { this.__clickOutsideHandlerProp = config.handleClickOutside(instance); if (typeof this.__clickOutsideHandlerProp !== 'function') { throw new Error('WrappedComponent lacks a function for processing outside click events specified by the handleClickOutside config option.'); } } this.componentNode = findDOMNode(this.getInstance()); this.enableOnClickOutside(); }; _proto.componentDidUpdate = function componentDidUpdate() { this.componentNode = findDOMNode(this.getInstance()); }; /** * Remove all document's event listeners for this component */ _proto.componentWillUnmount = function componentWillUnmount() { this.disableOnClickOutside(); }; /** * Can be called to explicitly enable event listening * for clicks and touches outside of this element. */ /** * Pass-through render */ _proto.render = function render() { // eslint-disable-next-line no-unused-vars var _props = this.props, excludeScrollbar = _props.excludeScrollbar, props = _objectWithoutProperties(_props, ["excludeScrollbar"]); if (WrappedComponent.prototype.isReactComponent) { props.ref = this.getRef; } else { props.wrappedRef = this.getRef; } props.disableOnClickOutside = this.disableOnClickOutside; props.enableOnClickOutside = this.enableOnClickOutside; return createElement(WrappedComponent, props); }; return onClickOutside; }(Component), _class.displayName = "OnClickOutside(" + (WrappedComponent.displayName || WrappedComponent.name || 'Component') + ")", _class.defaultProps = { eventTypes: ['mousedown', 'touchstart'], excludeScrollbar: config && config.excludeScrollbar || false, outsideClickIgnoreClass: IGNORE_CLASS_NAME, preventDefault: false, stopPropagation: false }, _class.getClass = function () { return WrappedComponent.getClass ? WrappedComponent.getClass() : WrappedComponent; }, _temp; } var classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; var createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var inherits = function (subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }; var possibleConstructorReturn = function (self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }; var DaysView = function (_Component) { inherits(DaysView, _Component); function DaysView(props) { classCallCheck(this, DaysView); // Bind functions var _this = possibleConstructorReturn(this, (DaysView.__proto__ || Object.getPrototypeOf(DaysView)).call(this, props)); _this.renderDays = _this.renderDays.bind(_this); _this.updateSelectedDate = _this.updateSelectedDate.bind(_this); _this.renderDay = _this.renderDay.bind(_this); _this.renderFooter = _this.renderFooter.bind(_this); _this.alwaysValidDate = _this.alwaysValidDate.bind(_this); _this.getFormatOptions = _this.getFormatOptions.bind(_this); return _this; } createClass(DaysView, [{ key: "getFormatOptions", value: function getFormatOptions() { return { locale: this.props.locale }; } }, { key: "render", value: function render() { var date = this.props.viewDate || new Date(); var theStartOfWeek = startOfWeek(date); return React.createElement( "div", { className: "rdtDays" }, React.createElement( "table", null, React.createElement( "thead", null, React.createElement( "tr", null, React.createElement( "th", { className: "rdtPrev", onClick: this.props.subtractTime(1, "months") }, React.createElement( "span", null, "\u2039" ) ), React.createElement( "th", { className: "rdtSwitch", onClick: this.props.showView("months"), colSpan: 5, "data-value": getMonth(this.props.viewDate) }, format(date, "MMMM YYYY", this.getFormatOptions()) ), React.createElement( "th", { className: "rdtNext", onClick: this.props.addTime(1, "months") }, React.createElement( "span", null, "\u203A" ) ) ), React.createElement( "tr", null, React.createElement( "th", { className: "dow" }, format(addDays(theStartOfWeek, 0), "dd", this.getFormatOptions()) ), React.createElement( "th", { className: "dow" }, format(addDays(theStartOfWeek, 1), "dd", this.getFormatOptions()) ), React.createElement( "th", { className: "dow" }, format(addDays(theStartOfWeek, 2), "dd", this.getFormatOptions()) ), React.createElement( "th", { className: "dow" }, format(addDays(theStartOfWeek, 3), "dd", this.getFormatOptions()) ), React.createElement( "th", { className: "dow" }, format(addDays(theStartOfWeek, 4), "dd", this.getFormatOptions()) ), React.createElement( "th", { className: "dow" }, format(addDays(theStartOfWeek, 5), "dd", this.getFormatOptions()) ), React.createElement( "th", { className: "dow" }, format(addDays(theStartOfWeek, 6), "dd", this.getFormatOptions()) ) ) ), React.createElement( "tbody", null, this.renderDays() ), this.renderFooter() ) ); } }, { key: "renderDays", value: function renderDays() { var date = this.props.viewDate || new Date(); var selectedDate = this.props.selectedDate ? this.props.selectedDate : undefined; var prevMonth = subMonths(date, 1); var currentYear = getYear(date); var currentMonth = getMonth(date); var weeks = []; var days = []; var renderer = this.props.renderDay || this.renderDay; var isValid = this.props.isValidDate || this.alwaysValidDate; var prevMonthLastWeekStart = startOfWeek(_setDate(prevMonth, getDaysInMonth(prevMonth))); var lastDay = addDays(prevMonthLastWeekStart, 42); for (var workingDate = prevMonthLastWeekStart; workingDate < lastDay; workingDate = addDays(workingDate, 1)) { var classes = "rdtDay"; var workingYear = getYear(workingDate); var workingMonth = getMonth(workingDate); if (workingYear === currentYear && workingMonth < currentMonth || workingYear < currentYear) { classes += " rdtOld"; } else if (workingYear === currentYear && workingMonth > currentMonth || workingYear > currentYear) { classes += " rdtNew"; } if (selectedDate && isSameDay(workingDate, selectedDate)) { classes += " rdtActive"; } if (isToday(workingDate)) { classes += " rdtToday"; } var isDisabled = !isValid(workingDate, selectedDate); if (isDisabled) { classes += " rdtDisabled"; } var dayProps = { key: getDate(workingDate), className: classes, "data-value": getDate(workingDate) }; if (!isDisabled) { dayProps.onClick = this.updateSelectedDate; } days.push(renderer(dayProps, workingDate, selectedDate)); if (days.length === 7) { weeks.push(React.createElement( "tr", { key: workingDate }, days )); days = []; } } return weeks; } }, { key: "updateSelectedDate", value: function updateSelectedDate(event) { this.props.updateSelectedDate(event, true); } }, { key: "renderDay", value: function renderDay(props, currentDate) { return React.createElement( "td", props, format(currentDate, "D", this.getFormatOptions()) ); } }, { key: "renderFooter", value: function renderFooter() { if (!this.props.timeFormat) { return null; } var date = this.props.selectedDate || this.props.viewDate; return React.createElement( "tfoot", null, React.createElement( "tr", null, React.createElement( "td", { onClick: this.props.showView("time"), colSpan: 7, className: "rdtTimeToggle" }, format(date, this.props.timeFormat, this.getFormatOptions()) ) ) ); } }, { key: "alwaysValidDate", value: function alwaysValidDate() { return 1; } }, { key: "handleClickOutside", value: function handleClickOutside() { this.props.handleClickOutside(); } }]); return DaysView; }(Component); var DaysView$1 = onClickOutsideHOC(DaysView); var MonthsView = function (_Component) { inherits(MonthsView, _Component); function MonthsView(props) { classCallCheck(this, MonthsView); // Bind functions var _this = possibleConstructorReturn(this, (MonthsView.__proto__ || Object.getPrototypeOf(MonthsView)).call(this, props)); _this.renderMonths = _this.renderMonths.bind(_this); _this.updateSelectedMonth = _this.updateSelectedMonth.bind(_this); _this.renderMonth = _this.renderMonth.bind(_this); _this.alwaysValidDate = _this.alwaysValidDate.bind(_this); _this.getFormatOptions = _this.getFormatOptions.bind(_this); return _this; } createClass(MonthsView, [{ key: "getFormatOptions", value: function getFormatOptions() { return { locale: this.props.locale }; } }, { key: "render", value: function render() { var date = this.props.viewDate || new Date(); return React.createElement( "div", { className: "rdtMonths" }, React.createElement( "table", null, React.createElement( "thead", null, React.createElement( "tr", null, React.createElement( "th", { className: "rdtPrev", onClick: this.props.subtractTime(1, "years") }, React.createElement( "span", null, "\u2039" ) ), React.createElement( "th", { className: "rdtSwitch", onClick: this.props.showView("years"), colSpan: 2, "data-value": getYear(this.props.viewDate) }, format(date, "YYYY", this.getFormatOptions()) ), React.createElement( "th", { className: "rdtNext", onClick: this.props.addTime(1, "years") }, React.createElement( "span", null, "\u203A" ) ) ) ) ), React.createElement( "table", null, React.createElement( "tbody", null, this.renderMonths() ) ) ); } }, { key: "renderMonths", value: function renderMonths() { var _this2 = this; var date = this.props.selectedDate; var year = getYear(this.props.viewDate); var renderer = this.props.renderMonth || this.renderMonth; var isValid = this.props.isValidDate || this.alwaysValidDate; var rows = []; var months = []; var _loop = function _loop(i) { var classes = "rdtMonth"; var currentMonth = setMonth(_this2.props.viewDate, i); var noOfDaysInMonth = getDaysInMonth(currentMonth); var daysInMonth = Array.from({ length: noOfDaysInMonth }, function (e, i) { return i + 1; }); var validDay = daysInMonth.find(function (d) { var day = _setDate(currentMonth, d); return isValid(day); }); var isDisabled = validDay === undefined; if (isDisabled) { classes += " rdtDisabled"; } if (date && i === getMonth(date) && year === getYear(date)) { classes += " rdtActive"; } var props = { key: i, "data-value": i, className: classes }; if (!isDisabled) { props.onClick = _this2.props.updateOn === "months" ? _this2.updateSelectedMonth : _this2.props.setDate("month"); } months.push(renderer(props, i, year, date)); if (months.length === 4) { rows.push(React.createElement( "tr", { key: i }, months )); months = []; } }; for (var i = 0; i < 12; i++) { _loop(i); } return rows; } }, { key: "updateSelectedMonth", value: function updateSelectedMonth(event) { this.props.updateSelectedDate(event); } }, { key: "renderMonth", value: function renderMonth(props, month, year, selected) { var monthDate = setMonth(new Date(), month); return React.createElement( "td", props, format(monthDate, "MMM", this.getFormatOptions()) ); } }, { key: "alwaysValidDate", value: function alwaysValidDate() { return 1; } }, { key: "handleClickOutside", value: function handleClickOutside() { this.props.handleClickOutside(); } }]); return MonthsView; }(Component); var MonthsView$1 = onClickOutsideHOC(MonthsView); var YearsView = function (_Component) { inherits(YearsView, _Component); function YearsView(props) { classCallCheck(this, YearsView); // Bind functions var _this = possibleConstructorReturn(this, (YearsView.__proto__ || Object.getPrototypeOf(YearsView)).call(this, props)); _this.renderYears = _this.renderYears.bind(_this); _this.updateSelectedYear = _this.updateSelectedYear.bind(_this); _this.renderYear = _this.renderYear.bind(_this); _this.alwaysValidDate = _this.alwaysValidDate.bind(_this); return _this; } createClass(YearsView, [{ key: "render", value: function render() { var year = parseInt(getYear(this.props.viewDate) / 10, 10) * 10; return React.createElement( "div", { className: "rdtYears" }, React.createElement( "table", null, React.createElement( "thead", null, React.createElement( "tr", null, React.createElement( "th", { className: "rdtPrev", onClick: this.props.subtractTime(10, "years") }, React.createElement( "span", null, "\u2039" ) ), React.createElement( "th", { className: "rdtSwitch", onClick: this.props.showView("years"), colSpan: 2 }, year, "-", year + 9 ), React.createElement( "th", { className: "rdtNext", onClick: this.props.addTime(10, "years") }, React.createElement( "span", null, "\u203A" ) ) ) ) ), React.createElement( "table", null, React.createElement( "tbody", null, this.renderYears(year) ) ) ); } }, { key: "renderYears", value: function renderYears(year) { var _this2 = this; var renderer = this.props.renderYear || this.renderYear; var selectedDate = this.props.selectedDate; var date = this.props.viewDate || new Date(); var isValid = this.props.isValidDate || this.alwaysValidDate; var years = []; var rows = []; year--; var _loop = function _loop(i) { var classes = "rdtYear"; var currentYear = setYear(date, year); var noOfDaysInYear = getDaysInYear(date); var daysInYear = Array.from({ length: noOfDaysInYear }, function (e, i) { return i + 1; }); var validDay = daysInYear.find(function (d) { var day = setDayOfYear(currentYear, d); return isValid(day); }); var isDisabled = validDay === undefined; if (isDisabled) { classes += " rdtDisabled"; } if (selectedDate && getYear(selectedDate) === year) { classes += " rdtActive"; } var props = { key: year, "data-value": year, className: classes }; if (!isDisabled) { props.onClick = _this2.props.updateOn === "years" ? _this2.updateSelectedYear : _this2.props.setDate("year"); } years.push(renderer(props, year, selectedDate && new Date(selectedDate.getTime()))); if (years.length === 4) { rows.push(React.createElement( "tr", { key: i }, years )); years = []; } }; for (var i = -1; i < 11; i++, year++) { _loop(i); } return rows; } }, { key: "updateSelectedYear", value: function updateSelectedYear(event) { this.props.updateSelectedDate(event); } }, { key: "renderYear", value: function renderYear(props, year) { return React.createElement( "td", props, year ); } }, { key: "alwaysValidDate", value: function alwaysValidDate() { return 1; } }, { key: "handleClickOutside", value: function handleClickOutside() { this.props.handleClickOutside(); } }]); return YearsView; }(Component); var YearsView$1 = onClickOutsideHOC(YearsView); var padValues = { hours: 1, minutes: 2, seconds: 2, milliseconds: 3 }; var TimeView = function (_Component) { inherits(TimeView, _Component); function TimeView(props) { classCallCheck(this, TimeView); var _this = possibleConstructorReturn(this, (TimeView.__proto__ || Object.getPrototypeOf(TimeView)).call(this, props)); _this.state = _this.calculateState(props); // Bind functions _this.onStartClicking = _this.onStartClicking.bind(_this); _this.disableContextMenu = _this.disableContextMenu.bind(_this); _this.toggleDayPart = _this.toggleDayPart.bind(_this); _this.increase = _this.increase.bind(_this); _this.decrease = _this.decrease.bind(_this); _this.pad = _this.pad.bind(_this); _this.calculateState = _this.calculateState.bind(_this); _this.updateMilli = _this.updateMilli.bind(_this); _this.renderHeader = _this.renderHeader.bind(_this); _this.renderCounter = _this.renderCounter.bind(_this); _this.renderDayPart = _this.renderDayPart.bind(_this); _this.getFormatOptions = _this.getFormatOptions.bind(_this); return _this; } createClass(TimeView, [{ key: "getFormatOptions", value: function getFormatOptions() { return { locale: this.props.locale }; } }, { key: "componentDidMount", value: function componentDidMount() { var _this2 = this; this.timeConstraints = { hours: { min: 0, max: 23, step: 1 }, minutes: { min: 0, max: 59, step: 1 }, seconds: { min: 0, max: 59, step: 1 }, milliseconds: { min: 0, max: 999, step: 1 } }; if (this.props.timeConstraints) { ["hours", "minutes", "seconds", "millisecond"].forEach(function (type) { if (_this2.props.timeConstraints[type]) { _this2.timeConstraints[type] = _extends({}, _this2.timeConstraints[type], _this2.props.timeConstraints[type]); } }); } this.setState(this.calculateState(this.props)); } }, { key: "UNSAFE_componentWillReceiveProps", value: function UNSAFE_componentWillReceiveProps(nextProps) { this.setState(this.calculateState(nextProps)); } }, { key: "onStartClicking", value: function onStartClicking(action, type) { var _this3 = this; return function () { var update = {}; update[type] = _this3[action](type); _this3.setState(update); _this3.timer = setTimeout(function () { _this3.increaseTimer = setInterval(function () { update[type] = _this3[action](type); _this3.setState(update); }, 70); }, 500); _this3.mouseUpListener = function () { clearTimeout(_this3.timer); clearInterval(_this3.increaseTimer); _this3.props.setTime(type, _this3.state[type]); document.body.removeEventListener("mouseup", _this3.mouseUpListener); document.body.removeEventListener("touchend", _this3.mouseUpListener); }; document.body.addEventListener("mouseup", _this3.mouseUpListener); document.body.addEventListener("touchend", _this3.mouseUpListener); }; } }, { key: "disableContextMenu", value: function disableContextMenu(event) { event.preventDefault(); return false; } }, { key: "toggleDayPart", value: function toggleDayPart(type) { var constraints = this.timeConstraints[type]; // type is always 'hours' var value = parseInt(this.state[type], 10) + 12; if (value > constraints.max) { value = constraints.min + (value - (constraints.max + 1)); } return this.pad(type, value); } }, { key: "increase", value: function increase(type) { var constraints = this.timeConstraints[type]; var value = parseInt(this.state[type], 10) + constraints.step; if (value > constraints.max) { value = constraints.min + (value - (constraints.max + 1)); } return this.pad(type, value); } }, { key: "decrease", value: function decrease(type) { var constraints = this.timeConstraints[type]; var value = parseInt(this.state[type], 10) - constraints.step; if (value < constraints.min) { value = constraints.max + 1 - (constraints.min - value); } return this.pad(type, value); } }, { key: "pad", value: function pad(type, value) { var str = value + ""; while (str.length < padValues[type]) { str = "0" + str; }return str; } }, { key: "calculateState", value: function calculateState(props) { var date = props.selectedDate || props.viewDate; var timeFormat = typeof props.timeFormat === "string" ? props.timeFormat.toLowerCase() : ""; var counters = []; if (timeFormat.toLowerCase().indexOf("h") !== -1) { counters.push("hours"); if (timeFormat.indexOf("m") !== -1) { counters.push("minutes"); if (timeFormat.indexOf("s") !== -1) { counters.push("seconds"); } } } var hours = getHours(date); var daypart = undefined; if (this.state !== null && timeFormat.indexOf(" a") !== -1) { if (props.timeFormat.indexOf(" A") !== -1) { daypart = hours >= 12 ? "PM" : "AM"; } else { daypart = hours >= 12 ? "pm" : "am"; } } return { hours: hours, minutes: format(date, "mm", this.getFormatOptions()), seconds: format(date, "ss", this.getFormatOptions()), milliseconds: format(date, "SSS", this.getFormatOptions()), daypart: daypart, counters: counters }; } }, { key: "updateMilli", value: function updateMilli(e) { var milli = parseInt(e.target.value, 10); if (milli === e.target.value && milli >= 0 && milli < 1000) { this.props.setTime("milliseconds", milli); this.setState({ milliseconds: milli }); } } }, { key: "renderHeader", value: function renderHeader() { if (!this.props.dateFormat) { return null; } var date = this.props.selectedDate || this.props.viewDate; return React.createElement( "thead", null, React.createElement( "tr", null, React.createElement( "th", { className: "rdtSwitch", colSpan: 4, onClick: this.props.showView("days") }, format(date, this.props.dateFormat) ) ) ); } }, { key: "renderCounter", value: function renderCounter(type) { var timeFormat = typeof this.props.timeFormat === "string" ? this.props.timeFormat.toLowerCase() : ""; if (type !== "daypart") { var value = this.state[type]; if (type === "hours" && timeFormat.indexOf(" a") !== -1) { value = (value - 1) % 12 + 1; if (value === 0) { value = 12; } } return React.createElement( "div", { key: type, className: "rdtCounter" }, React.createElement( "span", { className: "rdtBtn", onMouseDown: this.onStartClicking("increase", type), onContextMenu: this.disableContextMenu }, "\u25B2" ), React.createElement( "div", { className: "rdtCount" }, value ), React.createElement( "span", { className: "rdtBtn", onMouseDown: this.onStartClicking("decrease", type), onContextMenu: this.disableContextMenu }, "\u25BC" ) ); } return null; } }, { key: "renderDayPart", value: function renderDayPart() { return React.createElement( "div", { key: "dayPart", className: "rdtCounter" }, React.createElement( "span", { className: "rdtBtn", onMouseDown: this.onStartClicking("toggleDayPart", "hours"), onContextMenu: this.disableContextMenu }, "\u25B2" ), React.createElement( "div", { className: "rdtCount" }, this.state.daypart ), React.createElement( "span", { className: "rdtBtn", onMouseDown: this.onStartClicking("toggleDayPart", "hours"), onContextMenu: this.disableContextMenu }, "\u25BC" ) ); } }, { key: "render", value: function render() { var _this4 = this; var counters = []; this.state.counters.forEach(function (c) { if (counters.length) { counters.push(React.createElement( "div", { key: "sep" + counters.length, className: "rdtCounterSeparator" }, ":" )); } counters.push(_this4.renderCounter(c)); }); if (this.state.daypart) { counters.push(this.renderDayPart()); } var timeFormat = this.props.timeFormat || ""; if (this.state.counters.length === 3 && timeFormat.indexOf("S") !== -1) { counters.push(React.createElement( "div", { key: "sep5", className: "rdtCounterSeparator" }, ":" )); counters.push(React.createElement( "div", { key: "ms", className: "rdtCounter rdtMilli" }, React.createElement("input", { type: "text", value: this.state.milliseconds, onChange: this.updateMilli }) )); } return React.createElement( "div", { className: "rdtTime" }, React.createElement( "table", null, this.renderHeader(), React.createElement( "tbody", null, React.createElement( "tr", null, React.createElement( "td", null, React.createElement( "div", { className: "rdtCounters" }, counters ) ) ) ) ) ); } }, { key: "handleClickOutside", value: function handleClickOutside() { this.props.handleClickOutside(); } }]); return TimeView; }(Component); var TimeView$1 = onClickOutsideHOC(TimeView); var viewComponents = { days: DaysView$1, months: MonthsView$1, years: YearsView$1, time: TimeView$1 }; var CalendarContainer = function CalendarContainer(props) { var view = props.view, viewProps = props.viewProps; var Component$$1 = viewComponents[view || "days"]; return React.createElement(Component$$1, viewProps); }; var toUtc = function toUtc(time) { return new Date(time.getTime() + time.getTimezoneOffset() * 60000); }; var toUtc$1 = function toUtc(time) { return new Date(time.getTime() - time.getTimezoneOffset() * 60000); }; var viewModes = Object.freeze({ YEARS: "years", MONTHS: "months", DAYS: "days", TIME: "time" }); var allowedSetTime = Object.freeze({ HOURS: "hours", MINUTES: "minutes", SECONDS: "seconds", MILLISECONDS: "milliseconds" }); var DateTime = function (_Component) { inherits(DateTime, _Component); function DateTime(props) { classCallCheck(this, DateTime); var _this = possibleConstructorReturn(this, (DateTime.__proto__ || Object.getPrototypeOf(DateTime)).call(this, props)); _this.componentProps = { fromProps: ["value", "isValidDate", "renderDay", "renderMonth", "renderYear", "timeConstraints", "locale"], fromState: ["viewDate", "selectedDate", "updateOn"], fromThis: ["setDate", "setTime", "showView", "addTime", "subtractTime", "updateSelectedDate", "handleClickOutside"] }; _this.state = _this.getInitialState(props); // Bind functions _this.getInitialState = _this.getInitialState.bind(_this); _this.parseDate = _this.parseDate.bind(_this); _this.getStateFromProps = _this.getStateFromProps.bind(_this); _this.getUpdateOn = _this.getUpdateOn.bind(_this); _this.getFormats = _this.getFormats.bind(_this); _this.onInputChange = _this.onInputChange.bind(_this); _this.onInputKey = _this.onInputKey.bind(_this); _this.showView = _this.showView.bind(_this); _this.setDate = _this.setDate.bind(_this); _this.subtractTime = _this.subtractTime.bind(_this); _this.addTime = _this.addTime.bind(_this); _this.updateTime = _this.updateTime.bind(_this); _this.setTime = _this.setTime.bind(_this); _this.updateSelectedDate = _this.updateSelectedDate.bind(_this); _this.openCalendar = _this.openCalendar.bind(_this); _this.closeCalendar = _this.closeCalendar.bind(_this); _this.handleClickOutside = _this.handleClickOutside.bind(_this); _this.getComponentProps = _this.getComponentProps.bind(_this); _this.getFormatOptions = _this.getFormatOptions.bind(_this); return _this; } createClass(DateTime, [{ key: "getInitialState", value: function getInitialState(props) { var state = this.getStateFromProps(props); if (state.open === undefined) state.open = !props.input; state.currentView = props.dateFormat ? props.viewMode || state.updateOn || viewModes.DAYS : viewModes.TIME; return state; } }, { key: "parseDate", value: function parseDate(date, formats) { if (date) { var parsedDate = parse(date); if (isDate(parsedDate) && isValidDate(parsedDate)) { return parsedDate; } } return undefined; } }, { key: "getStateFromProps", value: function getStateFromProps(props) { var formats = this.getFormats(props); var selectedDate = this.parseDate(props.value || props.defaultValue, formats); var viewDate = this.parseDate(props.viewDate, formats); viewDate = selectedDate ? startOfMonth(selectedDate) : viewDate ? startOfMonth(viewDate) : startOfMonth(new Date()); var updateOn = this.getUpdateOn(formats); var inputValue = selectedDate ? format(selectedDate, formats.datetime, this.getFormatOptions()) : isDate(props.value) && isValidDate(props.value) ? format(props.value, formats.datetime, this.getFormatOptions()) : isDate(props.defaultValue) && isValidDate(props.defaultValue) ? format(props.defaultValue, formats.datetime, this.getFormatOptions()) : props.defaultValue || ""; return { updateOn: updateOn, inputFormat: formats.datetime, viewDate: viewDate, selectedDate: selectedDate, inputValue: inputValue, open: props.open }; } }, { key: "getUpdateOn", value: function getUpdateOn(formats) { if (formats.date.match(/[lLD]/)) { return viewModes.DAYS; } else if (formats.date.indexOf("M") !== -1) { return viewModes.MONTHS; } else if (formats.date.indexOf("Y") !== -1) { return viewModes.YEARS; } return viewModes.DAYS; } }, { key: "getFormats", value: function getFormats(props) { var formats = { date: props.dateFormat || "", time: props.timeFormat || "" }; if (formats.date === true) { formats.date = "MM/DD/YYYY"; } else if (this.getUpdateOn(formats) !== viewModes.DAYS) { formats.time = ""; } if (formats.time === true) { formats.time = "h:mm A"; } formats.datetime = formats.date && formats.time ? formats.date + " " + formats.time : formats.date || formats.time; return formats; } }, { key: "UNSAFE_componentWillReceiveProps", value: function UNSAFE_componentWillReceiveProps(nextProps) { var formats = this.getFormats(nextProps); var 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) { updatedState.locale = nextProps.locale; if (this.state.viewDate) { var updatedViewDate = this.state.viewDate; updatedState.viewDate = updatedViewDate; } if (this.state.selectedDate) { var updatedSelectedDate = this.state.selectedDate; updatedState.selectedDate = updatedSelectedDate; updatedState.inputValue = format(updatedSelectedDate, formats.datetime, this.getFormatOptions()); } } if (nextProps.utc !== this.props.utc) { // Enabling UTC if (nextProps.utc) { if (this.state.viewDate) { updatedState.viewDate = toUtc(this.state.viewDate); } if (this.state.selectedDate) { updatedState.selectedDate = toUtc(this.state.selectedDate); updatedState.inputValue = format(updatedState.selectedDate, formats.datetime, this.getFormatOptions()); } } // Disabling UTC else { if (this.state.viewDate) { updatedState.viewDate = toUtc$1(this.state.viewDate); } if (this.state.selectedDate) { updatedState.selectedDate = toUtc$1(this.state.selectedDate); updatedState.inputValue = format(updatedState.selectedDate, formats.datetime, this.getFormatOptions()); } } } if (nextProps.viewDate !== this.props.viewDate) { updatedState.viewDate = parse(nextProps.viewDate); } this.setState(updatedState); } }, { key: "onInputChange", value: function onInputChange(e) { var _this2 = this; var value = e.target === null ? e : e.target.value; var date = parse(value); var update = { inputValue: value }; if (isDate(date) && isValidDate(date) && !this.props.value) { update.selectedDate = date; update.viewDate = startOfMonth(date); } else { update.selectedDate = null; } return this.setState(update, function () { return _this2.props.onChange(isDate(date) && isValidDate(date) ? date : _this2.state.inputValue); }); } }, { key: "onInputKey", value: function onInputKey(e) { if (e.which === 9 && this.props.closeOnTab) { this.closeCalendar(); } } }, { key: "showView", value: function showView(view) { var _this3 = this; return function () { if (_this3.state.currentView !== view) { _this3.props.onViewModeChange(view); } _this3.setState({ currentView: view }); }; } }, { key: "setDate", value: function setDate(type) { var _this4 = this; var nextViews = { month: viewModes.DAYS, year: viewModes.MONTHS }; return function (e) { var value = parseInt(e.target.getAttribute("data-value"), 10); var newDate = type === viewModes.DAYS ? startOfDay(_setDate(_this4.state.viewDate, value)) : type === "month