react-calendar
Version:
Ultimate calendar for your React app.
459 lines (458 loc) • 20.4 kB
JavaScript
'use client';
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);
};
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { forwardRef, useCallback, useImperativeHandle, useState } from 'react';
import clsx from 'clsx';
import Navigation from './Calendar/Navigation.js';
import CenturyView from './CenturyView.js';
import DecadeView from './DecadeView.js';
import YearView from './YearView.js';
import MonthView from './MonthView.js';
import { getBegin, getBeginNext, getEnd, getValueRange } from './shared/dates.js';
import { between } from './shared/utils.js';
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);
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 (Number.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(_a) {
var maxDate = _a.maxDate, maxDetail = _a.maxDetail, minDate = _a.minDate, minDetail = _a.minDetail, value = _a.value, view = _a.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(_a) {
var activeStartDate = _a.activeStartDate, defaultActiveStartDate = _a.defaultActiveStartDate, defaultValue = _a.defaultValue, defaultView = _a.defaultView, maxDate = _a.maxDate, maxDetail = _a.maxDetail, minDate = _a.minDate, minDetail = _a.minDetail, value = _a.value, view = _a.view;
var rangeType = getView(view, minDetail, maxDetail);
var valueFrom = activeStartDate || defaultActiveStartDate;
if (valueFrom) {
return getBegin(rangeType, valueFrom);
}
return getActiveStartDate({
maxDate: maxDate,
maxDetail: maxDetail,
minDate: minDate,
minDetail: minDetail,
value: value || defaultValue,
view: view || defaultView,
});
}
function getIsSingleValue(value) {
return value && (!Array.isArray(value) || value.length === 1);
}
function areDatesEqual(date1, date2) {
return date1 instanceof Date && date2 instanceof Date && date1.getTime() === date2.getTime();
}
var Calendar = forwardRef(function Calendar(props, ref) {
var activeStartDateProps = props.activeStartDate, allowPartialRange = props.allowPartialRange, calendarType = props.calendarType, className = props.className, defaultActiveStartDate = props.defaultActiveStartDate, defaultValue = props.defaultValue, defaultView = props.defaultView, formatDay = props.formatDay, formatLongDate = props.formatLongDate, formatMonth = props.formatMonth, formatMonthYear = props.formatMonthYear, formatShortWeekday = props.formatShortWeekday, formatWeekday = props.formatWeekday, formatYear = props.formatYear, _a = props.goToRangeStartOnSelect, goToRangeStartOnSelect = _a === void 0 ? true : _a, inputRef = props.inputRef, locale = props.locale, _b = props.maxDate, maxDate = _b === void 0 ? defaultMaxDate : _b, _c = props.maxDetail, maxDetail = _c === void 0 ? 'month' : _c, _d = props.minDate, minDate = _d === void 0 ? defaultMinDate : _d, _e = props.minDetail, minDetail = _e === void 0 ? 'century' : _e, navigationAriaLabel = props.navigationAriaLabel, navigationAriaLive = props.navigationAriaLive, navigationLabel = props.navigationLabel, next2AriaLabel = props.next2AriaLabel, next2Label = props.next2Label, nextAriaLabel = props.nextAriaLabel, nextLabel = props.nextLabel, onActiveStartDateChange = props.onActiveStartDateChange, onChangeProps = props.onChange, onClickDay = props.onClickDay, onClickDecade = props.onClickDecade, onClickMonth = props.onClickMonth, onClickWeekNumber = props.onClickWeekNumber, onClickYear = props.onClickYear, onDrillDown = props.onDrillDown, onDrillUp = props.onDrillUp, onViewChange = props.onViewChange, prev2AriaLabel = props.prev2AriaLabel, prev2Label = props.prev2Label, prevAriaLabel = props.prevAriaLabel, prevLabel = props.prevLabel, _f = props.returnValue, returnValue = _f === void 0 ? 'start' : _f, selectRange = props.selectRange, showDoubleView = props.showDoubleView, showFixedNumberOfWeeks = props.showFixedNumberOfWeeks, _g = props.showNavigation, showNavigation = _g === void 0 ? true : _g, showNeighboringCentury = props.showNeighboringCentury, showNeighboringDecade = props.showNeighboringDecade, _h = props.showNeighboringMonth, showNeighboringMonth = _h === void 0 ? true : _h, showWeekNumbers = props.showWeekNumbers, tileClassName = props.tileClassName, tileContent = props.tileContent, tileDisabled = props.tileDisabled, valueProps = props.value, viewProps = props.view;
var _j = useState(defaultActiveStartDate), activeStartDateState = _j[0], setActiveStartDateState = _j[1];
var _k = useState(null), hoverState = _k[0], setHoverState = _k[1];
var _l = useState(Array.isArray(defaultValue)
? defaultValue.map(function (el) { return (el !== null ? toDate(el) : null); })
: defaultValue !== null && defaultValue !== undefined
? toDate(defaultValue)
: null), valueState = _l[0], setValueState = _l[1];
var _m = useState(defaultView), viewState = _m[0], setViewState = _m[1];
var activeStartDate = activeStartDateProps ||
activeStartDateState ||
getInitialActiveStartDate({
activeStartDate: activeStartDateProps,
defaultActiveStartDate: defaultActiveStartDate,
defaultValue: defaultValue,
defaultView: defaultView,
maxDate: maxDate,
maxDetail: maxDetail,
minDate: minDate,
minDetail: minDetail,
value: valueProps,
view: viewProps,
});
var value = (function () {
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) : null); })
: rawValue !== null
? toDate(rawValue)
: null;
})();
var valueType = getValueType(maxDetail);
var view = getView(viewProps || viewState, minDetail, maxDetail);
var views = getLimitedViews(minDetail, maxDetail);
var hover = selectRange ? hoverState : null;
var drillDownAvailable = views.indexOf(view) < views.length - 1;
var drillUpAvailable = views.indexOf(view) > 0;
var getProcessedValue = useCallback(function (value) {
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({
maxDate: maxDate,
maxDetail: maxDetail,
minDate: minDate,
value: value,
});
}, [maxDate, maxDetail, minDate, returnValue]);
var setActiveStartDate = useCallback(function (nextActiveStartDate, action) {
setActiveStartDateState(nextActiveStartDate);
var args = {
action: action,
activeStartDate: nextActiveStartDate,
value: value,
view: view,
};
if (onActiveStartDateChange && !areDatesEqual(activeStartDate, nextActiveStartDate)) {
onActiveStartDateChange(args);
}
}, [activeStartDate, onActiveStartDateChange, value, view]);
var onClickTile = useCallback(function (value, event) {
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);
}, [onClickDay, onClickDecade, onClickMonth, onClickYear, view]);
var drillDown = useCallback(function (nextActiveStartDate, event) {
if (!drillDownAvailable) {
return;
}
onClickTile(nextActiveStartDate, event);
var nextView = views[views.indexOf(view) + 1];
if (!nextView) {
throw new Error('Attempted to drill down from the lowest view.');
}
setActiveStartDateState(nextActiveStartDate);
setViewState(nextView);
var args = {
action: 'drillDown',
activeStartDate: nextActiveStartDate,
value: value,
view: nextView,
};
if (onActiveStartDateChange && !areDatesEqual(activeStartDate, nextActiveStartDate)) {
onActiveStartDateChange(args);
}
if (onViewChange && view !== nextView) {
onViewChange(args);
}
if (onDrillDown) {
onDrillDown(args);
}
}, [
activeStartDate,
drillDownAvailable,
onActiveStartDateChange,
onClickTile,
onDrillDown,
onViewChange,
value,
view,
views,
]);
var drillUp = useCallback(function () {
if (!drillUpAvailable) {
return;
}
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);
setActiveStartDateState(nextActiveStartDate);
setViewState(nextView);
var args = {
action: 'drillUp',
activeStartDate: nextActiveStartDate,
value: value,
view: nextView,
};
if (onActiveStartDateChange && !areDatesEqual(activeStartDate, nextActiveStartDate)) {
onActiveStartDateChange(args);
}
if (onViewChange && view !== nextView) {
onViewChange(args);
}
if (onDrillUp) {
onDrillUp(args);
}
}, [
activeStartDate,
drillUpAvailable,
onActiveStartDateChange,
onDrillUp,
onViewChange,
value,
view,
views,
]);
var onChange = useCallback(function (rawNextValue, event) {
var previousValue = value;
onClickTile(rawNextValue, event);
var isFirstValueInRange = selectRange && !getIsSingleValue(previousValue);
var nextValue;
if (selectRange) {
// Range selection turned on
if (isFirstValueInRange) {
// Value has 0 or 2 elements - either way we're starting a new array
// First value
nextValue = getBegin(valueType, rawNextValue);
}
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, rawNextValue);
}
}
else {
// Range selection turned off
nextValue = getProcessedValue(rawNextValue);
}
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({
maxDate: maxDate,
maxDetail: maxDetail,
minDate: minDate,
minDetail: minDetail,
value: nextValue,
view: view,
})
: null;
event.persist();
setActiveStartDateState(nextActiveStartDate);
setValueState(nextValue);
var args = {
action: 'onChange',
activeStartDate: nextActiveStartDate,
value: nextValue,
view: view,
};
if (onActiveStartDateChange && !areDatesEqual(activeStartDate, nextActiveStartDate)) {
onActiveStartDateChange(args);
}
if (onChangeProps) {
if (selectRange) {
var isSingleValue = getIsSingleValue(nextValue);
if (!isSingleValue) {
onChangeProps(nextValue || null, event);
}
else if (allowPartialRange) {
if (Array.isArray(nextValue)) {
throw new Error('value must not be an array');
}
onChangeProps([nextValue || null, null], event);
}
}
else {
onChangeProps(nextValue || null, event);
}
}
}, [
activeStartDate,
allowPartialRange,
getProcessedValue,
goToRangeStartOnSelect,
maxDate,
maxDetail,
minDate,
minDetail,
onActiveStartDateChange,
onChangeProps,
onClickTile,
selectRange,
value,
valueType,
view,
]);
function onMouseOver(nextHover) {
setHoverState(nextHover);
}
function onMouseLeave() {
setHoverState(null);
}
useImperativeHandle(ref, function () { return ({
activeStartDate: activeStartDate,
drillDown: drillDown,
drillUp: drillUp,
onChange: onChange,
setActiveStartDate: setActiveStartDate,
value: value,
view: view,
}); }, [activeStartDate, drillDown, drillUp, onChange, setActiveStartDate, value, view]);
function renderContent(next) {
var currentActiveStartDate = next
? getBeginNext(view, activeStartDate)
: getBegin(view, activeStartDate);
var onClick = drillDownAvailable ? drillDown : onChange;
var commonProps = {
activeStartDate: currentActiveStartDate,
hover: hover,
locale: locale,
maxDate: maxDate,
minDate: minDate,
onClick: onClick,
onMouseOver: selectRange ? onMouseOver : undefined,
tileClassName: tileClassName,
tileContent: tileContent,
tileDisabled: tileDisabled,
value: value,
valueType: valueType,
};
switch (view) {
case 'century': {
return (_jsx(CenturyView, __assign({ formatYear: formatYear, showNeighboringCentury: showNeighboringCentury }, commonProps)));
}
case 'decade': {
return (_jsx(DecadeView, __assign({ formatYear: formatYear, showNeighboringDecade: showNeighboringDecade }, commonProps)));
}
case 'year': {
return (_jsx(YearView, __assign({ formatMonth: formatMonth, formatMonthYear: formatMonthYear }, commonProps)));
}
case 'month': {
return (_jsx(MonthView, __assign({ calendarType: calendarType, formatDay: formatDay, formatLongDate: formatLongDate, formatShortWeekday: formatShortWeekday, formatWeekday: formatWeekday, onClickWeekNumber: onClickWeekNumber, onMouseLeave: selectRange ? onMouseLeave : undefined, showFixedNumberOfWeeks: typeof showFixedNumberOfWeeks !== 'undefined'
? showFixedNumberOfWeeks
: showDoubleView, showNeighboringMonth: showNeighboringMonth, showWeekNumbers: showWeekNumbers }, commonProps)));
}
default:
throw new Error("Invalid view: ".concat(view, "."));
}
}
function renderNavigation() {
if (!showNavigation) {
return null;
}
return (_jsx(Navigation, { activeStartDate: activeStartDate, drillUp: 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: setActiveStartDate, showDoubleView: showDoubleView, view: view, views: views }));
}
var valueArray = Array.isArray(value) ? value : [value];
return (_jsxs("div", { className: clsx(baseClassName, selectRange && valueArray.length === 1 && "".concat(baseClassName, "--selectRange"), showDoubleView && "".concat(baseClassName, "--doubleView"), className), ref: inputRef, children: [renderNavigation(), _jsxs("div", { className: "".concat(baseClassName, "__viewContainer"), onBlur: selectRange ? onMouseLeave : undefined, onMouseLeave: selectRange ? onMouseLeave : undefined, children: [renderContent(), showDoubleView ? renderContent(true) : null] })] }));
});
export default Calendar;