devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
565 lines (562 loc) • 24.5 kB
JavaScript
/**
* DevExtreme (cjs/__internal/ui/calendar/m_calendar.base_view.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 _click = require("../../../common/core/events/click");
var _events_engine = _interopRequireDefault(require("../../../common/core/events/core/events_engine"));
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 _class = _interopRequireDefault(require("../../../core/class"));
var _dom_adapter = _interopRequireDefault(require("../../../core/dom_adapter"));
var _element = require("../../../core/element");
var _element_data = require("../../../core/element_data");
var _renderer = _interopRequireDefault(require("../../../core/renderer"));
var _common = require("../../../core/utils/common");
var _date2 = _interopRequireDefault(require("../../../core/utils/date"));
var _date_serialization = _interopRequireDefault(require("../../../core/utils/date_serialization"));
var _widget = _interopRequireDefault(require("../../core/widget/widget"));
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_OTHER_VIEW_CLASS = "dx-calendar-other-view";
const CALENDAR_CELL_CLASS = "dx-calendar-cell";
const CALENDAR_CELL_START_CLASS = "dx-calendar-cell-start";
const CALENDAR_CELL_END_CLASS = "dx-calendar-cell-end";
const CALENDAR_CELL_START_IN_ROW_CLASS = "dx-calendar-cell-start-in-row";
const CALENDAR_CELL_END_IN_ROW_CLASS = "dx-calendar-cell-end-in-row";
const CALENDAR_WEEK_NUMBER_CELL_CLASS = "dx-calendar-week-number-cell";
const CALENDAR_EMPTY_CELL_CLASS = "dx-calendar-empty-cell";
const CALENDAR_TODAY_CLASS = "dx-calendar-today";
const CALENDAR_SELECTED_DATE_CLASS = "dx-calendar-selected-date";
const CALENDAR_CELL_IN_RANGE_CLASS = "dx-calendar-cell-in-range";
const CALENDAR_CELL_RANGE_HOVER_CLASS = "dx-calendar-cell-range-hover";
const CALENDAR_CELL_RANGE_HOVER_START_CLASS = "dx-calendar-cell-range-hover-start";
const CALENDAR_CELL_RANGE_HOVER_END_CLASS = "dx-calendar-cell-range-hover-end";
const CALENDAR_RANGE_START_DATE_CLASS = "dx-calendar-range-start-date";
const CALENDAR_RANGE_END_DATE_CLASS = "dx-calendar-range-end-date";
const CALENDAR_CONTOURED_DATE_CLASS = "dx-calendar-contoured-date";
const NOT_WEEK_CELL_SELECTOR = "td:not(.dx-calendar-week-number-cell)";
const CALENDAR_DXCLICK_EVENT_NAME = (0, _index.addNamespace)(_click.name, "dxCalendar");
const CALENDAR_DXHOVERSTART_EVENT_NAME = (0, _index.addNamespace)(_hover.start, "dxCalendar");
const CALENDAR_DATE_VALUE_KEY = "dxDateValueKey";
const DAY_INTERVAL = 864e5;
const CURRENT_DATE_TEXT = {
month: _message.default.format("dxCalendar-currentDay"),
year: _message.default.format("dxCalendar-currentMonth"),
decade: _message.default.format("dxCalendar-currentYear"),
century: _message.default.format("dxCalendar-currentYearRange")
};
const ARIA_LABEL_DATE_FORMAT = "date";
const SELECTION_MODE = {
single: "single",
multiple: "multiple",
range: "range"
};
class BaseView extends _widget.default {
_getViewName() {
return "base"
}
_getDefaultOptions() {
return _extends({}, super._getDefaultOptions(), {
date: new Date,
focusStateEnabled: false,
cellTemplate: null,
disabledDates: null,
onCellClick: null,
onCellHover: null,
onWeekNumberClick: null,
rowCount: 3,
colCount: 4,
allowValueSelection: true,
_todayDate: () => new Date
})
}
_initMarkup() {
super._initMarkup();
this._renderImpl()
}
_renderImpl() {
this.$element().append(this._createTable());
this._createDisabledDatesHandler();
this._renderBody();
this._renderContouredDate();
this._renderValue();
this._renderRange();
this._renderEvents();
this._updateTableAriaLabel()
}
_getLocalizedWidgetName() {
const localizedWidgetName = _message.default.format("dxCalendar-ariaWidgetName");
return localizedWidgetName
}
_getSingleModeAriaLabel() {
const {
value: value
} = this.option();
const localizedWidgetName = this._getLocalizedWidgetName();
const formattedDate = _date.default.format(value, "date");
const selectedDatesText = _message.default.format("dxCalendar-selectedDate", formattedDate);
const ariaLabel = `${localizedWidgetName}. ${selectedDatesText}`;
return ariaLabel
}
_getRangeModeAriaLabel() {
const {
value: value
} = this.option();
const localizedWidgetName = this._getLocalizedWidgetName();
const [startDate, endDate] = value;
const formattedStartDate = _date.default.format(startDate, "date");
const formattedEndDate = _date.default.format(endDate, "date");
const selectedDatesText = startDate && endDate ? _message.default.format("dxCalendar-selectedDateRange", formattedStartDate, formattedEndDate) : _message.default.format("dxCalendar-selectedDate", formattedStartDate ?? formattedEndDate);
const ariaLabel = `${localizedWidgetName}. ${selectedDatesText}`;
return ariaLabel
}
_getMultipleModeAriaLabel() {
const localizedWidgetName = this._getLocalizedWidgetName();
const selectedRangesText = this._getMultipleRangesText();
const ariaLabel = `${localizedWidgetName}. ${selectedRangesText}`;
return ariaLabel
}
_getMultipleRangesText() {
const {
value: value
} = this.option();
const ranges = _date2.default.getRangesByDates(value.map((date => new Date(date))));
if (ranges.length > 2) {
const dateRangeCountText = _message.default.format("dxCalendar-selectedDateRangeCount", ranges.length);
return dateRangeCountText
}
const selectedDatesText = _message.default.format("dxCalendar-selectedDates");
const rangesText = ranges.map((range => this._getRangeText(range))).join(", ");
const result = `${selectedDatesText}: ${rangesText}`;
return result
}
_getRangeText(range) {
const [startDate, endDate] = range;
const formattedStartDate = _date.default.format(startDate, "date");
const formattedEndDate = _date.default.format(endDate, "date");
const selectedDatesText = startDate && endDate ? _message.default.format("dxCalendar-selectedMultipleDateRange", formattedStartDate, formattedEndDate) : formattedStartDate;
return selectedDatesText
}
_getTableAriaLabel() {
const {
value: value,
selectionMode: selectionMode
} = this.option();
const isValueEmpty = !value || Array.isArray(value) && !value.filter(Boolean).length;
if (isValueEmpty) {
return this._getLocalizedWidgetName()
}
switch (selectionMode) {
case SELECTION_MODE.single:
return this._getSingleModeAriaLabel();
case SELECTION_MODE.range:
return this._getRangeModeAriaLabel();
case SELECTION_MODE.multiple:
return this._getMultipleModeAriaLabel()
}
}
_updateTableAriaLabel() {
const label = this._getTableAriaLabel();
this.setAria({
label: label
}, this._$table)
}
_createTable() {
this._$table = (0, _renderer.default)("<table>");
this.setAria({
role: "grid"
}, this._$table);
return this._$table
}
_renderBody() {
this.$body = (0, _renderer.default)("<tbody>").appendTo(this._$table);
const rowData = {
cellDate: this._getFirstCellData(),
prevCellDate: null
};
const {
rowCount: rowsCount,
colCount: colsCount
} = this.option();
for (let rowIndex = 0, rowCount = rowsCount; rowIndex < rowCount; rowIndex++) {
rowData.row = this._createRow();
for (let colIndex = 0, colCount = colsCount; colIndex < colCount; colIndex++) {
this._renderCell(rowData, colIndex)
}
this._renderWeekNumberCell(rowData)
}
}
_renderWeekNumberCell(rowData) {}
_createRow() {
const row = _dom_adapter.default.createElement("tr");
this.setAria("role", "row", (0, _renderer.default)(row));
this.$body.get(0).appendChild(row);
return row
}
_createCell(cellDate, cellIndex) {
const cell = _dom_adapter.default.createElement("td");
const $cell = (0, _renderer.default)(cell);
cell.className = this._getClassNameByDate(cellDate, cellIndex);
cell.setAttribute("data-value", _date_serialization.default.serializeDate(cellDate, _date2.default.getShortDateFormat()));
(0, _element_data.data)(cell, "dxDateValueKey", cellDate);
this.setAria({
role: "gridcell",
selected: false,
label: this.getCellAriaLabel(cellDate)
}, $cell);
return {
cell: cell,
$cell: $cell
}
}
_renderCell(params, cellIndex) {
const {
cellDate: cellDate,
prevCellDate: prevCellDate,
row: row
} = params;
if (prevCellDate) {
_date2.default.fixTimezoneGap(prevCellDate, cellDate)
}
params.prevCellDate = cellDate;
const {
cell: cell,
$cell: $cell
} = this._createCell(cellDate, cellIndex);
const cellTemplate = this.option("cellTemplate");
(0, _renderer.default)(row).append(cell);
if (cellTemplate) {
cellTemplate.render(this._prepareCellTemplateData(cellDate, cellIndex, $cell))
} else {
cell.innerHTML = this._getCellText(cellDate)
}
params.cellDate = this._getNextCellData(cellDate)
}
_getClassNameByDate(cellDate, cellIndex) {
let className = "dx-calendar-cell";
if (this._isTodayCell(cellDate)) {
className += " dx-calendar-today"
}
if (this._isDateOutOfRange(cellDate) || this.isDateDisabled(cellDate)) {
className += " dx-calendar-empty-cell"
}
if (this._isOtherView(cellDate)) {
className += " dx-calendar-other-view"
}
const {
selectionMode: selectionMode
} = this.option();
if (selectionMode === SELECTION_MODE.range) {
if (0 === cellIndex) {
className += " dx-calendar-cell-start-in-row"
}
const {
colCount: colCount
} = this.option();
if (cellIndex === colCount - 1) {
className += " dx-calendar-cell-end-in-row"
}
if (this._isStartDayOfMonth(cellDate)) {
className += " dx-calendar-cell-start"
}
if (this._isEndDayOfMonth(cellDate)) {
className += " dx-calendar-cell-end"
}
}
return className
}
_prepareCellTemplateData(cellDate, cellIndex, $cell) {
const isDateCell = cellDate instanceof Date;
const text = isDateCell ? this._getCellText(cellDate) : cellDate;
const date = isDateCell ? cellDate : void 0;
const view = this._getViewName();
return {
model: {
text: text,
date: date,
view: view
},
container: (0, _element.getPublicElement)($cell),
index: cellIndex
}
}
_renderEvents() {
this._createCellClickAction();
_events_engine.default.off(this._$table, CALENDAR_DXCLICK_EVENT_NAME);
_events_engine.default.on(this._$table, CALENDAR_DXCLICK_EVENT_NAME, NOT_WEEK_CELL_SELECTOR, (e => {
if (!(0, _renderer.default)(e.currentTarget).hasClass("dx-calendar-empty-cell")) {
this._cellClickAction({
event: e,
value: (0, _renderer.default)(e.currentTarget).data("dxDateValueKey")
})
}
}));
const {
selectionMode: selectionMode
} = this.option();
_events_engine.default.off(this._$table, CALENDAR_DXHOVERSTART_EVENT_NAME);
if (selectionMode === SELECTION_MODE.range) {
this._createCellHoverAction();
_events_engine.default.on(this._$table, CALENDAR_DXHOVERSTART_EVENT_NAME, NOT_WEEK_CELL_SELECTOR, (e => {
if (!(0, _renderer.default)(e.currentTarget).hasClass("dx-calendar-empty-cell")) {
this._cellHoverAction({
event: e,
value: (0, _renderer.default)(e.currentTarget).data("dxDateValueKey")
})
}
}))
}
if (selectionMode !== SELECTION_MODE.single) {
this._createWeekNumberCellClickAction();
_events_engine.default.on(this._$table, CALENDAR_DXCLICK_EVENT_NAME, ".dx-calendar-week-number-cell", (e => {
const $row = (0, _renderer.default)(e.currentTarget).closest("tr");
const firstDateInRow = $row.find(".dx-calendar-cell").first().data("dxDateValueKey");
const lastDateInRow = $row.find(".dx-calendar-cell").last().data("dxDateValueKey");
const rowDates = [..._date2.default.getDatesOfInterval(firstDateInRow, lastDateInRow, 864e5), lastDateInRow];
this._weekNumberCellClickAction({
event: e,
rowDates: rowDates
})
}))
}
}
_createCellClickAction() {
this._cellClickAction = this._createActionByOption("onCellClick")
}
_createCellHoverAction() {
this._cellHoverAction = this._createActionByOption("onCellHover")
}
_createWeekNumberCellClickAction() {
this._weekNumberCellClickAction = this._createActionByOption("onWeekNumberClick")
}
_createDisabledDatesHandler() {
const {
disabledDates: disabledDates
} = this.option();
this._disabledDatesHandler = Array.isArray(disabledDates) ? this._getDefaultDisabledDatesHandler(disabledDates) : disabledDates || _common.noop
}
_getDefaultDisabledDatesHandler(disabledDates) {
return _common.noop
}
_isTodayCell(cellDate) {
_class.default.abstract()
}
_isDateOutOfRange(cellDate) {
_class.default.abstract()
}
isDateDisabled(cellDate) {
const dateParts = {
date: cellDate,
view: this._getViewName()
};
return this._disabledDatesHandler(dateParts)
}
_isOtherView(cellDate) {
_class.default.abstract()
}
_isStartDayOfMonth(cellDate) {
_class.default.abstract()
}
_isEndDayOfMonth(cellDate) {
_class.default.abstract()
}
_getCellText(cellDate) {
_class.default.abstract()
}
_getFirstCellData() {
_class.default.abstract()
}
_getNextCellData(date) {
_class.default.abstract()
}
_renderContouredDate(contouredDate) {
if (!this.option("focusStateEnabled")) {
return
}
contouredDate = contouredDate || this.option("contouredDate");
const $oldContouredCell = this._getContouredCell();
const $newContouredCell = this._getCellByDate(contouredDate);
$oldContouredCell.removeClass("dx-calendar-contoured-date");
if (contouredDate) {
$newContouredCell.addClass("dx-calendar-contoured-date")
}
}
_getContouredCell() {
return this._$table.find(".dx-calendar-contoured-date")
}
_renderValue() {
if (!this.option("allowValueSelection")) {
return
}
let value = this.option("value");
if (!Array.isArray(value)) {
value = [value]
}
this._updateSelectedClass(value)
}
_updateSelectedClass(value) {
var _this$_$selectedCells;
if (this._isRangeMode() && !this._isMonthView()) {
return
}
null === (_this$_$selectedCells = this._$selectedCells) || void 0 === _this$_$selectedCells || _this$_$selectedCells.forEach(($cell => {
$cell.removeClass("dx-calendar-selected-date")
}));
this._$selectedCells = value.map((value => this._getCellByDate(value)));
this._$selectedCells.forEach(($cell => {
$cell.addClass("dx-calendar-selected-date")
}))
}
_renderRange() {
var _this$_$rangeCells, _this$_$hoveredRangeC, _this$_$rangeStartHov, _this$_$rangeEndHover, _this$_$rangeStartDat, _this$_$rangeEndDateC, _this$_$rangeStartDat2, _this$_$rangeEndDateC2;
const {
allowValueSelection: allowValueSelection,
value: value,
range: range
} = this.option();
if (!allowValueSelection || !this._isRangeMode() || !this._isMonthView()) {
return
}
null === (_this$_$rangeCells = this._$rangeCells) || void 0 === _this$_$rangeCells || _this$_$rangeCells.forEach(($cell => {
$cell.removeClass("dx-calendar-cell-in-range")
}));
null === (_this$_$hoveredRangeC = this._$hoveredRangeCells) || void 0 === _this$_$hoveredRangeC || _this$_$hoveredRangeC.forEach(($cell => {
$cell.removeClass("dx-calendar-cell-range-hover")
}));
null === (_this$_$rangeStartHov = this._$rangeStartHoverCell) || void 0 === _this$_$rangeStartHov || _this$_$rangeStartHov.removeClass("dx-calendar-cell-range-hover-start");
null === (_this$_$rangeEndHover = this._$rangeEndHoverCell) || void 0 === _this$_$rangeEndHover || _this$_$rangeEndHover.removeClass("dx-calendar-cell-range-hover-end");
null === (_this$_$rangeStartDat = this._$rangeStartDateCell) || void 0 === _this$_$rangeStartDat || _this$_$rangeStartDat.removeClass("dx-calendar-range-start-date");
null === (_this$_$rangeEndDateC = this._$rangeEndDateCell) || void 0 === _this$_$rangeEndDateC || _this$_$rangeEndDateC.removeClass("dx-calendar-range-end-date");
this._$rangeCells = range.map((value => this._getCellByDate(value)));
this._$rangeStartDateCell = this._getCellByDate(value[0]);
this._$rangeEndDateCell = this._getCellByDate(value[1]);
this._$rangeCells.forEach(($cell => {
$cell.addClass("dx-calendar-cell-in-range")
}));
null === (_this$_$rangeStartDat2 = this._$rangeStartDateCell) || void 0 === _this$_$rangeStartDat2 || _this$_$rangeStartDat2.addClass("dx-calendar-range-start-date");
null === (_this$_$rangeEndDateC2 = this._$rangeEndDateCell) || void 0 === _this$_$rangeEndDateC2 || _this$_$rangeEndDateC2.addClass("dx-calendar-range-end-date")
}
_renderHoveredRange() {
var _this$_$hoveredRangeC2, _this$_$rangeStartHov2, _this$_$rangeEndHover2, _this$_$rangeStartHov3, _this$_$rangeEndHover3;
const {
allowValueSelection: allowValueSelection,
hoveredRange: hoveredRange
} = this.option();
if (!allowValueSelection || !this._isRangeMode() || !this._isMonthView()) {
return
}
null === (_this$_$hoveredRangeC2 = this._$hoveredRangeCells) || void 0 === _this$_$hoveredRangeC2 || _this$_$hoveredRangeC2.forEach(($cell => {
$cell.removeClass("dx-calendar-cell-range-hover")
}));
null === (_this$_$rangeStartHov2 = this._$rangeStartHoverCell) || void 0 === _this$_$rangeStartHov2 || _this$_$rangeStartHov2.removeClass("dx-calendar-cell-range-hover-start");
null === (_this$_$rangeEndHover2 = this._$rangeEndHoverCell) || void 0 === _this$_$rangeEndHover2 || _this$_$rangeEndHover2.removeClass("dx-calendar-cell-range-hover-end");
this._$hoveredRangeCells = hoveredRange.map((value => this._getCellByDate(value)));
this._$rangeStartHoverCell = this._getCellByDate(hoveredRange[0]);
this._$rangeEndHoverCell = this._getCellByDate(hoveredRange[hoveredRange.length - 1]);
this._$hoveredRangeCells.forEach(($cell => {
$cell.addClass("dx-calendar-cell-range-hover")
}));
null === (_this$_$rangeStartHov3 = this._$rangeStartHoverCell) || void 0 === _this$_$rangeStartHov3 || _this$_$rangeStartHov3.addClass("dx-calendar-cell-range-hover-start");
null === (_this$_$rangeEndHover3 = this._$rangeEndHoverCell) || void 0 === _this$_$rangeEndHover3 || _this$_$rangeEndHover3.addClass("dx-calendar-cell-range-hover-end")
}
_isMonthView() {
const {
zoomLevel: zoomLevel
} = this.option();
return "month" === zoomLevel
}
_isRangeMode() {
const {
selectionMode: selectionMode
} = this.option();
return selectionMode === SELECTION_MODE.range
}
_getCurrentDateFormat() {
return null
}
getCellAriaLabel(date) {
const viewName = this._getViewName();
const isToday = this._isTodayCell(date);
const format = this._getCurrentDateFormat();
const dateRangeText = format ? _date.default.format(date, format) : this._getCellText(date);
const ariaLabel = isToday ? `${dateRangeText}. ${CURRENT_DATE_TEXT[viewName]}` : dateRangeText;
return ariaLabel
}
_getFirstAvailableDate() {
let date = this.option("date");
const min = this.option("min");
date = _date2.default.getViewFirstCellDate(this._getViewName(), date);
return new Date(min && date < min ? min : date)
}
_getCellByDate(contouredDate) {
_class.default.abstract()
}
isBoundary(date) {
_class.default.abstract()
}
_optionChanged(args) {
const {
name: name,
value: value
} = args;
switch (name) {
case "value":
this._renderValue();
this._updateTableAriaLabel();
break;
case "range":
this._renderRange();
break;
case "hoveredRange":
this._renderHoveredRange();
break;
case "contouredDate":
this._renderContouredDate(value);
break;
case "onCellClick":
this._createCellClickAction();
break;
case "onCellHover":
this._createCellHoverAction();
break;
case "min":
case "max":
case "disabledDates":
case "cellTemplate":
case "selectionMode":
this._invalidate();
break;
case "_todayDate":
this._renderBody();
break;
default:
super._optionChanged(args)
}
}
}
var _default = exports.default = BaseView;