UNPKG

devextreme

Version:

JavaScript/TypeScript Component Suite for Responsive Web Development

1,227 lines (1,225 loc) • 59.3 kB
/** * DevExtreme (cjs/__internal/ui/calendar/calendar.js) * Version: 25.2.5 * Build date: Fri Feb 20 2026 * * Copyright (c) 2012 - 2026 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 _math = require("../../../core/utils/math"); var _size = require("../../../core/utils/size"); var _type = require("../../../core/utils/type"); var _window = require("../../../core/utils/window"); var _themes = require("../../../ui/themes"); var _wrapper = _interopRequireDefault(require("../../ui/button/wrapper")); var _editor = _interopRequireDefault(require("../../ui/editor/editor")); var _calendarMultipleSelection = _interopRequireDefault(require("./calendar.multiple.selection.strategy")); var _calendar = _interopRequireDefault(require("./calendar.navigator")); var _calendarRangeSelection = _interopRequireDefault(require("./calendar.range.selection.strategy")); var _calendarSingleSelection = _interopRequireDefault(require("./calendar.single.selection.strategy")); var _calendar2 = _interopRequireDefault(require("./calendar.views")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e } } 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: _calendarSingleSelection.default, MultipleSelection: _calendarMultipleSelection.default, RangeSelection: _calendarRangeSelection.default }; class Calendar extends _editor.default { _activeStateUnit() { return ".dx-calendar-cell" } _getDefaultOptions() { return Object.assign({}, 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, todayButtonText: _message.default.format("dxCalendar-todayButtonText"), 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 Object.assign({}, 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().get(0))) { 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().get(0))) { 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._getDateOption("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._getDateOption("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) { const { currentDate: currentDate = new Date } = this.option(); if (!this._isMaxZoomLevel()) { this._navigateDown() } else if (!this._view.isDateDisabled(currentDate)) { const value = this._updateTimeComponent(currentDate); this._selectionStrategy.selectValue(value, e) } } _getSerializationFormat() { let optionName = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : "value"; const { [optionName]: value } = this.option(); const { dateSerializationFormat: dateSerializationFormat } = this.option(); if (dateSerializationFormat) { return dateSerializationFormat } if ((0, _type.isNumeric)(value)) { return "number" } if (!(0, _type.isString)(value) || "" === 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._setDateOption("value", value) } _isArrayValue(optionName, value) { return "value" === optionName && !this._isSingleMode() } _setDateOption(optionName, optionValue) { const serializationFormat = this._getSerializationFormat(optionName); const serializedValue = this._isArrayValue(optionName, optionValue) ? optionValue.map((value => _date_serialization.default.serializeDate(value, serializationFormat))) : _date_serialization.default.serializeDate(optionValue, serializationFormat); this.option(optionName, serializedValue) } _getDateOption(optionName) { let { [optionName]: optionValue } = this.option(); if (!this._isArrayValue(optionName, optionValue)) { if ("" === optionValue) { optionValue = null } return this._convertToDate(optionValue) } const valueArray = optionValue ?? []; return valueArray.map((item => this._convertToDate(item))) } _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 { currentDate: baseDate = new Date, zoomLevel: zoomLevel = ZOOM_LEVEL.MONTH } = this.option(); let currentDate = new Date(baseDate); 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() { const { disabled: disabled } = this._navigator._nextButton.option(); return true === disabled } _isPrevViewDisabled() { const { disabled: disabled } = this._navigator._prevButton.option(); return true === disabled } _areDatesInSameView(zoomLevel, date1, date2) { switch (zoomLevel) { case ZOOM_LEVEL.YEAR: return date1.getFullYear() === date2.getFullYear(); case ZOOM_LEVEL.DECADE: return Math.floor(date1.getFullYear() / 10) === Math.floor(date2.getFullYear() / 10); case ZOOM_LEVEL.CENTURY: return Math.floor(date1.getFullYear() / 100) === Math.floor(date2.getFullYear() / 100); case ZOOM_LEVEL.MONTH: default: return date1.getMonth() === date2.getMonth() } } _areDatesInNeighborView(zoomLevel, date1, date2) { switch (zoomLevel) { case ZOOM_LEVEL.YEAR: return Math.abs(date1.getFullYear() - date2.getFullYear()) <= 1; case ZOOM_LEVEL.DECADE: return Math.abs(date1.getFullYear() - date2.getFullYear()) <= 10; case ZOOM_LEVEL.CENTURY: return Math.abs(date1.getFullYear() - date2.getFullYear()) <= 100; case ZOOM_LEVEL.MONTH: default: return ((a, b) => { const abs = Math.abs(a - b); return Math.min(abs, 12 - abs) })(date1.getMonth(), date2.getMonth()) <= 1 } } _moveToClosestAvailableDate(baseDate) { const { zoomLevel: zoomLevel = ZOOM_LEVEL.MONTH, currentDate: oldCurrentDate = new Date } = this.option(); let currentDate = new Date(baseDate ?? oldCurrentDate); const isCurrentDateAvailable = !this._isDateNotAvailable(currentDate); let isDateForwardAvailable = isCurrentDateAvailable; let isDateBackwardAvailable = isCurrentDateAvailable; let isDateForwardInStartView = true; let isDateBackwardInStartView = true; 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 ?? oldCurrentDate); isDateBackwardInStartView = this._areDatesInSameView(zoomLevel, dateBackward, baseDate ?? oldCurrentDate); 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._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 = ZOOM_LEVEL.CENTURY, maxZoomLevel: maxZoomLevel = ZOOM_LEVEL.MONTH, zoomLevel: zoomLevel = ZOOM_LEVEL.MONTH } = 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); return } if (LEVEL_COMPARE_MAP[zoomLevel] < LEVEL_COMPARE_MAP[minZoomLevel]) { this.option("zoomLevel", minZoomLevel) } } _initCurrentDate() { const { currentDate: currentDate = new Date } = this.option(); const defaultCurrentDate = this._selectionStrategy.getDefaultCurrentDate(); const date = (defaultCurrentDate ? this._getNormalizedDate(defaultCurrentDate) : null) ?? this._getNormalizedDate(currentDate); this.option("currentDate", date) } _getNormalizedDate(date) { const normalizedDate = _date2.default.normalizeDate(date, this._getMinDate(), this._getMaxDate()); return (0, _type.isDefined)(normalizedDate) ? this._getDate(normalizedDate) : 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((null === data || void 0 === data ? void 0 : data.text) || String(data))) })) }); super._initTemplates() } _updateCurrentDate(date) { if (_animation.fx.isAnimating(this._$viewsWrapper.get(0))) { _animation.fx.stop(this._$viewsWrapper.get(0), 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 } const { date: viewDate } = this._view.option(); let offset = this._getViewsOffset(viewDate, 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() { let date = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : new 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: rangeMin } = this.option(); if (rangeMin) { return rangeMin } if (this.min) { return this.min } this.min = this._getDateOption("min") ?? new Date(1e3, 0); return this.min } _getMaxDate() { const { rangeMax: rangeMax } = this.option(); if (rangeMax) { return rangeMax } if (this.max) { return this.max } this.max = this._getDateOption("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 = 1; switch (zoomLevel) { case ZOOM_LEVEL.CENTURY: zoomCorrection = 100; break; case ZOOM_LEVEL.DECADE: zoomCorrection = 10; break; default: zoomCorrection = 1 } return Math.floor(endDate.getFullYear() / zoomCorrection) - Math.floor(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() { const { rtlEnabled: rtlEnabled } = this.option(); return rtlEnabled ? -1 : 1 } _getDateByOffset(offset, initialDate) { const { currentDate: currentDate = new Date } = this.option(); const date = this._getDate(initialDate ?? 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(); const { currentDate: currentDate = new Date } = this.option(); this._setViewContoured(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 = new Date, 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 = ZOOM_LEVEL.MONTH } = this.option(); const specificView = _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) { const { firstDayOfWeek: firstDayOfWeek = _date.default.firstDayOfWeekIndex(), showWeekNumbers: showWeekNumbers = false, selectWeekOnClick: selectWeekOnClick, weekNumberRule: weekNumberRule, zoomLevel: zoomLevel = ZOOM_LEVEL.MONTH, focusStateEnabled: focusStateEnabled, hoverStateEnabled: hoverStateEnabled, disabledDates: disabledDatesOption, _todayDate: todayDate } = this.option(); const disabledDates = (0, _type.isFunction)(disabledDatesOption) ? this._injectComponent(disabledDatesOption.bind(this)) : disabledDatesOption; return Object.assign({}, this._selectionStrategy.getViewOptions(), { date: date, min: this._getMinDate(), max: this._getMaxDate(), firstDayOfWeek: firstDayOfWeek, showWeekNumbers: showWeekNumbers, selectWeekOnClick: selectWeekOnClick, weekNumberRule: weekNumberRule, zoomLevel: zoomLevel, tabIndex: void 0, focusStateEnabled: focusStateEnabled, hoverStateEnabled: hoverStateEnabled, disabledDates: disabledDates, onCellClick: this._cellClickHandler.bind(this), cellTemplate: this._getTemplateByOption("cellTemplate"), allowValueSelection: this._isMaxZoomLevel(), _todayDate: 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) { return params => func(Object.assign({}, params, { component: this })) } _isViewAvailable(date) { const { zoomLevel: zoomLevel } = this.option(); 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) { if (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._getDateOption("value"); if (currentValue && !this._isArrayValue("value", currentValue)) { result.setHours(currentValue.getHours()); result.setMinutes(currentValue.getMinutes()); result.setSeconds(currentValue.getSeconds()); result.setMilliseconds(currentValue.getMilliseconds()) } return result } _isMaxZoomLevel() { const { zoomLevel: zoomLevel = ZOOM_LEVEL.MONTH, maxZoomLevel: maxZoomLevel } = this.option(); return zoomLevel === maxZoomLevel } _navigateDown(cell) { const { zoomLevel: zoomLevel, currentDate: currentDate = new Date } = this.option(); if (this._isMaxZoomLevel()) { return } const nextView = _date2.default.getViewDown(zoomLevel); if (!nextView) { return } const { contouredDate: contouredDate, date: date } = this._view.option(); let newCurrentDate = contouredDate ?? 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(currentDate)) } _renderNavigator() { if (!this._navigator) { this._navigator = this._createComponent((0, _renderer.default)("<div>"), _calendar.default, 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: zoomLevel = ZOOM_LEVEL.MONTH, currentDate: currentDate = new Date } = this.option(); 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(currentDate)) } _isMinZoomLevel(zoomLevel) { const min = this._getMinDate(); const max = this._getMaxDate(); const { minZoomLevel: minZoomLevel } = this.option(); return !!_date2.default.sameView(zoomLevel, min, max) || 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: e => { this._swipeStartHandler(e.event) }, onUpdated: e => { this._swipeUpdateHandler(e.event) }, onEnd: e => { this._swipeEndHandler(e.event) }, itemSizeFunc: this._viewWidth.bind(this) }) } } _swipeStartHandler(event) { _animation.fx.stop(this._$viewsWrapper.get(0), true); const { viewsCount: viewsCount } = this.option(); this._toggleGestureCoverCursor("grabbing"); event.maxLeftOffset = this._getRequiredView("next") ? 1 / viewsCount : 0; event.maxRightOffset = this._getRequiredView("prev") ? 1 / viewsCount : 0 } _toggleGestureCoverCursor(cursor) { (0, _renderer.default)(".dx-gesture-cover").css("cursor", cursor) } _getRequiredView(name) { const { rtlEnabled: rtlEnabled } = this.option(); if ("prev" === name) { return rtlEnabled ? this._afterView : this._beforeView } return rtlEnabled ? this._beforeView : this._afterView } _swipeUpdateHandler(event) { const { offset: offset } = event; (0, _translator.move)(this._$viewsWrapper, { left: offset * this._viewWidth(), top: 0 }); this._updateNavigatorCaption(offset) } _swipeEndHandler(event) { this._toggleGestureCoverCursor("auto"); const { currentDate: currentDate, rtlEnabled: rtlEnabled } = this.option(); const { targetOffset: targetOffset } = 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(initialOffset) { const offset = initialOffset * this._getRtlCorrection(); const { viewsCount: viewsCount } = this.option(); const isMultiView = viewsCount > 1; let view = null; let additionalView = null; if (offset > .5 && this._beforeView) { view = this._beforeView; if (isMultiView) { additionalView = 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 false } const min = this._getMinDate(); const max = this._getMaxDate(); const normalizedDate = _date2.default.normalizeDate(date, min, max); return normalizedDate === min || normalizedDate === max } _renderFooter() { const { showTodayButton: showTodayButton, todayButtonText: text } = this.option(); if (showTodayButton) { const $todayButton = this._createComponent((0, _renderer.default)("<div>"), _wrapper.default, { focusStateEnabled: this.option("focusStateEnabled"), text: text, onClick: args => { this._toTodayView(args) }, type: (0, _themes.isFluent)((0, _themes.current)()) ? "normal" : "default", stylingMode: (0, _themes.isFluent)((0, _themes.current)()) ? "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()); const { value: value } = this.option(); this._setSubmitValue(value) } _setSubmitValue(value) { if (this._isArrayValue("value", value)) { return } 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().get(0), true); this._popAnimationView(this._view, .6, 1, 250); const { viewsCount: viewsCount } = this.option(); if (viewsCount > 1) { _animation.fx.stop(this._additionalView.$element().get(0), true); this._popAnimationView(this._additionalView, .6, 1, 250) } } _popAnimationView(view, from, to, duration) { return _animation.fx.animate(view.$element().get(0), { 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) { var _this$_afterView; null === (_this$_afterView = this._afterView) || void 0 === _this$_afterView || _this$_afterView.$element().remove(); this._afterView = newView } else { var _this$_beforeView; null === (_this$_beforeView = this._beforeView) || void 0 === _this$_beforeView || _this$_beforeView.$element().remove(); this._beforeView = newView } this._translateViews() } const rtlCorrection = this._getRtlCorrection(); const offsetSign = (0, _math.sign)(offset); 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.get(0), { 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 = -1; let viewToCreateKey = "_afterView"; let viewToRemoveKey = "_beforeView"; let viewBeforeCreateKey = 1 === viewsCount ? "_view" : "_additionalView"; let viewAfterRemoveKey = "_view"; if (offset < 0) { viewOffset = 1; viewToCreateKey = "_beforeView"; viewToRemoveKey = "_afterView"; viewBeforeCreateKey = "_view"; viewAfterRemoveKey = 1 === viewsCount ? "_view" : "_additionalView" } 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$_beforeView2, _this$_additionalView2, _this$_afterView2; this._view.$element().remove(); null === (_this$_beforeView2 = this._beforeView) || void 0 === _this$_beforeView2 || _this$_beforeView2.$element().remove(); null === (_this$_additionalView2 = this._additionalView) || void 0 === _this$_additionalView2 || _this$_additionalView2.$element().remove(); null === (_this$_afterView2 = this._afterView) || void 0 === _this$_afterView2 || _this$_afterView2.$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()