UNPKG

react-date-picker

Version:

A carefully crafted date picker for React

723 lines (568 loc) 22.7 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; 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 _react = require('react'); var _react2 = _interopRequireDefault(_react); var _reactDom = require('react-dom'); var _reactClass = require('react-class'); var _reactClass2 = _interopRequireDefault(_reactClass); var _objectAssign = require('object-assign'); var _objectAssign2 = _interopRequireDefault(_objectAssign); var _join = require('./join'); var _join2 = _interopRequireDefault(_join); var _toMoment2 = require('./toMoment'); var _toMoment3 = _interopRequireDefault(_toMoment2); var _forwardTime = require('./utils/forwardTime'); var _forwardTime2 = _interopRequireDefault(_forwardTime); var _getTransitionEnd = require('./getTransitionEnd'); var _getTransitionEnd2 = _interopRequireDefault(_getTransitionEnd); var _assignDefined = require('./assignDefined'); var _assignDefined2 = _interopRequireDefault(_assignDefined); var _MonthView = require('./MonthView'); var _NavBar = require('./NavBar'); var _NavBar2 = _interopRequireDefault(_NavBar); var _reactFlex = require('react-flex'); var _times = require('./utils/times'); var _times2 = _interopRequireDefault(_times); var _reactInlineBlock = require('react-inline-block'); var _reactInlineBlock2 = _interopRequireDefault(_reactInlineBlock); var _reactStyleNormalizer = require('react-style-normalizer'); var _reactStyleNormalizer2 = _interopRequireDefault(_reactStyleNormalizer); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(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; } function _inherits(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 renderHiddenNav = function renderHiddenNav(props) { return _react2.default.createElement(_reactInlineBlock2.default, _extends({}, props, { style: { visibility: 'hidden' } })); }; var joinFunctions = function joinFunctions(a, b) { if (a && b) { return function () { a.apply(undefined, arguments); b.apply(undefined, arguments); }; } return a || b; }; var TRANSITION_DURATION = '0.4s'; var TransitionView = function (_Component) { _inherits(TransitionView, _Component); function TransitionView(props) { _classCallCheck(this, TransitionView); var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(TransitionView).call(this, props)); var child = _react2.default.Children.toArray(_this.props.children)[0]; var childProps = child.props; var viewDate = props.viewDate || props.defaultViewDate || props.defaultDate || props.date || childProps.viewDate || childProps.defaultViewDate || childProps.defaultDate || childProps.date; var dateFormat = props.dateFormat || childProps.dateFormat; var locale = props.locale || childProps.locale; _this.state = { rendered: false, viewDate: _this.toMoment(viewDate, { dateFormat: dateFormat, locale: locale }) }; return _this; } _createClass(TransitionView, [{ key: 'toMoment', value: function toMoment(value, props) { props = props || this.props; return (0, _toMoment3.default)(value, { locale: props.locale, dateFormat: props.dateFormat }); } }, { key: 'format', value: function format(mom, props) { props = props || this.props; return mom.format(props.dateFormat); } }, { key: 'componentDidMount', value: function componentDidMount() { this.setState({ rendered: true }); } }, { key: 'componentWillReceiveProps', value: function componentWillReceiveProps(nextProps) { if (nextProps.viewDate) { // this is in order to transition when the prop changes // if we were to simply do setState({ viewDate }) it wouldn't have had a transition this.transitionTo(nextProps.viewDate, nextProps); } } }, { key: 'transitionTo', value: function transitionTo(date, props) { props = props || this.props; var dateMoment = this.toMoment(date, props); this.doTransition(dateMoment); } }, { key: 'getViewChild', value: function getViewChild() { return _react2.default.Children.toArray(this.props.children).filter(function (c) { return c && c.props && c.props.isDatePicker; })[0]; } }, { key: 'prepareChildProps', value: function prepareChildProps(child, extraProps) { if (this.view) { return this.view.p; } child = child || this.getViewChild(); return (0, _objectAssign2.default)({}, child.props, extraProps); } }, { key: 'render', value: function render() { var _this2 = this; var props = this.props; var child = this.child = this.getViewChild(); var viewDate = this.state.viewDate || props.viewMoment || props.viewDate; var renderedChildProps = this.renderedChildProps = this.prepareChildProps(child, (0, _assignDefined2.default)({ viewDate: viewDate })); viewDate = this.state.viewDate || renderedChildProps.viewMoment || renderedChildProps.viewDate; if (!this.state.transition) { this.viewDate = viewDate; } var multiView = !!(child.props.size && child.props.size >= 2); var onViewDateChange = joinFunctions(this.onViewDateChange, props.onViewDateChange); // TODO make transition view pass all props, as is to child component var newProps = { key: 'picker', ref: function ref(v) { _this2.view = v; }, viewDate: this.viewDate, onViewDateChange: onViewDateChange, navigation: multiView, constrainActiveInView: props.constrainActiveInView, className: (0, _join2.default)(child.props.className, 'react-date-picker__center') }; // only pass those down if they have been specified // as props on this TransitionView (0, _assignDefined2.default)(newProps, { // tabIndex: -1, range: props.range, date: props.date, activeDate: props.activeDate, footer: false, insideField: props.insideField, defaultRange: props.defaultRange, defaultDate: props.defaultDate, defaultActiveDate: props.defaultActiveDate, // this is here in order to ensure time changes are reflected // when using a TransitionView inside a DateField onTimeChange: props.onTimeChange, onClockInputBlur: props.onClockInputBlur, onClockInputFocus: props.onClockInputFocus, onClockEnterKey: props.onClockEnterKey, onClockEscapeKey: props.onClockEscapeKey, showClock: props.showClock, tabIndex: props.tabIndex, dateFormat: props.dateFormat, locale: props.locale, theme: props.theme, minDate: props.minDate, maxDate: props.maxDate, onKeyDown: this.onKeyDown, onBlur: this.onBlur }); if (props.onChange) { newProps.onChange = joinFunctions(props.onChange, renderedChildProps.onChange); } if (props.onRangeChange) { newProps.onRangeChange = joinFunctions(props.onRangeChange, renderedChildProps.onRangeChange); } if (props.onActiveDateChange) { newProps.onActiveDateChange = joinFunctions(props.onActiveDateChange, renderedChildProps.onActiveDateChange); } if (this.state.transition) { this.transitionDurationStyle = (0, _reactStyleNormalizer2.default)({ transitionDuration: props.transitionDuration || TRANSITION_DURATION }); newProps.style = (0, _objectAssign2.default)({}, child.props.style, this.transitionDurationStyle); newProps.className = (0, _join2.default)(newProps.className, 'react-date-picker--transition', 'react-date-picker--transition-' + (this.state.transition == -1 ? 'left' : 'right')); } var navBar = void 0; var navBarProps = { minDate: props.minDate || renderedChildProps.minDate, maxDate: props.maxDate || renderedChildProps.maxDate, enableHistoryView: props.enableHistoryView === undefined ? renderedChildProps.enableHistoryView : props.enableHistoryView, secondary: true, viewDate: this.nextViewDate || this.viewDate, onViewDateChange: onViewDateChange, multiView: multiView }; if (props.navigation) { navBar = this.renderNavBar((0, _objectAssign2.default)({}, navBarProps, { mainNavBar: true })); } var footer = void 0; if (props.footer) { footer = (0, _MonthView.renderFooter)(props, props.insideField ? props : this.view); } if (multiView) { newProps.renderNavBar = this.renderMultiViewNavBar.bind(this, navBarProps); } var clone = _react2.default.cloneElement(child, newProps); return _react2.default.createElement( _reactFlex.Flex, _extends({ column: true, inline: true, wrap: false, alignItems: 'stretch' }, props, { className: (0, _join2.default)(props.className, 'react-date-picker__transition-month-view', props.theme && 'react-date-picker__transition-month-view--theme-' + props.theme) }), navBar, _react2.default.createElement( _reactFlex.Flex, { inline: true, row: true, style: { position: 'relative' } }, this.renderAt(-1, { multiView: multiView, navBarProps: navBarProps }), clone, this.renderAt(1, { multiView: multiView, navBarProps: navBarProps }) ), footer ); } }, { key: 'tryNavBarKeyDown', value: function tryNavBarKeyDown(event) { if (this.navBar && this.navBar.getHistoryView) { var historyView = this.navBar.getHistoryView(); if (historyView && historyView.onKeyDown) { historyView.onKeyDown(event); return true; } } return false; } }, { key: 'onKeyDown', value: function onKeyDown(event) { var initialKeyDown = this.child.onKeyDown; if (this.tryNavBarKeyDown(event)) { return false; } if (initialKeyDown) { return initialKeyDown(event); } } }, { key: 'isHistoryViewVisible', value: function isHistoryViewVisible() { if (this.navBar && this.navBar.isHistoryViewVisible) { return this.navBar.isHistoryViewVisible(); } return false; } }, { key: 'showHistoryView', value: function showHistoryView() { if (this.navBar) { this.navBar.showHistoryView(); } } }, { key: 'hideHistoryView', value: function hideHistoryView() { if (this.navBar) { this.navBar.hideHistoryView(); } } }, { key: 'onBlur', value: function onBlur(event) { var initialBlur = this.child.onBlur; this.hideHistoryView(); if (initialBlur) { initialBlur(event); } return true; } /** * This method is only called when rendering the NavBar of the MonthViews * that are not on the first row of the MultiMonthView * * @param {Object} navBarProps * @param {Object} config * @return {ReactNode} */ }, { key: 'renderMultiViewNavBar', value: function renderMultiViewNavBar(navBarProps, config) { var index = config.index; var count = this.child.props.perRow; if (index >= count) { var viewDate = this.toMoment(navBarProps.viewDate).add(index, 'month'); return _react2.default.createElement(_NavBar2.default, _extends({}, navBarProps, { renderNavNext: renderHiddenNav, renderNavPrev: renderHiddenNav, onViewDateChange: null, viewDate: this.toMoment(viewDate) })); } return null; } }, { key: 'renderNavBar', value: function renderNavBar(navBarProps) { var _this3 = this; navBarProps = (0, _objectAssign2.default)({}, navBarProps); if (navBarProps.mainNavBar) { navBarProps.ref = function (navBar) { _this3.navBar = navBar; }; navBarProps.onMouseDown = this.onNavMouseDown; } var props = this.props; var _navBarProps = navBarProps; var multiView = _navBarProps.multiView; var navBar = _react2.default.Children.toArray(props.children).filter(function (c) { return c && c.props && c.props.isDatePickerNavBar; })[0]; var newProps = navBarProps; if (navBar) { newProps = (0, _objectAssign2.default)({}, navBarProps, navBar.props); // have viewDate & onViewDateChange win over initial navBar.props newProps.viewDate = navBarProps.viewDate; newProps.onViewDateChange = navBarProps.onViewDateChange; } if (multiView) { var _ret = function () { var count = _this3.child.props.perRow; var viewSize = _this3.getViewSize(); var bars = (0, _times2.default)(count).map(function (index) { var onUpdate = function onUpdate(dateMoment, dir) { var mom = _this3.toMoment(newProps.viewDate); if (Math.abs(dir) == 1) { mom.add(dir * viewSize, 'month'); } else { var sign = dir > 0 ? 1 : -1; mom.add(sign, 'year'); } return mom; }; var barProps = (0, _objectAssign2.default)({}, newProps, { onUpdate: onUpdate, renderNavNext: renderHiddenNav, renderNavPrev: renderHiddenNav, viewDate: _this3.toMoment(newProps.viewDate).add(index, 'month') }); if (index == 0) { delete barProps.renderNavPrev; } if (index == count - 1) { delete barProps.renderNavNext; } return _react2.default.createElement(_NavBar2.default, _extends({ flex: true }, barProps)); }); return { v: _react2.default.createElement(_reactFlex.Flex, { row: true, children: bars }) }; }(); if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v; } return navBar ? _react2.default.cloneElement(navBar, newProps) : _react2.default.createElement(_NavBar2.default, newProps); } }, { key: 'getViewSize', value: function getViewSize() { return this.view && this.view.getViewSize ? this.view.getViewSize() || 1 : 1; } }, { key: 'renderAt', value: function renderAt(index, _ref) { var multiView = _ref.multiView; var navBarProps = _ref.navBarProps; if (!this.state.rendered || !this.view) { // || this.state.prepareTransition != -index ) { return null; } var viewSize = this.getViewSize(); var viewDiff = viewSize * index; var childProps = this.child.props; var renderedProps = this.renderedChildProps; var viewDate = this.toMoment(this.viewDate).add(viewDiff, 'month'); if (this.nextViewDate && this.state.prepareTransition == -index) { // we're transitioning to this viewDate, so make sure // it renders the date we'll need at the end of the transition viewDate = this.nextViewDate; } var date = renderedProps.date || renderedProps.moment; if (this.state.transitionTime) { date = (0, _forwardTime2.default)(this.state.transitionTime, this.toMoment(date)); // console.log('date.format', date.format('HH:mm')); } var newProps = (0, _objectAssign2.default)({ date: date, readOnly: true, range: renderedProps.range, activeDate: renderedProps.activeDate, dateFormat: renderedProps.dateFormat, locale: renderedProps.locale, tabIndex: -1, clockTabIndex: -1, navigation: multiView, viewDate: viewDate, key: index, footer: false, className: (0, _join2.default)(childProps.className, 'react-date-picker__' + (index == -1 ? 'prev' : 'next')) }); (0, _assignDefined2.default)(newProps, { showClock: renderedProps.showClock, minDate: renderedProps.minDate, maxDate: renderedProps.maxDate }); if (this.state.transition && this.state.transition != index) { newProps.style = (0, _objectAssign2.default)({}, childProps.style, this.transitionDurationStyle); newProps.className = (0, _join2.default)(newProps.className, 'react-date-picker--transition', 'react-date-picker--transition-' + (this.state.transition == -1 ? 'left' : 'right')); } if (multiView) { newProps.renderNavBar = this.renderMultiViewNavBar.bind(this, (0, _objectAssign2.default)({}, navBarProps, { viewDate: viewDate, onViewDateChange: null })); } return _react2.default.cloneElement(this.child, newProps); } }, { key: 'getView', value: function getView() { return this.view; } }, { key: 'isInView', value: function isInView() { var _view; return (_view = this.view).isInView.apply(_view, arguments); } }, { key: 'onViewDateChange', value: function onViewDateChange(dateString, _ref2) { var dateMoment = _ref2.dateMoment; this.doTransition(dateMoment); } }, { key: 'doTransition', value: function doTransition(dateMoment) { var _this4 = this; if (this.state.transition) { // this.nextViewDate = dateMoment return; } // to protect of null, which will default to current date dateMoment = this.toMoment(dateMoment); var newMoment = this.toMoment(dateMoment).startOf('month'); var viewMoment = this.toMoment(this.viewDate).startOf('month'); if (newMoment.format('YYYY-MM') == viewMoment.format('YYYY-MM')) { return; } var navNext = newMoment.isAfter(viewMoment); var transition = navNext ? -1 : 1; var viewSize = this.getViewSize(); if (Math.abs(viewSize) > 1) { var temp = this.toMoment(viewMoment).add(viewSize * -transition, 'month'); if (navNext) { dateMoment = dateMoment.isAfter(temp) ? dateMoment : temp; } else { dateMoment = dateMoment.isBefore(temp) ? dateMoment : temp; } } var transitionTime = this.props.getTransitionTime ? this.props.getTransitionTime() : null; this.setState({ transitionTime: transitionTime, prepareTransition: transition }, function () { setTimeout(function () { // in order to allow this.view.p to update if (!(0, _reactDom.findDOMNode)(_this4.view)) { return; } _this4.nextViewDate = dateMoment; _this4.addTransitionEnd(); _this4.setState({ transition: transition }); }); }); } }, { key: 'addTransitionEnd', value: function addTransitionEnd() { var dom = (0, _reactDom.findDOMNode)(this.view); if (dom) { dom.addEventListener((0, _getTransitionEnd2.default)(), this.onTransitionEnd, false); } } }, { key: 'removeTransitionEnd', value: function removeTransitionEnd(dom) { dom = dom || (0, _reactDom.findDOMNode)(this.view); if (dom) { dom.removeEventListener((0, _getTransitionEnd2.default)(), this.onTransitionEnd); } } }, { key: 'onTransitionEnd', value: function onTransitionEnd() { this.removeTransitionEnd(); if (!this.nextViewDate) { return; } this.setState({ viewDate: this.nextViewDate, transition: 0, prepareTransition: 0 }); if (this.props.focusOnTransitionEnd) { this.focus(); } delete this.nextViewDate; } }, { key: 'onNavMouseDown', value: function onNavMouseDown() { if (this.props.focusOnNavMouseDown && !this.isFocused()) { this.focus(); } } }, { key: 'isFocused', value: function isFocused() { var view = this.getView(); if (view) { return view.isFocused(); } return false; } }, { key: 'focus', value: function focus() { this.getView().focus(); } }]); return TransitionView; }(_reactClass2.default); exports.default = TransitionView; TransitionView.propTypes = { children: _react2.default.PropTypes.node.isRequired }; TransitionView.defaultProps = { focusOnNavMouseDown: true, onTransitionStart: function onTransitionStart() {}, onTransitionEnd: function onTransitionEnd() {}, footerClearDate: null, enableHistoryView: true, constrainActiveInView: false, focusOnTransitionEnd: false, navigation: true, theme: 'default', isDatePicker: true };