devextreme
Version:
JavaScript/TypeScript Component Suite for Responsive Web Development
478 lines (477 loc) • 17.6 kB
JavaScript
/**
* DevExtreme (esm/__internal/scheduler/workspaces/m_agenda.js)
* Version: 25.2.7
* Build date: Tue May 05 2026
*
* Copyright (c) 2012 - 2026 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/
import dateLocalization from "../../../common/core/localization/date";
import registerComponent from "../../../core/component_registrator";
import domAdapter from "../../../core/dom_adapter";
import {
getPublicElement
} from "../../../core/element";
import $ from "../../../core/renderer";
import {
noop
} from "../../../core/utils/common";
import dateUtils from "../../../core/utils/date";
import {
extend
} from "../../../core/utils/extend";
import {
each
} from "../../../core/utils/iterator";
import {
setHeight,
setOuterHeight
} from "../../../core/utils/size";
import {
EMPTY_ACTIVE_STATE_UNIT
} from "../../core/widget/widget";
import {
DATE_TABLE_CLASS,
DATE_TABLE_ROW_CLASS,
GROUP_HEADER_CONTENT_CLASS,
GROUP_ROW_CLASS,
TIME_PANEL_CLASS
} from "../m_classes";
import tableCreatorModule from "../m_table_creator";
import {
agendaUtils,
formatWeekday,
getVerticalGroupCountClass
} from "../r1/utils/index";
import {
VIEWS
} from "../utils/options/constants_view";
import {
reduceResourcesTree
} from "../utils/resource_manager/agenda_group_utils";
import WorkSpace from "./m_work_space";
const {
tableCreator: tableCreator
} = tableCreatorModule;
const AGENDA_CLASS = "dx-scheduler-agenda";
const AGENDA_DATE_CLASS = "dx-scheduler-agenda-date";
const GROUP_TABLE_CLASS = "dx-scheduler-group-table";
const TIME_PANEL_ROW_CLASS = "dx-scheduler-time-panel-row";
const TIME_PANEL_CELL_CLASS = "dx-scheduler-time-panel-cell";
const NODATA_CONTAINER_CLASS = "dx-scheduler-agenda-nodata";
const LAST_ROW_CLASS = "dx-scheduler-date-table-last-row";
const INNER_CELL_MARGIN = 5;
const OUTER_CELL_MARGIN = 20;
class SchedulerAgenda extends WorkSpace {
constructor() {
super(...arguments);
this.rows = []
}
_activeStateUnit() {
return EMPTY_ACTIVE_STATE_UNIT
}
get type() {
return VIEWS.AGENDA
}
getStartViewDate() {
return this.startViewDate
}
_init() {
super._init()
}
_getDefaultOptions() {
return extend(super._getDefaultOptions(), {
agendaDuration: 7,
rowHeight: 60,
noDataText: ""
})
}
_optionChanged(args) {
const {
name: name
} = args;
const {
value: value
} = args;
switch (name) {
case "agendaDuration":
break;
case "noDataText":
case "rowHeight":
this.recalculateAgenda(this.rows);
break;
case "groups":
if (!(null !== value && void 0 !== value && value.length)) {
if (this._$groupTable) {
this._$groupTable.remove();
this._$groupTable = null;
this.detachGroupCountClass()
}
} else if (!this._$groupTable) {
this.initGroupTable();
this._dateTableScrollable.$content().prepend(this._$groupTable)
}
super._optionChanged(args);
break;
default:
super._optionChanged(args)
}
}
_renderFocusState() {
return noop()
}
_renderFocusTarget() {
return noop()
}
_cleanFocusState() {
return noop()
}
supportAllDayRow() {
return false
}
isVerticalGroupedWorkSpace() {
return false
}
getElementClass() {
return AGENDA_CLASS
}
getRowCount() {
return this.option("agendaDuration")
}
_getCellCount() {
return 1
}
getTimePanelRowCount() {
return this.option("agendaDuration")
}
renderAllDayPanel() {
return noop()
}
updateAllDayVisibility() {
return noop()
}
initWorkSpaceUnits() {
this.initGroupTable();
this.$timePanel = $("<table>").attr("aria-hidden", true).addClass(TIME_PANEL_CLASS);
this._$dateTable = $("<table>").attr("aria-hidden", true).addClass(DATE_TABLE_CLASS);
this._$dateTableScrollableContent = $("<div>").addClass("dx-scheduler-date-table-scrollable-content");
this.$dateTableContainer = $("<div>").addClass("dx-scheduler-date-table-container")
}
initGroupTable() {
const groups = this.option("groups");
if (null !== groups && void 0 !== groups && groups.length) {
this._$groupTable = $("<table>").attr("aria-hidden", true).addClass(GROUP_TABLE_CLASS)
}
}
renderView() {
this.startViewDate = agendaUtils.calculateStartViewDate(this.option("currentDate"), this.option("startDayHour"));
this.rows = []
}
recalculateAgenda(rows) {
let cellTemplates = [];
this.cleanView();
if (this.rowsIsEmpty(rows)) {
this.renderNoData();
return
}
this.rows = rows;
if (this._$groupTable) {
cellTemplates = this.renderGroupHeader();
this.setGroupHeaderCellsHeight()
}
this.renderTimePanel();
this.renderDateTable();
this.applyCellTemplates(cellTemplates);
this._dateTableScrollable.update()
}
renderNoData() {
this.$noDataContainer = $("<div>").addClass(NODATA_CONTAINER_CLASS).html(this.option("noDataText"));
this._dateTableScrollable.$content().append(this.$noDataContainer)
}
setTableSizes() {
return noop()
}
toggleHorizontalScrollClass() {
return noop()
}
createCrossScrollingConfig(argument) {
return noop()
}
setGroupHeaderCellsHeight() {
const $cells = this.getGroupHeaderCells().filter((_, element) => !element.getAttribute("rowSpan"));
const rows = this.removeEmptyRows(this.rows);
if (!rows.length) {
return
}
for (let i = 0; i < $cells.length; i++) {
const $cellContent = $cells.eq(i).find(".dx-scheduler-group-header-content");
setOuterHeight($cellContent, this.getGroupRowHeight(rows[i]))
}
}
rowsIsEmpty(rows) {
let result = true;
for (let i = 0; i < rows.length; i++) {
const groupRow = rows[i];
for (let j = 0; j < groupRow.length; j++) {
if (groupRow[j]) {
result = false;
break
}
}
}
return result
}
attachGroupCountClass() {
const className = getVerticalGroupCountClass(this.option("groups"));
this.$element().addClass(className)
}
removeEmptyRows(rows) {
const result = [];
const isEmpty = function(data) {
return !data.some(value => value > 0)
};
for (let i = 0; i < rows.length; i++) {
if (rows[i].length && !isEmpty(rows[i])) {
result.push(rows[i])
}
}
return result
}
getGroupHeaderContainer() {
return this._$groupTable
}
makeGroupRows() {
const resourceManager = this.option("getResourceManager")();
const allAppointments = this.option("getFilteredItems")();
const tree = reduceResourcesTree(resourceManager.resourceById, resourceManager.groupsTree, allAppointments);
const cellTemplate = this.option("resourceCellTemplate");
const getGroupHeaderContentClass = GROUP_HEADER_CONTENT_CLASS;
const cellTemplates = [];
const table = tableCreator.makeGroupedTableFromJSON(tree, {
cellTag: "th",
groupTableClass: GROUP_TABLE_CLASS,
groupRowClass: GROUP_ROW_CLASS,
groupCellClass: this.getGroupHeaderClass(),
groupCellCustomContent(cell, cellTextElement, index, node) {
const container = domAdapter.createElement("div");
container.className = getGroupHeaderContentClass;
const value = node.grouped[node.resourceIndex];
const resource = resourceManager.resourceById[node.resourceIndex];
const resourceData = null === resource || void 0 === resource ? void 0 : resource.data.find(rItem => resource.dataAccessor.get("id", rItem) === value);
const resourceItem = null === resource || void 0 === resource ? void 0 : resource.items.find(rItem => rItem.id === value);
if (null !== cellTemplate && void 0 !== cellTemplate && cellTemplate.render) {
cellTemplates.push(cellTemplate.render.bind(cellTemplate, {
model: {
data: resourceData,
id: value,
color: null === resourceItem || void 0 === resourceItem ? void 0 : resourceItem.color,
text: cellTextElement.textContent
},
container: getPublicElement($(container)),
index: index
}))
} else {
const contentWrapper = domAdapter.createElement("div");
contentWrapper.appendChild(cellTextElement);
container.appendChild(contentWrapper)
}
cell.appendChild(container)
},
cellTemplate: cellTemplate
});
return {
elements: $(table).find(`.${GROUP_ROW_CLASS}`),
cellTemplates: cellTemplates
}
}
cleanView() {
this._$dateTable.empty();
this.$timePanel.empty();
if (this._$groupTable) {
this._$groupTable.empty()
}
if (this.$noDataContainer) {
this.$noDataContainer.empty();
this.$noDataContainer.remove();
delete this.$noDataContainer
}
}
createWorkSpaceElements() {
this.createWorkSpaceStaticElements()
}
createWorkSpaceStaticElements() {
this.$dateTableContainer.append(this._$dateTable);
this._dateTableScrollable.$content().append(this._$dateTableScrollableContent);
if (this._$groupTable) {
this._$dateTableScrollableContent.prepend(this._$groupTable)
}
this._$dateTableScrollableContent.append(this.$timePanel, this.$dateTableContainer);
this.$element().append(this._dateTableScrollable.$element())
}
renderDateTable() {
this.renderTableBody({
container: getPublicElement(this._$dateTable),
rowClass: DATE_TABLE_ROW_CLASS,
cellClass: this.getDateTableCellClass()
})
}
attachTablesEvents() {
return noop()
}
attachEvents() {
return noop()
}
isIndicationAvailable() {
return false
}
prepareCellTemplateOptions(text, date, rowIndex, $cell) {
const leaf = this.resourceManager.groupsLeafs[rowIndex];
const groups = (null === leaf || void 0 === leaf ? void 0 : leaf.grouped) ?? {};
const groupIndex = null === leaf || void 0 === leaf ? void 0 : leaf.groupIndex;
return {
model: {
text: text,
date: date,
groups: groups,
groupIndex: groupIndex
},
container: getPublicElement($cell),
index: rowIndex
}
}
renderTableBody(options, delayCellTemplateRendering) {
const cellTemplates = [];
const cellTemplateOpt = options.cellTemplate;
this.$rows = [];
let i;
const fillTableBody = function(rowIndex, rowSize) {
if (rowSize) {
let date;
let cellDateNumber;
let cellDayName;
const $row = $("<tr>");
const $td = $("<td>");
setHeight($td, this.getRowHeight(rowSize));
if (options.getStartDate) {
var _options$getStartDate;
date = null === (_options$getStartDate = options.getStartDate) || void 0 === _options$getStartDate ? void 0 : _options$getStartDate.call(options, rowIndex);
cellDateNumber = dateLocalization.format(date, "d");
cellDayName = dateLocalization.format(date, formatWeekday)
}
if (null !== cellTemplateOpt && void 0 !== cellTemplateOpt && cellTemplateOpt.render) {
const templateOptions = this.prepareCellTemplateOptions(`${cellDateNumber} ${cellDayName}`, date, i, $td);
cellTemplates.push(cellTemplateOpt.render.bind(cellTemplateOpt, templateOptions))
} else if (cellDateNumber && cellDayName) {
$td.addClass(AGENDA_DATE_CLASS).text(`${cellDateNumber} ${cellDayName}`)
}
if (options.rowClass) {
$row.addClass(options.rowClass)
}
if (options.cellClass) {
$td.addClass(options.cellClass)
}
$row.append($td);
this.$rows.push($row)
}
}.bind(this);
for (i = 0; i < this.rows.length; i++) {
each(this.rows[i], fillTableBody);
this.setLastRowClass()
}
$(options.container).append($("<tbody>").append(this.$rows));
this.applyCellTemplates(cellTemplates)
}
setLastRowClass() {
if (this.rows.length > 1 && this.$rows.length) {
const $lastRow = this.$rows[this.$rows.length - 1];
$lastRow.addClass(LAST_ROW_CLASS)
}
}
renderTimePanel() {
this.renderTableBody({
container: getPublicElement(this.$timePanel),
rowCount: this.getTimePanelRowCount(),
cellCount: 1,
rowClass: TIME_PANEL_ROW_CLASS,
cellClass: TIME_PANEL_CELL_CLASS,
cellTemplate: this.option("dateCellTemplate"),
getStartDate: this.getTimePanelStartDate.bind(this)
})
}
getTimePanelStartDate(rowIndex) {
const current = new Date(this.option("currentDate"));
const cellDate = new Date(current.setDate(current.getDate() + rowIndex));
return cellDate
}
getRowHeight(rowSize) {
const baseHeight = this.option("rowHeight");
const innerOffset = 5 * (rowSize - 1);
return rowSize ? baseHeight * rowSize + innerOffset + 20 : 0
}
getGroupRowHeight(groupRows) {
if (!groupRows) {
return
}
let result = 0;
for (let i = 0; i < groupRows.length; i++) {
result += this.getRowHeight(groupRows[i])
}
return result
}
renderAgendaLayout(appointments) {
this.renderView();
const rows = agendaUtils.calculateRows(appointments, this.option("agendaDuration"), this.getStartViewDate(), this.resourceManager.groupCount());
this.recalculateAgenda(rows)
}
getAgendaVerticalStepHeight() {
return this.option("rowHeight")
}
getEndViewDate() {
const currentDate = new Date(this.option("currentDate"));
const agendaDuration = this.option("agendaDuration");
currentDate.setHours(this.option("endDayHour"));
const result = currentDate.setDate(currentDate.getDate() + agendaDuration - 1) - 6e4;
return new Date(result)
}
getEndViewDateByEndDayHour() {
return this.getEndViewDate()
}
updateScrollPosition(date) {
const newDate = this.timeZoneCalculator.createDate(date, "toGrid");
const bounds = this.getVisibleBounds();
const startDateHour = newDate.getHours();
const startDateMinutes = newDate.getMinutes();
if (this.needUpdateScrollPosition(startDateHour, startDateMinutes, bounds, newDate)) {
this.scrollTo(newDate)
}
}
needUpdateScrollPosition(hours, minutes, bounds, newData) {
let isUpdateNeeded = false;
if (hours < bounds.top.hours || hours > bounds.bottom.hours) {
isUpdateNeeded = true
}
if (hours === bounds.top.hours && minutes < bounds.top.minutes) {
isUpdateNeeded = true
}
if (hours === bounds.bottom.hours && minutes > bounds.top.minutes) {
isUpdateNeeded = true
}
return isUpdateNeeded
}
renovatedRenderSupported() {
return false
}
getTotalViewDuration() {
return dateUtils.dateToMilliseconds("day") * this.option("intervalCount")
}
getDOMElementsMetaData() {
return {
dateTableCellsMeta: [
[{}]
],
allDayPanelCellsMeta: [{}]
}
}
}
registerComponent("dxSchedulerAgenda", SchedulerAgenda);
export default SchedulerAgenda;