UNPKG

react-calendar

Version:

Ultimate calendar for your React app.

606 lines (605 loc) 27.7 kB
var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; import React, { Component } from 'react'; import PropTypes from 'prop-types'; import clsx from 'clsx'; import Navigation from './Calendar/Navigation'; import CenturyView from './CenturyView'; import DecadeView from './DecadeView'; import YearView from './YearView'; import MonthView from './MonthView'; import { getBegin, getBeginNext, getEnd, getValueRange } from './shared/dates'; import { isCalendarType, isClassName, isMaxDate, isMinDate, isRef, isView, } from './shared/propTypes'; import { between } from './shared/utils'; var baseClassName = 'react-calendar'; var allViews = ['century', 'decade', 'year', 'month']; var allValueTypes = ['decade', 'year', 'month', 'day']; var defaultMinDate = new Date(); defaultMinDate.setFullYear(1, 0, 1); defaultMinDate.setHours(0, 0, 0, 0); var defaultMaxDate = new Date(8.64e15); var defaultProps = { goToRangeStartOnSelect: true, maxDate: defaultMaxDate, maxDetail: 'month', minDate: defaultMinDate, minDetail: 'century', returnValue: 'start', showNavigation: true, showNeighboringMonth: true, }; function toDate(value) { if (value instanceof Date) { return value; } return new Date(value); } /** * Returns views array with disallowed values cut off. */ function getLimitedViews(minDetail, maxDetail) { return allViews.slice(allViews.indexOf(minDetail), allViews.indexOf(maxDetail) + 1); } /** * Determines whether a given view is allowed with currently applied settings. */ function isViewAllowed(view, minDetail, maxDetail) { var views = getLimitedViews(minDetail, maxDetail); return views.indexOf(view) !== -1; } /** * Gets either provided view if allowed by minDetail and maxDetail, or gets * the default view if not allowed. */ function getView(view, minDetail, maxDetail) { if (!view) { return maxDetail; } if (isViewAllowed(view, minDetail, maxDetail)) { return view; } return maxDetail; } /** * Returns value type that can be returned with currently applied settings. */ function getValueType(view) { var index = allViews.indexOf(view); return allValueTypes[index]; } function getValue(value, index) { var rawValue = Array.isArray(value) ? value[index] : value; if (!rawValue) { return null; } var valueDate = toDate(rawValue); if (isNaN(valueDate.getTime())) { throw new Error("Invalid date: ".concat(value)); } return valueDate; } function getDetailValue(_a, index) { var value = _a.value, minDate = _a.minDate, maxDate = _a.maxDate, maxDetail = _a.maxDetail; var valuePiece = getValue(value, index); if (!valuePiece) { return null; } var valueType = getValueType(maxDetail); var detailValueFrom = (function () { switch (index) { case 0: return getBegin(valueType, valuePiece); case 1: return getEnd(valueType, valuePiece); default: throw new Error("Invalid index value: ".concat(index)); } })(); return between(detailValueFrom, minDate, maxDate); } var getDetailValueFrom = function (args) { return getDetailValue(args, 0); }; var getDetailValueTo = function (args) { return getDetailValue(args, 1); }; var getDetailValueArray = function (args) { return [getDetailValueFrom, getDetailValueTo].map(function (fn) { return fn(args); }); }; function getActiveStartDate(props) { var maxDate = props.maxDate, maxDetail = props.maxDetail, minDate = props.minDate, minDetail = props.minDetail, value = props.value, view = props.view; var rangeType = getView(view, minDetail, maxDetail); var valueFrom = getDetailValueFrom({ value: value, minDate: minDate, maxDate: maxDate, maxDetail: maxDetail, }) || new Date(); return getBegin(rangeType, valueFrom); } function getInitialActiveStartDate(props) { var activeStartDate = props.activeStartDate, defaultActiveStartDate = props.defaultActiveStartDate, defaultValue = props.defaultValue, defaultView = props.defaultView, maxDetail = props.maxDetail, minDetail = props.minDetail, value = props.value, view = props.view, otherProps = __rest(props, ["activeStartDate", "defaultActiveStartDate", "defaultValue", "defaultView", "maxDetail", "minDetail", "value", "view"]); var rangeType = getView(view, minDetail, maxDetail); var valueFrom = activeStartDate || defaultActiveStartDate; if (valueFrom) { return getBegin(rangeType, valueFrom); } return getActiveStartDate(__assign({ maxDetail: maxDetail, minDetail: minDetail, value: value || defaultValue, view: view || defaultView }, otherProps)); } function getIsSingleValue(value) { return value && (!Array.isArray(value) || value.length === 1); } var isActiveStartDate = PropTypes.instanceOf(Date); var isValue = PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]); var isValueOrValueArray = PropTypes.oneOfType([isValue, PropTypes.arrayOf(isValue)]); var Calendar = /** @class */ (function (_super) { __extends(Calendar, _super); function Calendar() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.state = { activeStartDate: _this.props.defaultActiveStartDate, hover: null, value: Array.isArray(_this.props.defaultValue) ? _this.props.defaultValue.map(function (el) { return (el !== null ? toDate(el) : el); }) : _this.props.defaultValue !== null && _this.props.defaultValue !== undefined ? toDate(_this.props.defaultValue) : _this.props.defaultValue, view: _this.props.defaultView, }; _this.setStateAndCallCallbacks = function (nextState, event, callback) { var _a = _this, previousActiveStartDate = _a.activeStartDate, previousView = _a.view; var _b = _this .props, allowPartialRange = _b.allowPartialRange, onActiveStartDateChange = _b.onActiveStartDateChange, onChange = _b.onChange, onViewChange = _b.onViewChange, selectRange = _b.selectRange; var prevArgs = { action: undefined, activeStartDate: previousActiveStartDate, value: undefined, view: previousView, }; _this.setState(nextState, function () { var args = { action: nextState.action, activeStartDate: nextState.activeStartDate || _this.activeStartDate, value: ('value' in nextState && nextState.value) || _this.value, view: ('view' in nextState && nextState.view) || _this.view, }; function shouldUpdate(key) { // Key must exist, and… if (!(key in nextState)) { return false; } var nextValue = nextState[key]; var prevValue = prevArgs[key]; // …key changed from defined to undefined or the other way around, or… if (typeof nextValue !== typeof prevValue) { return true; } // …value changed. if (nextValue instanceof Date && prevValue instanceof Date) { return nextValue.getTime() !== prevValue.getTime(); } else { return nextValue !== prevValue; } } if (shouldUpdate('activeStartDate')) { if (onActiveStartDateChange) onActiveStartDateChange(args); } if (shouldUpdate('view')) { if (onViewChange) onViewChange(args); } if (shouldUpdate('value')) { if (onChange) { if (!event) { throw new Error('event is required'); } if (selectRange) { var isSingleValue = getIsSingleValue(nextState.value); if (!isSingleValue) { onChange(nextState.value || null, event); } else if (allowPartialRange) { if (Array.isArray(nextState.value)) { throw new Error('value must not be an array'); } onChange([nextState.value || null, null], event); } } else { onChange(nextState.value || null, event); } } } if (callback) callback(args); }); }; /** * Called when the user uses navigation buttons. */ _this.setActiveStartDate = function (nextActiveStartDate, action) { _this.setStateAndCallCallbacks({ action: action, activeStartDate: nextActiveStartDate, }); }; _this.drillDown = function (nextActiveStartDate, event) { if (!_this.drillDownAvailable) { return; } _this.onClickTile(nextActiveStartDate, event); var _a = _this, view = _a.view, views = _a.views; var onDrillDown = _this.props.onDrillDown; var nextView = views[views.indexOf(view) + 1]; if (!nextView) { throw new Error('Attempted to drill down from the lowest view.'); } _this.setStateAndCallCallbacks({ action: 'drillDown', activeStartDate: nextActiveStartDate, view: nextView, }, undefined, onDrillDown); }; _this.drillUp = function () { if (!_this.drillUpAvailable) { return; } var _a = _this, activeStartDate = _a.activeStartDate, view = _a.view, views = _a.views; var onDrillUp = _this.props.onDrillUp; var nextView = views[views.indexOf(view) - 1]; if (!nextView) { throw new Error('Attempted to drill up from the highest view.'); } var nextActiveStartDate = getBegin(nextView, activeStartDate); _this.setStateAndCallCallbacks({ action: 'drillUp', activeStartDate: nextActiveStartDate, view: nextView, }, undefined, onDrillUp); }; _this.onChange = function (value, event) { var previousValue = _this.value; var _a = _this.props, goToRangeStartOnSelect = _a.goToRangeStartOnSelect, selectRange = _a.selectRange; _this.onClickTile(value, event); var isFirstValueInRange = selectRange && !getIsSingleValue(previousValue); var nextValue; if (selectRange) { // Range selection turned on var valueType = _this.valueType; if (isFirstValueInRange) { // Value has 0 or 2 elements - either way we're starting a new array // First value nextValue = getBegin(valueType, value); } else { if (!previousValue) { throw new Error('previousValue is required'); } if (Array.isArray(previousValue)) { throw new Error('previousValue must not be an array'); } // Second value nextValue = getValueRange(valueType, previousValue, value); } } else { // Range selection turned off nextValue = _this.getProcessedValue(value); } var nextActiveStartDate = // Range selection turned off !selectRange || // Range selection turned on, first value isFirstValueInRange || // Range selection turned on, second value, goToRangeStartOnSelect toggled on goToRangeStartOnSelect ? getActiveStartDate(__assign(__assign({}, _this.props), { value: nextValue })) : null; event.persist(); _this.setStateAndCallCallbacks({ action: 'onChange', activeStartDate: nextActiveStartDate, value: nextValue, }, event); }; _this.onClickTile = function (value, event) { var view = _this.view; var _a = _this .props, onClickDay = _a.onClickDay, onClickDecade = _a.onClickDecade, onClickMonth = _a.onClickMonth, onClickYear = _a.onClickYear; var callback = (function () { switch (view) { case 'century': return onClickDecade; case 'decade': return onClickYear; case 'year': return onClickMonth; case 'month': return onClickDay; default: throw new Error("Invalid view: ".concat(view, ".")); } })(); if (callback) callback(value, event); }; _this.onMouseOver = function (value) { _this.setState(function (prevState) { if (prevState.hover && prevState.hover.getTime() === value.getTime()) { return null; } return { hover: value }; }); }; _this.onMouseLeave = function () { _this.setState({ hover: null }); }; return _this; } Object.defineProperty(Calendar.prototype, "activeStartDate", { get: function () { var activeStartDateProps = this.props.activeStartDate; var activeStartDateState = this.state.activeStartDate; return (activeStartDateProps || activeStartDateState || getInitialActiveStartDate(this.props)); }, enumerable: false, configurable: true }); Object.defineProperty(Calendar.prototype, "value", { get: function () { var _a = this.props, selectRange = _a.selectRange, valueProps = _a.value; var valueState = this.state.value; var rawValue = (function () { // In the middle of range selection, use value from state if (selectRange && getIsSingleValue(valueState)) { return valueState; } return valueProps !== undefined ? valueProps : valueState; })(); if (!rawValue) { return null; } return Array.isArray(rawValue) ? rawValue.map(function (el) { return (el !== null ? toDate(el) : el); }) : rawValue !== null ? toDate(rawValue) : rawValue; }, enumerable: false, configurable: true }); Object.defineProperty(Calendar.prototype, "valueType", { get: function () { var maxDetail = this.props.maxDetail; return getValueType(maxDetail); }, enumerable: false, configurable: true }); Object.defineProperty(Calendar.prototype, "view", { get: function () { var _a = this.props, minDetail = _a.minDetail, maxDetail = _a.maxDetail, viewProps = _a.view; var viewState = this.state.view; return getView(viewProps || viewState, minDetail, maxDetail); }, enumerable: false, configurable: true }); Object.defineProperty(Calendar.prototype, "views", { get: function () { var _a = this.props, minDetail = _a.minDetail, maxDetail = _a.maxDetail; return getLimitedViews(minDetail, maxDetail); }, enumerable: false, configurable: true }); Object.defineProperty(Calendar.prototype, "hover", { get: function () { var selectRange = this.props.selectRange; var hover = this.state.hover; return selectRange ? hover : null; }, enumerable: false, configurable: true }); Object.defineProperty(Calendar.prototype, "drillDownAvailable", { get: function () { var _a = this, view = _a.view, views = _a.views; return views.indexOf(view) < views.length - 1; }, enumerable: false, configurable: true }); Object.defineProperty(Calendar.prototype, "drillUpAvailable", { get: function () { var _a = this, view = _a.view, views = _a.views; return views.indexOf(view) > 0; }, enumerable: false, configurable: true }); /** * Gets current value in a desired format. */ Calendar.prototype.getProcessedValue = function (value) { var _a = this.props, minDate = _a.minDate, maxDate = _a.maxDate, maxDetail = _a.maxDetail, returnValue = _a.returnValue; var processFunction = (function () { switch (returnValue) { case 'start': return getDetailValueFrom; case 'end': return getDetailValueTo; case 'range': return getDetailValueArray; default: throw new Error('Invalid returnValue.'); } })(); return processFunction({ value: value, minDate: minDate, maxDate: maxDate, maxDetail: maxDetail, }); }; Calendar.prototype.renderContent = function (next) { var _a = this, currentActiveStartDate = _a.activeStartDate, onMouseOver = _a.onMouseOver, valueType = _a.valueType, value = _a.value, view = _a.view; var _b = this.props, calendarType = _b.calendarType, locale = _b.locale, maxDate = _b.maxDate, minDate = _b.minDate, selectRange = _b.selectRange, tileClassName = _b.tileClassName, tileContent = _b.tileContent, tileDisabled = _b.tileDisabled; var hover = this.hover; var activeStartDate = next ? getBeginNext(view, currentActiveStartDate) : getBegin(view, currentActiveStartDate); var onClick = this.drillDownAvailable ? this.drillDown : this.onChange; var commonProps = { activeStartDate: activeStartDate, hover: hover, locale: locale, maxDate: maxDate, minDate: minDate, onClick: onClick, onMouseOver: selectRange ? onMouseOver : null, tileClassName: tileClassName, tileContent: tileContent, tileDisabled: tileDisabled, value: value, valueType: valueType, }; switch (view) { case 'century': { var formatYear = this.props.formatYear; return React.createElement(CenturyView, __assign({ formatYear: formatYear }, commonProps)); } case 'decade': { var formatYear = this.props.formatYear; return React.createElement(DecadeView, __assign({ formatYear: formatYear }, commonProps)); } case 'year': { var _c = this.props, formatMonth = _c.formatMonth, formatMonthYear = _c.formatMonthYear; return (React.createElement(YearView, __assign({ formatMonth: formatMonth, formatMonthYear: formatMonthYear }, commonProps))); } case 'month': { var _d = this.props, formatDay = _d.formatDay, formatLongDate = _d.formatLongDate, formatShortWeekday = _d.formatShortWeekday, formatWeekday = _d.formatWeekday, onClickWeekNumber = _d.onClickWeekNumber, showDoubleView = _d.showDoubleView, showFixedNumberOfWeeks = _d.showFixedNumberOfWeeks, showNeighboringMonth = _d.showNeighboringMonth, showWeekNumbers = _d.showWeekNumbers; var onMouseLeave = this.onMouseLeave; return (React.createElement(MonthView, __assign({ calendarType: calendarType, formatDay: formatDay, formatLongDate: formatLongDate, formatShortWeekday: formatShortWeekday, formatWeekday: formatWeekday, onClickWeekNumber: onClickWeekNumber, onMouseLeave: selectRange ? onMouseLeave : null, showFixedNumberOfWeeks: typeof showFixedNumberOfWeeks !== 'undefined' ? showFixedNumberOfWeeks : showDoubleView, showNeighboringMonth: showNeighboringMonth, showWeekNumbers: showWeekNumbers }, commonProps))); } default: throw new Error("Invalid view: ".concat(view, ".")); } }; Calendar.prototype.renderNavigation = function () { var showNavigation = this.props.showNavigation; if (!showNavigation) { return null; } var _a = this, activeStartDate = _a.activeStartDate, view = _a.view, views = _a.views; var _b = this.props, formatMonthYear = _b.formatMonthYear, formatYear = _b.formatYear, locale = _b.locale, maxDate = _b.maxDate, minDate = _b.minDate, navigationAriaLabel = _b.navigationAriaLabel, navigationAriaLive = _b.navigationAriaLive, navigationLabel = _b.navigationLabel, next2AriaLabel = _b.next2AriaLabel, next2Label = _b.next2Label, nextAriaLabel = _b.nextAriaLabel, nextLabel = _b.nextLabel, prev2AriaLabel = _b.prev2AriaLabel, prev2Label = _b.prev2Label, prevAriaLabel = _b.prevAriaLabel, prevLabel = _b.prevLabel, showDoubleView = _b.showDoubleView; return (React.createElement(Navigation, { activeStartDate: activeStartDate, drillUp: this.drillUp, formatMonthYear: formatMonthYear, formatYear: formatYear, locale: locale, maxDate: maxDate, minDate: minDate, navigationAriaLabel: navigationAriaLabel, navigationAriaLive: navigationAriaLive, navigationLabel: navigationLabel, next2AriaLabel: next2AriaLabel, next2Label: next2Label, nextAriaLabel: nextAriaLabel, nextLabel: nextLabel, prev2AriaLabel: prev2AriaLabel, prev2Label: prev2Label, prevAriaLabel: prevAriaLabel, prevLabel: prevLabel, setActiveStartDate: this.setActiveStartDate, showDoubleView: showDoubleView, view: view, views: views })); }; Calendar.prototype.render = function () { var _a = this .props, className = _a.className, inputRef = _a.inputRef, selectRange = _a.selectRange, showDoubleView = _a.showDoubleView; var _b = this, onMouseLeave = _b.onMouseLeave, value = _b.value; var valueArray = Array.isArray(value) ? value : [value]; return (React.createElement("div", { className: clsx(baseClassName, selectRange && valueArray.length === 1 && "".concat(baseClassName, "--selectRange"), showDoubleView && "".concat(baseClassName, "--doubleView"), className), ref: inputRef }, this.renderNavigation(), React.createElement("div", { className: "".concat(baseClassName, "__viewContainer"), onBlur: selectRange ? onMouseLeave : undefined, onMouseLeave: selectRange ? onMouseLeave : undefined }, this.renderContent(), showDoubleView ? this.renderContent(true) : null))); }; Calendar.defaultProps = defaultProps; Calendar.propTypes = { activeStartDate: isActiveStartDate, allowPartialRange: PropTypes.bool, calendarType: isCalendarType, className: isClassName, defaultActiveStartDate: isActiveStartDate, defaultValue: isValueOrValueArray, defaultView: isView, formatDay: PropTypes.func, formatLongDate: PropTypes.func, formatMonth: PropTypes.func, formatMonthYear: PropTypes.func, formatShortWeekday: PropTypes.func, formatWeekday: PropTypes.func, formatYear: PropTypes.func, goToRangeStartOnSelect: PropTypes.bool, inputRef: isRef, locale: PropTypes.string, maxDate: isMaxDate, maxDetail: PropTypes.oneOf(allViews), minDate: isMinDate, minDetail: PropTypes.oneOf(allViews), navigationAriaLabel: PropTypes.string, navigationAriaLive: PropTypes.oneOf(['off', 'polite', 'assertive']), navigationLabel: PropTypes.func, next2AriaLabel: PropTypes.string, next2Label: PropTypes.node, nextAriaLabel: PropTypes.string, nextLabel: PropTypes.node, onActiveStartDateChange: PropTypes.func, onChange: PropTypes.func, onClickDay: PropTypes.func, onClickDecade: PropTypes.func, onClickMonth: PropTypes.func, onClickWeekNumber: PropTypes.func, onClickYear: PropTypes.func, onDrillDown: PropTypes.func, onDrillUp: PropTypes.func, onViewChange: PropTypes.func, prev2AriaLabel: PropTypes.string, prev2Label: PropTypes.node, prevAriaLabel: PropTypes.string, prevLabel: PropTypes.node, returnValue: PropTypes.oneOf(['start', 'end', 'range']), selectRange: PropTypes.bool, showDoubleView: PropTypes.bool, showFixedNumberOfWeeks: PropTypes.bool, showNavigation: PropTypes.bool, showNeighboringMonth: PropTypes.bool, showWeekNumbers: PropTypes.bool, tileClassName: PropTypes.oneOfType([PropTypes.func, isClassName]), tileContent: PropTypes.oneOfType([PropTypes.func, PropTypes.node]), tileDisabled: PropTypes.func, value: isValueOrValueArray, view: isView, }; return Calendar; }(Component)); export default Calendar;