react-calendar
Version:
Ultimate calendar for your React app.
606 lines (605 loc) • 27.7 kB
JavaScript
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;