UNPKG

devextreme

Version:

JavaScript/TypeScript Component Suite for Responsive Web Development

469 lines (468 loc) • 16.6 kB
/** * DevExtreme (esm/__internal/scheduler/workspaces/agenda.js) * Version: 26.1.3 * Build date: Wed Jun 10 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 "../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 = []; 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 (!Array.isArray(value) || !value.length) { if (this.$groupTable) { this.$groupTable.remove(); this.$groupTable = null; this.detachGroupCountClass() } } else if (!this.$groupTable) { this.initGroupTable(); if (this.$groupTable) { 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() } needCreateCrossScrolling() { return false } 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 += 1) { const $cellContent = $cells.eq(i).find(".dx-scheduler-group-header-content"); setOuterHeight($cellContent, this.getGroupRowHeight(rows[i])) } } rowsIsEmpty(rows) { return rows.every(groupRow => groupRow.every(cell => !cell)) } attachGroupCountClass() { const className = getVerticalGroupCountClass(this.option("groups")); if (className) { this.$element().addClass(className) } } removeEmptyRows(rows) { return rows.filter(row => { return row.length && !(data = row, !data.some(value => value > 0)); var data }) } 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 = 0; const fillTableBody = (rowIndex, rowSize) => { if (rowSize) { var _options$getStartDate; const date = null === (_options$getStartDate = options.getStartDate) || void 0 === _options$getStartDate ? void 0 : _options$getStartDate.call(options, rowIndex); let cellDateNumber = ""; let cellDayName = ""; const $row = $("<tr>"); const $td = $("<td>"); setHeight($td, this.getRowHeight(rowSize)); if (date) { cellDateNumber = String(dateLocalization.format(date, "d")); cellDayName = String(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) } }; for (i = 0; i < this.rows.length; i += 1) { 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) { return agendaUtils.getDateByIndex(this.getStartViewDate(), rowIndex) } getRowHeight(rowSize) { const baseHeight = this.option("rowHeight"); const innerOffset = 5 * (rowSize - 1); return rowSize ? baseHeight * rowSize + innerOffset + 20 : 0 } getGroupRowHeight(groupRows) { if (!groupRows) { return 0 } return groupRows.reduce((result, groupRow) => result + this.getRowHeight(groupRow), 0) } 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() { return agendaUtils.calculateEndViewDate(this.getStartViewDate(), this.option("endDayHour"), this.option("agendaDuration")) } getEndViewDateByEndDayHour() { return this.getEndViewDate() } updateScrollPosition(date) { const newDate = this.timeZoneCalculator.createDate(date, "toGrid"); if (this.needUpdateScrollPosition(newDate)) { this.scrollTo(newDate) } } needUpdateScrollPosition(date) { const bounds = this.getVisibleBounds(); const hours = date.getHours(); const minutes = date.getMinutes(); 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.bottom.minutes) { isUpdateNeeded = true } return isUpdateNeeded } renovatedRenderSupported() { return false } isVirtualScrolling() { return false } getTotalViewDuration() { return dateUtils.dateToMilliseconds("day") * this.option("intervalCount") } getDOMElementsMetaData() { return { dateTableCellsMeta: [ [{ top: 0, left: 0, width: 0, height: 0 }] ], allDayPanelCellsMeta: [{ top: 0, left: 0, width: 0, height: 0 }] } } } registerComponent("dxSchedulerAgenda", SchedulerAgenda); export default SchedulerAgenda;