devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
1,196 lines (1,194 loc) • 55.7 kB
JavaScript
/**
* DevExtreme (cjs/__internal/ui/calendar/m_calendar.js)
* Version: 24.2.6
* Build date: Mon Mar 17 2025
*
* Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _animation = require("../../../common/core/animation");
var _translator = require("../../../common/core/animation/translator");
var _events_engine = _interopRequireDefault(require("../../../common/core/events/core/events_engine"));
var _swipeable = _interopRequireDefault(require("../../../common/core/events/gesture/swipeable"));
var _hover = require("../../../common/core/events/hover");
var _index = require("../../../common/core/events/utils/index");
var _date = _interopRequireDefault(require("../../../common/core/localization/date"));
var _message = _interopRequireDefault(require("../../../common/core/localization/message"));
var _component_registrator = _interopRequireDefault(require("../../../core/component_registrator"));
var _devices = _interopRequireDefault(require("../../../core/devices"));
var _guid = _interopRequireDefault(require("../../../core/guid"));
var _renderer = _interopRequireDefault(require("../../../core/renderer"));
var _function_template = require("../../../core/templates/function_template");
var _date2 = _interopRequireDefault(require("../../../core/utils/date"));
var _date_serialization = _interopRequireDefault(require("../../../core/utils/date_serialization"));
var _extend = require("../../../core/utils/extend");
var _math = require("../../../core/utils/math");
var _size = require("../../../core/utils/size");
var _type = require("../../../core/utils/type");
var _window = require("../../../core/utils/window");
var _button = _interopRequireDefault(require("../../../ui/button"));
var _themes = require("../../../ui/themes");
var _editor = _interopRequireDefault(require("../../ui/editor/editor"));
var _m_calendarMultipleSelection = _interopRequireDefault(require("./m_calendar.multiple.selection.strategy"));
var _m_calendar = _interopRequireDefault(require("./m_calendar.navigator"));
var _m_calendarRangeSelection = _interopRequireDefault(require("./m_calendar.range.selection.strategy"));
var _m_calendarSingleSelection = _interopRequireDefault(require("./m_calendar.single.selection.strategy"));
var _m_calendar2 = _interopRequireDefault(require("./m_calendar.views"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : {
default: e
}
}
function _extends() {
return _extends = Object.assign ? Object.assign.bind() : function(n) {
for (var e = 1; e < arguments.length; e++) {
var t = arguments[e];
for (var r in t) {
({}).hasOwnProperty.call(t, r) && (n[r] = t[r])
}
}
return n
}, _extends.apply(null, arguments)
}
const CALENDAR_CLASS = "dx-calendar";
const CALENDAR_BODY_CLASS = "dx-calendar-body";
const CALENDAR_CELL_CLASS = "dx-calendar-cell";
const CALENDAR_FOOTER_CLASS = "dx-calendar-footer";
const CALENDAR_TODAY_BUTTON_CLASS = "dx-calendar-today-button";
const CALENDAR_HAS_FOOTER_CLASS = "dx-calendar-with-footer";
const CALENDAR_VIEWS_WRAPPER_CLASS = "dx-calendar-views-wrapper";
const CALENDAR_VIEW_CLASS = "dx-calendar-view";
const CALENDAR_MULTIVIEW_CLASS = "dx-calendar-multiview";
const CALENDAR_RANGE_CLASS = "dx-calendar-range";
const GESTURE_COVER_CLASS = "dx-gesture-cover";
const ANIMATION_DURATION_SHOW_VIEW = 250;
const POP_ANIMATION_FROM = .6;
const POP_ANIMATION_TO = 1;
const CALENDAR_INPUT_STANDARD_PATTERN = "yyyy-MM-dd";
const CALENDAR_DATE_VALUE_KEY = "dxDateValueKey";
const CALENDAR_DXHOVEREND_EVENT_NAME = (0, _index.addNamespace)(_hover.end, "dxCalendar");
const LEVEL_COMPARE_MAP = {
month: 3,
year: 2,
decade: 1,
century: 0
};
const ZOOM_LEVEL = {
MONTH: "month",
YEAR: "year",
DECADE: "decade",
CENTURY: "century"
};
const SELECTION_STRATEGIES = {
SingleSelection: _m_calendarSingleSelection.default,
MultipleSelection: _m_calendarMultipleSelection.default,
RangeSelection: _m_calendarRangeSelection.default
};
class Calendar extends _editor.default {
_getDefaultOptions() {
return _extends({}, super._getDefaultOptions(), {
hoverStateEnabled: true,
activeStateEnabled: true,
currentDate: new Date,
value: null,
min: new Date(1e3, 0),
max: new Date(3e3, 0),
viewsCount: 1,
zoomLevel: ZOOM_LEVEL.MONTH,
maxZoomLevel: ZOOM_LEVEL.MONTH,
minZoomLevel: ZOOM_LEVEL.CENTURY,
selectionMode: "single",
selectWeekOnClick: true,
showTodayButton: false,
showWeekNumbers: false,
weekNumberRule: "auto",
cellTemplate: "cell",
disabledDates: null,
onCellClick: null,
onContouredChanged: null,
skipFocusCheck: false,
_todayDate: () => new Date
})
}
_defaultOptionsRules() {
return super._defaultOptionsRules().concat([{
device: () => "desktop" === _devices.default.real().deviceType && !_devices.default.isSimulator(),
options: {
focusStateEnabled: true
}
}])
}
_supportedKeys() {
return _extends({}, super._supportedKeys(), {
rightArrow(e) {
e.preventDefault();
if ((0, _index.isCommandKeyPressed)(e)) {
this._waitRenderView(1)
} else {
this._moveCurrentDateByOffset(1 * this._getRtlCorrection())
}
},
leftArrow(e) {
e.preventDefault();
if ((0, _index.isCommandKeyPressed)(e)) {
this._waitRenderView(-1)
} else {
this._moveCurrentDateByOffset(-1 * this._getRtlCorrection())
}
},
upArrow(e) {
e.preventDefault();
if ((0, _index.isCommandKeyPressed)(e)) {
this._navigateUp()
} else {
if (_animation.fx.isAnimating(this._view.$element())) {
return
}
this._moveCurrentDateByOffset(-1 * this._view.option("colCount"))
}
},
downArrow(e) {
e.preventDefault();
if ((0, _index.isCommandKeyPressed)(e)) {
this._navigateDown()
} else {
if (_animation.fx.isAnimating(this._view.$element())) {
return
}
this._moveCurrentDateByOffset(1 * this._view.option("colCount"))
}
},
home(e) {
e.preventDefault();
const zoomLevel = this.option("zoomLevel");
const currentDate = this.option("currentDate");
const min = this._dateOption("min");
if (this._view.isDateDisabled(currentDate)) {
return
}
const date = _date2.default.sameView(zoomLevel, currentDate, min) ? min : _date2.default.getViewFirstCellDate(zoomLevel, currentDate);
this._moveToClosestAvailableDate(date)
},
end(e) {
e.preventDefault();
const zoomLevel = this.option("zoomLevel");
const currentDate = this.option("currentDate");
const max = this._dateOption("max");
if (this._view.isDateDisabled(currentDate)) {
return
}
const date = _date2.default.sameView(zoomLevel, currentDate, max) ? max : _date2.default.getViewLastCellDate(zoomLevel, currentDate);
this._moveToClosestAvailableDate(date)
},
pageUp(e) {
e.preventDefault();
this._waitRenderView(-1 * this._getRtlCorrection())
},
pageDown(e) {
e.preventDefault();
this._waitRenderView(1 * this._getRtlCorrection())
},
tab() {},
enter: this._enterKeyHandler
})
}
_enterKeyHandler(e) {
if (!this._isMaxZoomLevel()) {
this._navigateDown()
} else if (!this._view.isDateDisabled(this.option("currentDate"))) {
const value = this._updateTimeComponent(this.option("currentDate"));
this._selectionStrategy.selectValue(value, e)
}
}
_getSerializationFormat(optionName) {
const value = this.option(optionName || "value");
if (this.option("dateSerializationFormat")) {
return this.option("dateSerializationFormat")
}
if ((0, _type.isNumeric)(value)) {
return "number"
}
if (!(0, _type.isString)(value)) {
return
}
return _date_serialization.default.getDateSerializationFormat(value)
}
_convertToDate(value) {
return _date_serialization.default.deserializeDate(value)
}
_dateValue(value, event) {
if (event) {
if ("keydown" === event.type) {
const cellElement = this._view._getContouredCell().get(0);
event.target = cellElement
}
this._saveValueChangeEvent(event)
}
this._dateOption("value", value)
}
_dateOption(optionName, optionValue) {
const isArray = "value" === optionName && !this._isSingleMode();
const value = this.option("value");
if (1 === arguments.length) {
return isArray ? (value ?? []).map((value => this._convertToDate(value))) : this._convertToDate(this.option(optionName))
}
const serializationFormat = this._getSerializationFormat(optionName);
const serializedValue = isArray ? (null === optionValue || void 0 === optionValue ? void 0 : optionValue.map((value => _date_serialization.default.serializeDate(value, serializationFormat)))) || [] : _date_serialization.default.serializeDate(optionValue, serializationFormat);
this.option(optionName, serializedValue)
}
_isSingleMode() {
const {
selectionMode: selectionMode
} = this.option();
return "single" === selectionMode
}
_shiftDate(zoomLevel, date, offset, reverse) {
switch (zoomLevel) {
case ZOOM_LEVEL.MONTH:
date.setDate(date.getDate() + offset * reverse);
break;
case ZOOM_LEVEL.YEAR:
date.setMonth(date.getMonth() + offset * reverse);
break;
case ZOOM_LEVEL.DECADE:
date.setFullYear(date.getFullYear() + offset * reverse);
break;
case ZOOM_LEVEL.CENTURY:
date.setFullYear(date.getFullYear() + 10 * offset * reverse)
}
}
_moveCurrentDateByOffset(offset) {
const baseDate = this.option("currentDate");
let currentDate = new Date(baseDate);
const zoomLevel = this.option("zoomLevel");
this._shiftDate(zoomLevel, currentDate, offset, 1);
const maxDate = this._getMaxDate();
const minDate = this._getMinDate();
let isDateForwardInNeighborView = this._areDatesInNeighborView(zoomLevel, currentDate, baseDate);
let isDateForwardInRange = (0, _math.inRange)(currentDate, minDate, maxDate) && isDateForwardInNeighborView;
const dateForward = new Date(currentDate);
while (isDateForwardInRange) {
if (!this._view.isDateDisabled(dateForward)) {
currentDate = dateForward;
break
}
this._shiftDate(zoomLevel, dateForward, offset, 1);
isDateForwardInNeighborView = this._areDatesInNeighborView(zoomLevel, dateForward, baseDate);
isDateForwardInRange = (0, _math.inRange)(dateForward, minDate, maxDate) && isDateForwardInNeighborView
}
if (this._view.isDateDisabled(baseDate) || this._view.isDateDisabled(currentDate)) {
const direction = offset > 0 ? 1 : -1;
const isViewDisabled = 1 === direction ? this._isNextViewDisabled() : this._isPrevViewDisabled();
if (!isViewDisabled) {
this._waitRenderView(direction)
} else {
this._moveToClosestAvailableDate(currentDate)
}
} else {
this._skipNavigate = true;
this.option("currentDate", currentDate)
}
}
_isNextViewDisabled() {
return this._navigator._nextButton.option("disabled")
}
_isPrevViewDisabled() {
return this._navigator._prevButton.option("disabled")
}
_areDatesInSameView(zoomLevel, date1, date2) {
switch (zoomLevel) {
case ZOOM_LEVEL.MONTH:
return date1.getMonth() === date2.getMonth();
case ZOOM_LEVEL.YEAR:
return date1.getYear() === date2.getYear();
case ZOOM_LEVEL.DECADE:
return parseInt(date1.getYear() / 10) === parseInt(date2.getYear() / 10);
case ZOOM_LEVEL.CENTURY:
return parseInt(date1.getYear() / 100) === parseInt(date2.getYear() / 100)
}
}
_areDatesInNeighborView(zoomLevel, date1, date2) {
switch (zoomLevel) {
case ZOOM_LEVEL.MONTH:
return ((a, b) => {
const abs = Math.abs(a - b);
return Math.min(abs, 12 - abs)
})(date1.getMonth(), date2.getMonth()) <= 1;
case ZOOM_LEVEL.YEAR:
return Math.abs(date1.getYear() - date2.getYear()) <= 1;
case ZOOM_LEVEL.DECADE:
return Math.abs(date1.getYear() - date2.getYear()) <= 10;
case ZOOM_LEVEL.CENTURY:
return Math.abs(date1.getYear() - date2.getYear()) <= 100
}
}
_moveToClosestAvailableDate() {
let baseDate = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : this.option("currentDate");
let currentDate = new Date(baseDate);
const zoomLevel = this.option("zoomLevel");
const isCurrentDateAvailable = !this._isDateNotAvailable(currentDate);
let isDateForwardAvailable = isCurrentDateAvailable;
let isDateBackwardAvailable = isCurrentDateAvailable;
let isDateForwardInStartView;
let isDateBackwardInStartView;
const dateForward = new Date(currentDate);
const dateBackward = new Date(currentDate);
do {
if (isDateForwardAvailable) {
currentDate = dateForward;
break
}
if (isDateBackwardAvailable) {
currentDate = dateBackward;
break
}
this._shiftDate(zoomLevel, dateForward, 1, 1);
this._shiftDate(zoomLevel, dateBackward, 1, -1);
isDateForwardInStartView = this._areDatesInSameView(zoomLevel, dateForward, baseDate);
isDateBackwardInStartView = this._areDatesInSameView(zoomLevel, dateBackward, baseDate);
isDateForwardAvailable = isDateForwardInStartView && !this._isDateNotAvailable(dateForward);
isDateBackwardAvailable = isDateBackwardInStartView && !this._isDateNotAvailable(dateBackward)
} while (isDateForwardInStartView || isDateBackwardInStartView);
this.option("currentDate", currentDate)
}
_isDateNotAvailable(date) {
const maxDate = this._getMaxDate();
const minDate = this._getMinDate();
return !(0, _math.inRange)(date, minDate, maxDate) || this._view.isDateDisabled(date)
}
_init() {
super._init();
this._activeStateUnit = ".dx-calendar-cell";
this._initSelectionStrategy();
this._correctZoomLevel();
this._initCurrentDate();
this._initActions()
}
_initSelectionStrategy() {
const strategyName = this._getSelectionStrategyName();
const strategy = SELECTION_STRATEGIES[strategyName];
if (!this._selectionStrategy || this._selectionStrategy.NAME !== strategyName) {
this._selectionStrategy = new strategy(this)
}
}
_refreshSelectionStrategy() {
this._initSelectionStrategy();
this._selectionStrategy.restoreValue();
this._refresh()
}
_getSelectionStrategyName() {
const {
selectionMode: selectionMode
} = this.option();
switch (selectionMode) {
case "multiple":
return "MultipleSelection";
case "range":
return "RangeSelection";
default:
return "SingleSelection"
}
}
_correctZoomLevel() {
const {
minZoomLevel: minZoomLevel,
maxZoomLevel: maxZoomLevel,
zoomLevel: zoomLevel
} = this.option();
if (LEVEL_COMPARE_MAP[maxZoomLevel] < LEVEL_COMPARE_MAP[minZoomLevel]) {
return
}
if (LEVEL_COMPARE_MAP[zoomLevel] > LEVEL_COMPARE_MAP[maxZoomLevel]) {
this.option("zoomLevel", maxZoomLevel)
} else if (LEVEL_COMPARE_MAP[zoomLevel] < LEVEL_COMPARE_MAP[minZoomLevel]) {
this.option("zoomLevel", minZoomLevel)
}
}
_initCurrentDate() {
const currentDate = this._getNormalizedDate(this._selectionStrategy.getDefaultCurrentDate()) ?? this._getNormalizedDate(this.option("currentDate"));
this.option("currentDate", currentDate)
}
_getNormalizedDate(date) {
date = _date2.default.normalizeDate(date, this._getMinDate(), this._getMaxDate());
return (0, _type.isDefined)(date) ? this._getDate(date) : date
}
_initActions() {
this._cellClickAction = this._createActionByOption("onCellClick");
this._onContouredChanged = this._createActionByOption("onContouredChanged")
}
_initTemplates() {
this._templateManager.addDefaultTemplates({
cell: new _function_template.FunctionTemplate((options => {
const data = options.model;
(0, _renderer.default)(options.container).append((0, _renderer.default)("<span>").text(data && data.text || String(data)))
}))
});
super._initTemplates()
}
_updateCurrentDate(date) {
if (_animation.fx.isAnimating(this._$viewsWrapper)) {
_animation.fx.stop(this._$viewsWrapper, true)
}
const min = this._getMinDate();
const max = this._getMaxDate();
if (min > max) {
this.option("currentDate", new Date);
return
}
const normalizedDate = this._getNormalizedDate(date);
if (date.getTime() !== normalizedDate.getTime()) {
this.option("currentDate", new Date(normalizedDate));
return
}
let offset = this._getViewsOffset(this._view.option("date"), normalizedDate);
if (0 !== offset && !this._isMaxZoomLevel() && this._isOtherViewCellClicked) {
offset = 0
}
if (this._view && 0 !== offset && !this._suppressNavigation) {
if (this._additionalView) {
if (offset > 2 || offset < -1) {
this._refreshViews();
this._setViewContoured(normalizedDate);
this._updateAriaId(normalizedDate);
this._renderNavigator()
} else if (1 === offset && this._skipNavigate) {
this._setViewContoured(normalizedDate);
this._updateAriaId(normalizedDate)
} else {
this._navigate(offset, normalizedDate)
}
} else {
this._navigate(offset, normalizedDate)
}
} else {
this._renderNavigator();
this._setViewContoured(normalizedDate);
this._updateAriaId(normalizedDate)
}
this._skipNavigate = false
}
_isAdditionalViewDate(date) {
if (!this._additionalView) {
return false
}
return date >= this._additionalView._getFirstAvailableDate()
}
_getActiveView(date) {
return this._isAdditionalViewDate(date) ? this._additionalView : this._view
}
_setViewContoured(date) {
if (this.option("skipFocusCheck") || (0, _renderer.default)(this._$viewsWrapper).is(":focus")) {
var _this$_additionalView;
this._view.option("contouredDate", null);
null === (_this$_additionalView = this._additionalView) || void 0 === _this$_additionalView || _this$_additionalView.option("contouredDate", null);
const view = this._isAdditionalViewDate(date) ? this._additionalView : this._view;
view.option("contouredDate", date)
}
}
_getMinDate() {
const _rangeMin = this.option("_rangeMin");
if (_rangeMin) {
return _rangeMin
}
if (this.min) {
return this.min
}
this.min = this._dateOption("min") || new Date(1e3, 0);
return this.min
}
_getMaxDate() {
const _rangeMax = this.option("_rangeMax");
if (_rangeMax) {
return _rangeMax
}
if (this.max) {
return this.max
}
this.max = this._dateOption("max") || new Date(3e3, 0);
return this.max
}
_getViewsOffset(startDate, endDate) {
const {
zoomLevel: zoomLevel
} = this.option();
if (zoomLevel === ZOOM_LEVEL.MONTH) {
return this._getMonthsOffset(startDate, endDate)
}
let zoomCorrection;
switch (zoomLevel) {
case ZOOM_LEVEL.CENTURY:
zoomCorrection = 100;
break;
case ZOOM_LEVEL.DECADE:
zoomCorrection = 10;
break;
default:
zoomCorrection = 1
}
return parseInt(endDate.getFullYear() / zoomCorrection) - parseInt(startDate.getFullYear() / zoomCorrection)
}
_getMonthsOffset(startDate, endDate) {
const yearOffset = endDate.getFullYear() - startDate.getFullYear();
const monthOffset = endDate.getMonth() - startDate.getMonth();
return 12 * yearOffset + monthOffset
}
_waitRenderView(offset) {
if (this._alreadyViewRender) {
return
}
this._alreadyViewRender = true;
const date = this._getDateByOffset(offset * this._getRtlCorrection());
this._moveToClosestAvailableDate(date);
this._waitRenderViewTimeout = setTimeout((() => {
this._alreadyViewRender = false
}))
}
_getRtlCorrection() {
return this.option("rtlEnabled") ? -1 : 1
}
_getDateByOffset(offset, date) {
date = this._getDate(date ?? this.option("currentDate"));
const currentDay = date.getDate();
const difference = _date2.default.getDifferenceInMonth(this.option("zoomLevel")) * offset;
date.setDate(1);
date.setMonth(date.getMonth() + difference);
const lastDay = _date2.default.getLastMonthDate(date).getDate();
date.setDate(currentDay > lastDay ? lastDay : currentDay);
return date
}
_focusTarget() {
return this._$viewsWrapper
}
_focusEventTarget() {
return this.$element()
}
_initMarkup() {
this._renderSubmitElement();
const $element = this.$element();
$element.addClass("dx-calendar");
const {
selectionMode: selectionMode
} = this.option();
$element.toggleClass("dx-calendar-range", "range" === selectionMode);
this._renderBody();
$element.append(this.$body);
this._renderViews();
this._renderNavigator();
super._initMarkup();
this._renderEvents();
$element.prepend(this._navigator.$element());
this._renderSwipeable();
this._renderFooter();
this._selectionStrategy.updateAriaSelected();
this._updateAriaId();
this._updateNavigatorLabels();
this.setAria("role", "application");
this._updateAriaLabelAndRole();
this._moveToClosestAvailableDate()
}
_render() {
super._render();
this._setViewContoured(this.option("currentDate"))
}
_renderBody() {
if (!this._$viewsWrapper) {
this.$body = (0, _renderer.default)("<div>").addClass("dx-calendar-body");
this._$viewsWrapper = (0, _renderer.default)("<div>").addClass("dx-calendar-views-wrapper");
this.$body.append(this._$viewsWrapper)
}
}
_updateAriaLabelAndRole() {
const readOnly = this.option("readOnly");
const $element = this.$element();
const aria = {
role: readOnly ? "group" : void 0,
label: readOnly ? _message.default.format("dxCalendar-readOnlyLabel") : void 0
};
this.setAria(aria, $element)
}
_setAriaReadonly() {}
_getKeyboardListeners() {
return super._getKeyboardListeners().concat([this._view])
}
_renderViews() {
const {
zoomLevel: zoomLevel
} = this.option();
this.$element().addClass(`dx-calendar-view-${zoomLevel}`);
const {
currentDate: currentDate,
viewsCount: viewsCount
} = this.option();
this.$element().toggleClass("dx-calendar-multiview", viewsCount > 1);
this._view = this._renderSpecificView(currentDate);
if ((0, _window.hasWindow)()) {
const beforeDate = this._getDateByOffset(-1, currentDate);
this._beforeView = this._isViewAvailable(beforeDate) ? this._renderSpecificView(beforeDate) : null;
const afterDate = this._getDateByOffset(viewsCount, currentDate);
afterDate.setDate(1);
this._afterView = this._isViewAvailable(afterDate) ? this._renderSpecificView(afterDate) : null
}
if (viewsCount > 1) {
this._additionalView = this._renderSpecificView(this._getDateByOffset(1, currentDate))
}
this._translateViews()
}
_renderSpecificView(date) {
const {
zoomLevel: zoomLevel
} = this.option();
const specificView = _m_calendar2.default[zoomLevel];
const $view = (0, _renderer.default)("<div>").appendTo(this._$viewsWrapper);
const config = this._viewConfig(date);
const view = this._createComponent($view, specificView, config);
return view
}
_viewConfig(date) {
let disabledDates = this.option("disabledDates");
disabledDates = (0, _type.isFunction)(disabledDates) ? this._injectComponent(disabledDates.bind(this)) : disabledDates;
return _extends({}, this._selectionStrategy.getViewOptions(), {
date: date,
min: this._getMinDate(),
max: this._getMaxDate(),
firstDayOfWeek: this.option("firstDayOfWeek") ?? _date.default.firstDayOfWeekIndex(),
showWeekNumbers: this.option("showWeekNumbers"),
selectWeekOnClick: this.option("selectWeekOnClick"),
weekNumberRule: this.option("weekNumberRule"),
zoomLevel: this.option("zoomLevel"),
tabIndex: void 0,
focusStateEnabled: this.option("focusStateEnabled"),
hoverStateEnabled: this.option("hoverStateEnabled"),
disabledDates: disabledDates,
onCellClick: this._cellClickHandler.bind(this),
cellTemplate: this._getTemplateByOption("cellTemplate"),
allowValueSelection: this._isMaxZoomLevel(),
_todayDate: this.option("_todayDate")
})
}
_renderEvents() {
_events_engine.default.off(this._$viewsWrapper, CALENDAR_DXHOVEREND_EVENT_NAME);
const {
selectionMode: selectionMode
} = this.option();
if ("range" === selectionMode) {
_events_engine.default.on(this._$viewsWrapper, CALENDAR_DXHOVEREND_EVENT_NAME, null, (() => {
this._updateViewsOption("hoveredRange", [])
}))
}
}
_injectComponent(func) {
const that = this;
return function(params) {
(0, _extend.extend)(params, {
component: that
});
return func(params)
}
}
_isViewAvailable(date) {
const zoomLevel = this.option("zoomLevel");
const min = _date2.default.getViewMinBoundaryDate(zoomLevel, this._getMinDate());
const max = _date2.default.getViewMaxBoundaryDate(zoomLevel, this._getMaxDate());
return _date2.default.dateInRange(date, min, max)
}
_translateViews() {
const {
viewsCount: viewsCount
} = this.option();
(0, _translator.move)(this._view.$element(), {
left: 0,
top: 0
});
this._moveViewElement(this._beforeView, -1);
this._moveViewElement(this._afterView, viewsCount);
this._moveViewElement(this._additionalView, 1)
}
_moveViewElement(view, coefficient) {
view && (0, _translator.move)(view.$element(), {
left: this._getViewPosition(coefficient),
top: 0
})
}
_getViewPosition(coefficient) {
const rtlCorrection = this.option("rtlEnabled") ? -1 : 1;
return 100 * coefficient * rtlCorrection + "%"
}
_cellClickHandler(e) {
const zoomLevel = this.option("zoomLevel");
const nextView = _date2.default.getViewDown(zoomLevel);
const isMaxZoomLevel = this._isMaxZoomLevel();
if (nextView && !isMaxZoomLevel) {
this._navigateDown(e.event.currentTarget)
} else {
var _this$_cellClickActio;
const newValue = this._updateTimeComponent(e.value);
this._selectionStrategy.selectValue(newValue, e.event);
null === (_this$_cellClickActio = this._cellClickAction) || void 0 === _this$_cellClickActio || _this$_cellClickActio.call(this, e)
}
}
_updateTimeComponent(date) {
const result = new Date(date);
const currentValue = this._dateOption("value");
if (currentValue && this._isSingleMode()) {
result.setHours(currentValue.getHours());
result.setMinutes(currentValue.getMinutes());
result.setSeconds(currentValue.getSeconds());
result.setMilliseconds(currentValue.getMilliseconds())
}
return result
}
_isMaxZoomLevel() {
return this.option("zoomLevel") === this.option("maxZoomLevel")
}
_navigateDown(cell) {
const zoomLevel = this.option("zoomLevel");
if (this._isMaxZoomLevel()) {
return
}
const nextView = _date2.default.getViewDown(zoomLevel);
if (!nextView) {
return
}
let newCurrentDate = this._view.option("contouredDate") || this._view.option("date");
if (cell) {
newCurrentDate = (0, _renderer.default)(cell).data("dxDateValueKey")
}
this._isOtherViewCellClicked = true;
this.option("currentDate", newCurrentDate);
this.option("zoomLevel", nextView);
this._isOtherViewCellClicked = false;
this._renderNavigator();
this._animateShowView();
this._moveToClosestAvailableDate();
this._setViewContoured(this._getNormalizedDate(this.option("currentDate")))
}
_renderNavigator() {
if (!this._navigator) {
this._navigator = new _m_calendar.default((0, _renderer.default)("<div>"), this._navigatorConfig())
}
this._navigator.option("text", this._getViewsCaption(this._view, this._additionalView));
this._updateButtonsVisibility()
}
_navigatorConfig() {
const {
focusStateEnabled: focusStateEnabled,
rtlEnabled: rtlEnabled
} = this.option();
return {
text: this._getViewsCaption(this._view, this._additionalView),
onClick: this._navigatorClickHandler.bind(this),
onCaptionClick: this._navigateUp.bind(this),
focusStateEnabled: focusStateEnabled,
rtlEnabled: rtlEnabled,
tabIndex: void 0
}
}
_navigatorClickHandler(e) {
const {
currentDate: currentDate,
viewsCount: viewsCount
} = this.option();
let offset = e.direction;
if (viewsCount > 1) {
const additionalViewActive = this._isAdditionalViewDate(currentDate);
const shouldDoubleOffset = additionalViewActive && offset < 0 || !additionalViewActive && offset > 0;
if (shouldDoubleOffset) {
offset *= 2
}
}
const newCurrentDate = this._getDateByOffset(offset, currentDate);
this._moveToClosestAvailableDate(newCurrentDate)
}
_navigateUp() {
const zoomLevel = this.option("zoomLevel");
const nextView = _date2.default.getViewUp(zoomLevel);
if (!nextView || this._isMinZoomLevel(zoomLevel)) {
return
}
this.option("zoomLevel", nextView);
this._renderNavigator();
this._animateShowView();
this._moveToClosestAvailableDate();
this._setViewContoured(this._getNormalizedDate(this.option("currentDate")))
}
_isMinZoomLevel(zoomLevel) {
const min = this._getMinDate();
const max = this._getMaxDate();
return _date2.default.sameView(zoomLevel, min, max) || this.option("minZoomLevel") === zoomLevel
}
_updateButtonsVisibility() {
this._navigator.toggleButton("next", !(0, _type.isDefined)(this._afterView));
this._navigator.toggleButton("prev", !(0, _type.isDefined)(this._beforeView))
}
_renderSwipeable() {
if (!this._swipeable) {
this._swipeable = this._createComponent(this.$element(), _swipeable.default, {
onStart: this._swipeStartHandler.bind(this),
onUpdated: this._swipeUpdateHandler.bind(this),
onEnd: this._swipeEndHandler.bind(this),
itemSizeFunc: this._viewWidth.bind(this)
})
}
}
_swipeStartHandler(e) {
_animation.fx.stop(this._$viewsWrapper, true);
const {
viewsCount: viewsCount
} = this.option();
this._toggleGestureCoverCursor("grabbing");
e.event.maxLeftOffset = this._getRequiredView("next") ? 1 / viewsCount : 0;
e.event.maxRightOffset = this._getRequiredView("prev") ? 1 / viewsCount : 0
}
_toggleGestureCoverCursor(cursor) {
(0, _renderer.default)(".dx-gesture-cover").css("cursor", cursor)
}
_getRequiredView(name) {
let view;
const isRtl = this.option("rtlEnabled");
if ("next" === name) {
view = isRtl ? this._beforeView : this._afterView
} else if ("prev" === name) {
view = isRtl ? this._afterView : this._beforeView
}
return view
}
_swipeUpdateHandler(e) {
const {
offset: offset
} = e.event;
(0, _translator.move)(this._$viewsWrapper, {
left: offset * this._viewWidth(),
top: 0
});
this._updateNavigatorCaption(offset)
}
_swipeEndHandler(e) {
this._toggleGestureCoverCursor("auto");
const {
currentDate: currentDate,
rtlEnabled: rtlEnabled
} = this.option();
const {
targetOffset: targetOffset
} = e.event;
const moveOffset = !targetOffset ? 0 : targetOffset / Math.abs(targetOffset);
const isAdditionalViewActive = this._isAdditionalViewDate(currentDate);
const shouldDoubleOffset = isAdditionalViewActive && (rtlEnabled ? -1 === moveOffset : 1 === moveOffset);
if (0 === moveOffset) {
this._animateWrapper(0, 250);
return
}
const offset = -moveOffset * this._getRtlCorrection() * (shouldDoubleOffset ? 2 : 1);
let date = this._getDateByOffset(offset);
if (this._isDateInInvalidRange(date)) {
if (moveOffset >= 0) {
date = new Date(this._getMinDate())
} else {
date = new Date(this._getMaxDate())
}
}
this.option("currentDate", date)
}
_viewWidth() {
if (!this._viewWidthValue) {
const {
viewsCount: viewsCount
} = this.option();
this._viewWidthValue = (0, _size.getWidth)(this.$element()) / viewsCount
}
return this._viewWidthValue
}
_updateNavigatorCaption(offset) {
offset *= this._getRtlCorrection();
const {
viewsCount: viewsCount
} = this.option();
const isMultiView = viewsCount > 1;
let view;
let additionalView;
if (offset > .5 && this._beforeView) {
view = this._beforeView;
additionalView = isMultiView && this._view
} else if (offset < -.5 && this._afterView) {
view = isMultiView ? this._additionalView : this._afterView;
additionalView = isMultiView ? this._afterView : null
} else {
view = this._view;
additionalView = isMultiView ? this._additionalView : null
}
this._navigator.option("text", this._getViewsCaption(view, additionalView))
}
_getViewsCaption(view, additionalView) {
let caption = view.getNavigatorCaption();
const {
viewsCount: viewsCount
} = this.option();
if (viewsCount > 1 && additionalView) {
const additionalViewCaption = additionalView.getNavigatorCaption();
caption = `${caption} - ${additionalViewCaption}`
}
return caption
}
_isDateInInvalidRange(date) {
if (this._view.isBoundary(date)) {
return
}
const min = this._getMinDate();
const max = this._getMaxDate();
const normalizedDate = _date2.default.normalizeDate(date, min, max);
return normalizedDate === min || normalizedDate === max
}
_renderFooter() {
const showTodayButton = this.option("showTodayButton");
if (showTodayButton) {
const $todayButton = this._createComponent((0, _renderer.default)("<div>"), _button.default, {
focusStateEnabled: this.option("focusStateEnabled"),
text: _message.default.format("dxCalendar-todayButtonText"),
onClick: args => {
this._toTodayView(args)
},
type: (0, _themes.isFluent)() ? "normal" : "default",
stylingMode: (0, _themes.isFluent)() ? "outlined" : "text",
integrationOptions: {}
}).$element().addClass("dx-calendar-today-button");
this._$footer = (0, _renderer.default)("<div>").addClass("dx-calendar-footer").append($todayButton);
this.$element().append(this._$footer)
}
this.$element().toggleClass("dx-calendar-with-footer", showTodayButton)
}
_renderSubmitElement() {
this._$submitElement = (0, _renderer.default)("<input>").attr("type", "hidden").appendTo(this.$element());
this._setSubmitValue(this.option("value"))
}
_setSubmitValue(value) {
const dateValue = this._convertToDate(value);
this._getSubmitElement().val(_date_serialization.default.serializeDate(dateValue, "yyyy-MM-dd"))
}
_getSubmitElement() {
return this._$submitElement
}
_animateShowView() {
_animation.fx.stop(this._view.$element(), true);
this._popAnimationView(this._view, .6, 1, 250);
const {
viewsCount: viewsCount
} = this.option();
if (viewsCount > 1) {
_animation.fx.stop(this._additionalView.$element(), true);
this._popAnimationView(this._additionalView, .6, 1, 250)
}
}
_popAnimationView(view, from, to, duration) {
return _animation.fx.animate(view.$element(), {
type: "pop",
from: {
scale: from,
opacity: from
},
to: {
scale: to,
opacity: to
},
duration: duration
})
}
_navigate(offset, value) {
if (0 !== offset && 1 !== Math.abs(offset) && this._isViewAvailable(value)) {
const newView = this._renderSpecificView(value);
if (offset > 0) {
this._afterView && this._afterView.$element().remove();
this._afterView = newView
} else {
this._beforeView && this._beforeView.$element().remove();
this._beforeView = newView
}
this._translateViews()
}
const rtlCorrection = this._getRtlCorrection();
const offsetSign = offset > 0 ? 1 : offset < 0 ? -1 : 0;
const endPosition = -rtlCorrection * offsetSign * this._viewWidth();
const viewsWrapperPosition = this._$viewsWrapper.position().left;
if (viewsWrapperPosition !== endPosition) {
if (this._preventViewChangeAnimation) {
this._wrapperAnimationEndHandler(offset, value)
} else {
this._animateWrapper(endPosition, 250).done(this._wrapperAnimationEndHandler.bind(this, offset, value))
}
}
}
_animateWrapper(to, duration) {
return _animation.fx.animate(this._$viewsWrapper, {
type: "slide",
from: {
left: this._$viewsWrapper.position().left
},
to: {
left: to
},
duration: duration
})
}
_getDate(value) {
return new Date(value)
}
_toTodayView(args) {
const today = new Date;
if (this._isMaxZoomLevel()) {
this._selectionStrategy.selectValue(today, args.event);
return
}
this._preventViewChangeAnimation = true;
this.option("zoomLevel", this.option("maxZoomLevel"));
this._selectionStrategy.selectValue(today, args.event);
this._animateShowView();
this._preventViewChangeAnimation = false
}
_wrapperAnimationEndHandler(offset, newDate) {
this._rearrangeViews(offset);
this._translateViews();
this._resetLocation();
this._renderNavigator();
this._setViewContoured(newDate);
this._updateAriaId(newDate);
this._selectionStrategy.updateAriaSelected()
}
_rearrangeViews(offset) {
var _this$viewToRemoveKey;
if (0 === offset) {
return
}
const {
viewsCount: viewsCount
} = this.option();
let viewOffset;
let viewToCreateKey;
let viewToRemoveKey;
let viewBeforeCreateKey;
let viewAfterRemoveKey;
if (offset < 0) {
viewOffset = 1;
viewToCreateKey = "_beforeView";
viewToRemoveKey = "_afterView";
viewBeforeCreateKey = "_view";
viewAfterRemoveKey = 1 === viewsCount ? "_view" : "_additionalView"
} else {
viewOffset = -1;
viewToCreateKey = "_afterView";
viewToRemoveKey = "_beforeView";
viewBeforeCreateKey = 1 === viewsCount ? "_view" : "_additionalView";
viewAfterRemoveKey = "_view"
}
if (!this[viewToCreateKey]) {
return
}
const destinationDate = this[viewToCreateKey].option("date");
null === (_this$viewToRemoveKey = this[viewToRemoveKey]) || void 0 === _this$viewToRemoveKey || _this$viewToRemoveKey.$element().remove();
this[viewToRemoveKey] = this._renderSpecificView(this._getDateByOffset(viewOffset * viewsCount, destinationDate));
this[viewAfterRemoveKey].$element().remove();
if (1 === viewsCount) {
this[viewAfterRemoveKey] = this[viewToCreateKey]
} else {
this[viewAfterRemoveKey] = this[viewBeforeCreateKey];
this[viewBeforeCreateKey] = this[viewToCreateKey]
}
const dateByOffset = this._getDateByOffset(-viewOffset, destinationDate);
this[viewToCreateKey] = this._isViewAvailable(dateByOffset) ? this._renderSpecificView(dateByOffset) : null
}
_resetLocation() {
(0, _translator.move)(this._$viewsWrapper, {
left: 0,
top: 0
})
}
_clean() {
super._clean();
this._clearViewWidthCache();
delete this._$viewsWrapper;
delete this._navigator;
delete this._$footer
}
_clearViewWidthCache() {
delete this._viewWidthValue
}
_disposeViews() {
var _this$_beforeView, _this$_additionalView2, _this$_afterView;
this._view.$element().remove();
null === (_this$_beforeView = this._beforeView) || void 0 === _this$_beforeView || _this$_beforeView.$element().remove();
null === (_this$_additionalView2 = this._additionalView) || void 0 === _this$_additionalView2 || _this$_additionalView2.$element().remove();
null === (_this$_afterView = this._afterView) || void 0 === _this$_afterView || _this$_afterView.$element().remove();
delete this._view;
delete this._additionalView;
delete this._beforeView;
delete this._afterView;
delete this._skipNavigate
}
_dispose() {
clearTimeout(this._waitRenderViewTimeout);
super._dispose()
}
_refreshViews() {
this._resetActiveState();
this._disposeViews();
this._renderViews()
}
_visibilityChanged() {
this._translateViews()
}
_shouldSkipFocusEvent(event) {
const {
target: target,
relatedTarget: relatedTarget
} = event;
return (0, _renderer.default)(target).parents(".dx-calendar").length && (0, _renderer.default)(relatedTarget).parents(".dx-calendar").length
}
_focusInHandler(event) {
if ((0, _renderer.default)(event.target).is(this._$viewsWrapper)) {
this._setViewContoured(this.option("currentDate"))
}
if (this._shouldSkipFocusEvent(event)) {
return
}
super._focusInHandler.apply(this, arguments);
this._toggleFocusClass(true, this.$element())
}
_focusOutHandler(event) {
if ((0, _renderer.default)(event.target).is(this._$viewsWrapper)) {
var _this$_additionalView3;
this._view.option("contouredDate", null);
null === (_this$_additionalView3 = this._additionalView) || void 0 === _this$_additionalView3 || _this$_additionalView3.option("contouredDate", null)
}
if (this._shouldSkipFocusEvent(event)) {
return
}
super._focusOutHandler.apply(this, arguments);
this._toggleFocusClass(false, this.$element())
}
_updateViewsOption(optionName, newValue) {
var _this$_additionalView4, _this$_beforeView2, _this$_afterView2;
this._view.option(optionName, newValue);
null === (_this$_additionalView4 = this._additionalView) || void 0 === _this$_additionalView4 || _this$_additionalView4.option(optionName, newValue);
null === (_this$_beforeView2 = this._beforeView) || void 0 === _this$_beforeView2 || _this$_beforeView2.option(optionName, newValue);
null === (_this$_afterView2 = this._afterView) || void 0 === _this$_afterView2 || _this$_afterView2.option(optionName, newValue)
}
_setViewsMinOption(min) {
this._restoreViewsMinMaxOptions();
this.option("_rangeMin", this._convertToDate(min));
this._updateViewsOption("min", this._getMinDate())
}
_setViewsMaxOption(max) {
this._restoreViewsMinMaxOptions();
this.option("_rangeMax", this._convertToDate(max));
this._updateViewsOption("max", this._getMaxDate())
}
_restoreViewsMinMaxOptions() {
this._resetActiveState();
this.option({
_rangeMin: null,
_rangeMax: null
});
this._updateViewsOption("min", this._getM