diff --git a/packages/devextreme/js/__internal/scheduler/m_table_creator.ts b/packages/devextreme/js/__internal/scheduler/m_table_creator.ts deleted file mode 100644 index d38fdba2f3f1..000000000000 --- a/packages/devextreme/js/__internal/scheduler/m_table_creator.ts +++ /dev/null @@ -1,450 +0,0 @@ -import type { Orientation } from '@js/common'; -import domAdapter from '@js/core/dom_adapter'; -import { getPublicElement } from '@js/core/element'; -import { data as elementData } from '@js/core/element_data'; -import type { dxElementWrapper } from '@js/core/renderer'; -import $ from '@js/core/renderer'; -import type { TemplateBase } from '@js/core/templates/template_base'; -import { isFunction } from '@js/core/utils/type'; - -import type { ResourceLoader } from './utils/loader/resource_loader'; -import type { GroupNode } from './utils/resource_manager/types'; - -const ROW_SELECTOR = 'tr'; - -export interface GroupCssClasses { - groupHeaderRowClass?: string; - groupRowClass?: string; - groupHeaderClass: string | ((index: number) => string); - groupHeaderContentClass?: string; -} - -export interface GroupRows { - elements: dxElementWrapper | dxElementWrapper[]; - cellTemplates: (() => dxElementWrapper)[]; -} - -class SchedulerTableCreator { - readonly VERTICAL = 'vertical'; - - readonly HORIZONTAL = 'horizontal'; - - insertAllDayRow(allDayElements, tableBody, index) { - if (allDayElements[index]) { - let row = allDayElements[index].find(ROW_SELECTOR); - - if (!row.length) { - row = $(domAdapter.createElement(ROW_SELECTOR)); - row.append(allDayElements[index].get(0)); - } - - tableBody.appendChild(row.get ? row.get(0) : row); - } - } - - makeTable(options) { - const tableBody = domAdapter.createElement('tbody'); - const templateCallbacks: any[] = []; - let row; - const rowCountInGroup = options.groupCount ? options.rowCount / options.groupCount : options.rowCount; - let allDayElementIndex = 0; - const { allDayElements } = options; - const { groupIndex } = options; - const { rowCount } = options; - - $(options.container).append(tableBody); - - if (allDayElements) { - this.insertAllDayRow(allDayElements, tableBody, 0); - allDayElementIndex++; - } - - for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) { - row = domAdapter.createElement(ROW_SELECTOR); - tableBody.appendChild(row); - - const isLastRowInGroup = (rowIndex + 1) % rowCountInGroup === 0; - - if (options.rowClass) { - row.className = options.rowClass; - } - - for (let columnIndex = 0; columnIndex < options.cellCount; columnIndex++) { - const td = domAdapter.createElement('td'); - row.appendChild(td); - - if (options.cellClass) { - if (isFunction(options.cellClass)) { - td.className = options.cellClass(rowIndex, columnIndex); - } else { - td.className = options.cellClass; - } - } - - let cellDataObject; - let dataKey; - let dataValue; - - if (options.getCellData) { - cellDataObject = options.getCellData(td, rowIndex, columnIndex, groupIndex); - dataKey = cellDataObject.key; - dataValue = cellDataObject.value; - dataKey && elementData(td, dataKey, dataValue); - } - - options.setAdditionalClasses?.($(td), dataValue); - - if (options.cellTemplate && options.cellTemplate.render) { - const additionalTemplateData = options.getTemplateData - ? options.getTemplateData(rowIndex) - : {}; - - const templateOptions = { - model: { - text: options.getCellText ? options.getCellText(rowIndex, columnIndex) : '', - date: options.getCellDate ? options.getCellDate(rowIndex) : undefined, - ...additionalTemplateData, - }, - container: getPublicElement($(td)), - index: rowIndex * options.cellCount + columnIndex, - }; - - if (dataValue) { - if (dataValue.startDate) { - templateOptions.model.startDate = dataValue.startDate; - } - - if (dataValue.endDate) { - templateOptions.model.endDate = dataValue.endDate; - } - - if (dataValue.groups) { - templateOptions.model.groups = dataValue.groups; - } - - if (dataValue.allDay) { - templateOptions.model.allDay = dataValue.allDay; - } - } - - templateCallbacks.push(options.cellTemplate.render.bind(options.cellTemplate, templateOptions)); - } else if (options.getCellText) { - $('
') - .text(options.getCellText(rowIndex, columnIndex)) - .addClass(options.getCellTextClass) - .appendTo($(td)); - } - } - - if (allDayElements && isLastRowInGroup) { - this.insertAllDayRow(allDayElements, tableBody, allDayElementIndex); - allDayElementIndex++; - } - } - - return templateCallbacks; - } - - makeGroupedTable( - type: Orientation, - groups: ResourceLoader[], - cssClasses: GroupCssClasses, - cellCount: number, - cellTemplate: TemplateBase | null | undefined, - rowCount: number, - groupByDate: boolean, - ): GroupRows { - if (type === this.VERTICAL) { - return this.makeVerticalGroupedRows(groups, cssClasses, cellTemplate, rowCount); - } - - return this.makeHorizontalGroupedRows(groups, cssClasses, cellCount, cellTemplate, groupByDate); - } - - makeGroupedTableFromJSON(tree: GroupNode[], config?) { - let table; - const cellStorage: any[] = []; - let rowIndex = 0; - - config = config || {}; - - const cellTag = config.cellTag || 'td'; - const { groupTableClass } = config; - const { groupRowClass } = config; - const { groupCellClass } = config; - const { groupCellCustomContent } = config; - - function createTable() { - table = domAdapter.createElement('table'); - - if (groupTableClass) { - table.className = groupTableClass; - } - } - - function getChildCount(item: GroupNode) { - if (item.children) { - return item.children.length; - } - return 0; - } - - function createCell(text: string, childCount, index, node: GroupNode) { - const cell = { - element: domAdapter.createElement(cellTag), - childCount, - }; - - if (groupCellClass) { - cell.element.className = groupCellClass; - } - - const cellText = (domAdapter as any).createTextNode(text); - if (typeof groupCellCustomContent === 'function') { - groupCellCustomContent(cell.element, cellText, index, node); - } else { - cell.element.appendChild(cellText); - } - - return cell; - } - - function generateCells(groupNodes: GroupNode[]) { - for (let i = 0; i < groupNodes.length; i++) { - const childCount = getChildCount(groupNodes[i]); - const cell = createCell(groupNodes[i].resourceText, childCount, i, groupNodes[i]); - - if (!cellStorage[rowIndex]) { - cellStorage[rowIndex] = []; - } - cellStorage[rowIndex].push(cell); - - if (childCount) { - generateCells(groupNodes[i].children); - } else { - rowIndex++; - } - } - } - - function putCellsToRows() { - cellStorage.forEach((cells) => { - const row = domAdapter.createElement(ROW_SELECTOR); - if (groupRowClass) { - row.className = groupRowClass; - } - - const rowspans: any[] = []; - - for (let i = cells.length - 1; i >= 0; i--) { - const prev = cells[i + 1]; - let rowspan = cells[i].childCount; - if (prev && prev.childCount) { - rowspan *= prev.childCount; - } - rowspans.push(rowspan); - } - rowspans.reverse(); - - cells.forEach((cell, index) => { - if (rowspans[index]) { - cell.element.setAttribute('rowSpan', rowspans[index]); - } - row.appendChild(cell.element); - }); - - table.appendChild(row); - }); - } - - createTable(); - generateCells(tree); - putCellsToRows(); - - return table; - } - - private makeFlexGroupedRowCells(group, repeatCount, cssClasses, cellTemplate, repeatByDate = 1) { - const cells: any[] = []; - const { items } = group; - const itemCount = items.length; - - for (let i = 0; i < repeatCount * repeatByDate; i++) { - for (let j = 0; j < itemCount; j++) { - let $container = $('
'); - const cell: any = {}; - - if (cellTemplate && cellTemplate.render) { - const templateOptions = { - model: items[j], - container: getPublicElement($container), - index: i * itemCount + j, - }; - - if (group.data) { - templateOptions.model.data = group.data[j]; - } - - cell.template = cellTemplate.render.bind(cellTemplate, templateOptions); - } else { - $container.text(items[j].text).attr('title', items[j].text).addClass('dx-scheduler-group-header-content'); - $container = $('
').append($container); - } - - const cssClass = isFunction(cssClasses.groupHeaderClass) ? cssClasses.groupHeaderClass(j) : cssClasses.groupHeaderClass; - - cell.element = $container.addClass(cssClass); - - cells.push(cell); - } - } - - return cells; - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - private makeVerticalGroupedRows(groups, cssClasses, cellTemplate, rowCount?: any) { - const cellTemplates: any = []; - let repeatCount = 1; - const cellsArray: any = []; - - const cellIterator = function (cell) { - if (cell.template) { - cellTemplates.push(cell.template); - } - }; - - for (let i = 0; i < groups.length; i++) { - if (i > 0) { - // eslint-disable-next-line operator-assignment - repeatCount = groups[i - 1].items.length * repeatCount; - } - - const cells = this.makeFlexGroupedRowCells(groups[i], repeatCount, cssClasses, cellTemplate); - cells.forEach(cellIterator); - cellsArray.push(cells); - } - - const rows: any = []; - const groupCount = cellsArray.length; - - for (let i = 0; i < groupCount; i++) { - rows.push($('
').addClass(cssClasses.groupHeaderRowClass)); - } - - for (let i = groupCount - 1; i >= 0; i--) { - const currentColumnLength = cellsArray[i].length; - for (let j = 0; j < currentColumnLength; j++) { - rows[i].append(cellsArray[i][j].element); - } - } - - return { - elements: $('
').addClass('dx-scheduler-group-flex-container').append(rows), - cellTemplates, - }; - } - - private makeHorizontalGroupedRows(groups, cssClasses, cellCount, cellTemplate, groupByDate) { - let repeatCount = 1; - const groupCount = groups.length; - const rows: any = []; - const cellTemplates: any = []; - const repeatByDate = groupByDate ? cellCount : 1; - - const cellIterator = function (cell) { - if (cell.template) { - cellTemplates.push(cell.template); - } - - return cell.element; - }; - - for (let i = 0; i < groupCount; i++) { - if (i > 0) { - // eslint-disable-next-line operator-assignment - repeatCount = groups[i - 1].items.length * repeatCount; - } - - const cells: any = this.makeGroupedRowCells(groups[i], repeatCount, cssClasses, cellTemplate, repeatByDate); - - rows.push( - $('') - .addClass(cssClasses.groupRowClass) - .append(cells.map(cellIterator)), - ); - } - - const maxCellCount = rows[groupCount - 1].find('th').length; - - for (let j = 0; j < groupCount; j++) { - const $cell = rows[j].find('th'); - let colspan = maxCellCount / $cell.length; - - if (!groupByDate) { - colspan *= cellCount; - } - if ((colspan > 1 && repeatByDate === 1) || (groupByDate && groupCount > 1)) { - $cell.attr('colSpan', colspan); - } - } - - return { - elements: rows, - cellTemplates, - }; - } - - private makeGroupedRowCells(group, repeatCount, cssClasses, cellTemplate, repeatByDate) { - repeatByDate = repeatByDate || 1; - repeatCount *= repeatByDate; - - const cells: any[] = []; - const { items } = group; - const itemCount = items.length; - - for (let i = 0; i < repeatCount; i++) { - for (let j = 0; j < itemCount; j++) { - let $container = $('
'); - const cell: any = {}; - - if (cellTemplate && cellTemplate.render) { - const templateOptions = { - model: items[j], - container: getPublicElement($container), - index: i * itemCount + j, - }; - - if (group.data) { - templateOptions.model.data = group.data[j]; - } - - cell.template = cellTemplate.render.bind(cellTemplate, templateOptions); - } else { - $container.text(items[j].text); - $container = $('
').append($container); - } - - $container.addClass(cssClasses.groupHeaderContentClass); - - let cssClass; - - if (isFunction(cssClasses.groupHeaderClass)) { - cssClass = cssClasses.groupHeaderClass(j); - } else { - cssClass = cssClasses.groupHeaderClass; - } - - cell.element = $('').addClass(cssClass).append($container); - - cells.push(cell); - } - } - - return cells; - } -} - -export default { - tableCreator: new SchedulerTableCreator(), -}; diff --git a/packages/devextreme/js/__internal/scheduler/table_creator.ts b/packages/devextreme/js/__internal/scheduler/table_creator.ts new file mode 100644 index 000000000000..77e27ce5ed6b --- /dev/null +++ b/packages/devextreme/js/__internal/scheduler/table_creator.ts @@ -0,0 +1,553 @@ +import type { Orientation } from '@js/common'; +import domAdapter from '@js/core/dom_adapter'; +import { getPublicElement } from '@js/core/element'; +import { data as elementData } from '@js/core/element_data'; +import type { dxElementWrapper } from '@js/core/renderer'; +import $ from '@js/core/renderer'; +import type { TemplateBase } from '@js/core/templates/template_base'; +import { isFunction } from '@js/core/utils/type'; + +import type { ResourceLoader } from './utils/loader/resource_loader'; +import type { GroupNode } from './utils/resource_manager/types'; + +const ROW_SELECTOR = 'tr'; + +type TemplateCallback = () => dxElementWrapper; + +interface CellData { + startDate?: Date; + endDate?: Date; + groups?: Record; + allDay?: boolean; +} + +export interface MakeTableOptions { + container: HTMLElement | dxElementWrapper; + rowCount: number; + cellCount: number; + groupCount?: number; + groupIndex?: number; + rowClass?: string; + cellClass?: string | ((rowIndex: number, columnIndex: number) => string); + getCellTextClass?: string; + allDayElements?: dxElementWrapper[]; + getCellData?: ( + cell: HTMLElement, + rowIndex: number, + columnIndex: number, + groupIndex: number | undefined, + ) => { key: string; value: CellData | undefined }; + setAdditionalClasses?: ( + $cell: dxElementWrapper, + dataValue: CellData | undefined, + ) => void; + cellTemplate?: TemplateBase | null; + getCellText?: (rowIndex: number, columnIndex: number) => string; + getCellDate?: (rowIndex: number) => Date | undefined; + getTemplateData?: (rowIndex: number) => Record; +} + +export interface GroupCssClasses { + groupHeaderRowClass?: string; + groupRowClass?: string; + groupHeaderClass: string | ((index: number) => string); + groupHeaderContentClass?: string; +} + +export interface GroupRows { + elements: dxElementWrapper | dxElementWrapper[]; + cellTemplates: TemplateCallback[]; +} + +interface GroupedTableConfig { + cellTag?: string; + groupTableClass?: string; + groupRowClass?: string; + groupCellClass?: string; + groupCellCustomContent?: ( + cell: HTMLElement, + cellText: Text, + index: number, + node: GroupNode, + ) => void; +} + +interface TableCell { + element: HTMLElement; + childCount: number; +} + +type TableRow = TableCell[]; + +interface GroupCell { + element: dxElementWrapper; + template?: TemplateCallback; +} + +type GroupCellRow = GroupCell[]; + +class SchedulerTableCreator { + readonly VERTICAL = 'vertical'; + + readonly HORIZONTAL = 'horizontal'; + + insertAllDayRow( + allDayElements: dxElementWrapper[], + tableBody: HTMLElement, + index: number, + ): void { + if (allDayElements[index]) { + let row = allDayElements[index].find(ROW_SELECTOR); + + if (!row.length) { + row = $(domAdapter.createElement(ROW_SELECTOR)); + row.append(allDayElements[index].get(0)); + } + + tableBody.appendChild(row.get(0) as HTMLElement); + } + } + + makeTable(options: MakeTableOptions): TemplateCallback[] { + const tableBody = domAdapter.createElement('tbody'); + const templateCallbacks: TemplateCallback[] = []; + const rowCountInGroup = options.groupCount + ? options.rowCount / options.groupCount + : options.rowCount; + let allDayElementIndex = 0; + const { allDayElements } = options; + const { groupIndex } = options; + const { rowCount } = options; + + $(options.container).append(tableBody); + + if (allDayElements) { + this.insertAllDayRow(allDayElements, tableBody, 0); + allDayElementIndex += 1; + } + + for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) { + const row = domAdapter.createElement(ROW_SELECTOR); + tableBody.appendChild(row); + + const isLastRowInGroup = (rowIndex + 1) % rowCountInGroup === 0; + + if (options.rowClass) { + row.className = options.rowClass; + } + + for (let columnIndex = 0; columnIndex < options.cellCount; columnIndex += 1) { + const td = domAdapter.createElement('td'); + row.appendChild(td); + + if (options.cellClass) { + td.className = isFunction(options.cellClass) + ? options.cellClass(rowIndex, columnIndex) + : options.cellClass; + } + + const cellDataObject = options.getCellData?.(td, rowIndex, columnIndex, groupIndex); + const dataValue = cellDataObject?.value; + + if (cellDataObject?.key) { + elementData(td, cellDataObject.key, dataValue); + } + + options.setAdditionalClasses?.($(td), dataValue); + + if (options.cellTemplate?.render) { + const additionalTemplateData = options.getTemplateData + ? options.getTemplateData(rowIndex) + : {}; + + const model: Record = { + text: options.getCellText + ? options.getCellText(rowIndex, columnIndex) + : '', + date: options.getCellDate + ? options.getCellDate(rowIndex) + : undefined, + ...additionalTemplateData, + }; + + /* eslint-disable max-depth */ + if (dataValue) { + if (dataValue.startDate) { model.startDate = dataValue.startDate; } + if (dataValue.endDate) { model.endDate = dataValue.endDate; } + if (dataValue.groups) { model.groups = dataValue.groups; } + if (dataValue.allDay) { model.allDay = dataValue.allDay; } + } + /* eslint-enable max-depth */ + + templateCallbacks.push(options.cellTemplate.render.bind( + options.cellTemplate, + { + model, + container: getPublicElement($(td)), + index: rowIndex * options.cellCount + columnIndex, + }, + )); + } else if (options.getCellText) { + $('
') + .text(options.getCellText(rowIndex, columnIndex)) + .addClass(options.getCellTextClass ?? '') + .appendTo($(td)); + } + } + + if (allDayElements && isLastRowInGroup) { + this.insertAllDayRow(allDayElements, tableBody, allDayElementIndex); + allDayElementIndex += 1; + } + } + + return templateCallbacks; + } + + makeGroupedTable( + type: Orientation, + groups: ResourceLoader[], + cssClasses: GroupCssClasses, + cellCount: number, + cellTemplate: TemplateBase | null | undefined, + rowCount: number, + groupByDate: boolean, + ): GroupRows { + if (type === this.VERTICAL) { + return this.makeVerticalGroupedRows(groups, cssClasses, cellTemplate, rowCount); + } + + return this.makeHorizontalGroupedRows(groups, cssClasses, cellCount, cellTemplate, groupByDate); + } + + makeGroupedTableFromJSON( + tree: GroupNode[], + tableConfig?: GroupedTableConfig, + ): HTMLElement { + const config = tableConfig || {}; + const cellTag = config.cellTag || 'td'; + const { groupTableClass } = config; + const { groupRowClass } = config; + const { groupCellClass } = config; + const { groupCellCustomContent } = config; + + const cellStorage: TableRow[] = []; + let rowIndex = 0; + + const table = domAdapter.createElement('table'); + if (groupTableClass) { + table.className = groupTableClass; + } + + const getChildCount = (item: GroupNode): number => { + if (item.children) { + return item.children.length; + } + return 0; + }; + + const createCell = ( + text: string, + childCount: number, + index: number, + node: GroupNode, + ): TableCell => { + const cell: TableCell = { + element: domAdapter.createElement(cellTag), + childCount, + }; + + if (groupCellClass) { + cell.element.className = groupCellClass; + } + + const cellText = domAdapter.createTextNode(text) as Text; + if (typeof groupCellCustomContent === 'function') { + groupCellCustomContent(cell.element, cellText, index, node); + } else { + cell.element.appendChild(cellText); + } + + return cell; + }; + + const generateCells = (groupNodes: GroupNode[]): void => { + for (let i = 0; i < groupNodes.length; i += 1) { + const childCount = getChildCount(groupNodes[i]); + const cell = createCell( + groupNodes[i].resourceText, + childCount, + i, + groupNodes[i], + ); + + if (!cellStorage[rowIndex]) { + cellStorage[rowIndex] = []; + } + cellStorage[rowIndex].push(cell); + + if (childCount) { + generateCells(groupNodes[i].children); + } else { + rowIndex += 1; + } + } + }; + + const putCellsToRows = (): void => { + cellStorage.forEach((cells) => { + const row = domAdapter.createElement(ROW_SELECTOR); + if (groupRowClass) { + row.className = groupRowClass; + } + + const rowspans: number[] = []; + + for (let i = cells.length - 1; i >= 0; i -= 1) { + const prev = cells[i + 1]; + let rowspan = cells[i].childCount; + if (prev && prev.childCount) { + rowspan *= prev.childCount; + } + rowspans.push(rowspan); + } + rowspans.reverse(); + + cells.forEach((cell, index) => { + if (rowspans[index]) { + cell.element.setAttribute('rowSpan', String(rowspans[index])); + } + row.appendChild(cell.element); + }); + + table.appendChild(row); + }); + }; + + generateCells(tree); + putCellsToRows(); + + return table; + } + + private makeFlexGroupedRowCells( + group: ResourceLoader, + repeatCount: number, + cssClasses: GroupCssClasses, + cellTemplate: TemplateBase | null | undefined, + repeatByDate = 1, + ): GroupCell[] { + const cells: GroupCell[] = []; + const { items } = group; + const itemCount = items.length; + + for (let i = 0; i < repeatCount * repeatByDate; i += 1) { + for (let j = 0; j < itemCount; j += 1) { + let $container = $('
'); + let boundTemplate: TemplateCallback | null = null; + + if (cellTemplate?.render) { + const model = group.data + ? Object.assign(items[j], { data: group.data[j] }) + : items[j]; + const templateOptions = { + model, + container: getPublicElement($container), + index: i * itemCount + j, + }; + + boundTemplate = cellTemplate.render.bind(cellTemplate, templateOptions); + } else { + $container + .text(items[j].text) + .attr('title', items[j].text) + .addClass('dx-scheduler-group-header-content'); + $container = $('
').append($container); + } + + const cssClass = isFunction(cssClasses.groupHeaderClass) + ? cssClasses.groupHeaderClass(j) + : cssClasses.groupHeaderClass; + + const cell: GroupCell = { element: $container.addClass(cssClass) }; + if (boundTemplate) { + cell.template = boundTemplate; + } + + cells.push(cell); + } + } + + return cells; + } + + private makeVerticalGroupedRows( + groups: ResourceLoader[], + cssClasses: GroupCssClasses, + cellTemplate: TemplateBase | null | undefined, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + rowCount?: number, + ): GroupRows { + const cellTemplates: TemplateCallback[] = []; + let repeatCount = 1; + const cellsArray: GroupCellRow[] = []; + + const cellIterator = (cell: GroupCell): void => { + if (cell.template) { + cellTemplates.push(cell.template); + } + }; + + for (let i = 0; i < groups.length; i += 1) { + if (i > 0) { + repeatCount *= groups[i - 1].items.length; + } + + const cells = this.makeFlexGroupedRowCells(groups[i], repeatCount, cssClasses, cellTemplate); + cells.forEach(cellIterator); + cellsArray.push(cells); + } + + const rows: dxElementWrapper[] = []; + const groupCount = cellsArray.length; + + for (let i = 0; i < groupCount; i += 1) { + rows.push($('
').addClass(cssClasses.groupHeaderRowClass ?? '')); + } + + for (let i = groupCount - 1; i >= 0; i -= 1) { + const currentColumnLength = cellsArray[i].length; + for (let j = 0; j < currentColumnLength; j += 1) { + rows[i].append(cellsArray[i][j].element); + } + } + + return { + elements: $('
') + .addClass('dx-scheduler-group-flex-container') + .append(rows), + cellTemplates, + }; + } + + private makeHorizontalGroupedRows( + groups: ResourceLoader[], + cssClasses: GroupCssClasses, + cellCount: number, + cellTemplate: TemplateBase | null | undefined, + groupByDate: boolean, + ): GroupRows { + let repeatCount = 1; + const groupCount = groups.length; + const rows: dxElementWrapper[] = []; + const cellTemplates: TemplateCallback[] = []; + const repeatByDate = groupByDate ? cellCount : 1; + + const cellIterator = (cell: GroupCell): dxElementWrapper => { + if (cell.template) { + cellTemplates.push(cell.template); + } + + return cell.element; + }; + + for (let i = 0; i < groupCount; i += 1) { + if (i > 0) { + repeatCount *= groups[i - 1].items.length; + } + + const cells = this.makeGroupedRowCells( + groups[i], + repeatCount, + cssClasses, + cellTemplate, + repeatByDate, + ); + + rows.push( + $('') + .addClass(cssClasses.groupRowClass ?? '') + .append(cells.map(cellIterator)), + ); + } + + const maxCellCount = rows[groupCount - 1].find('th').length; + + for (let j = 0; j < groupCount; j += 1) { + const $cell = rows[j].find('th'); + let colspan = maxCellCount / $cell.length; + + if (!groupByDate) { + colspan *= cellCount; + } + if ( + (colspan > 1 && repeatByDate === 1) + || (groupByDate && groupCount > 1) + ) { + $cell.attr('colSpan', colspan); + } + } + + return { + elements: rows, + cellTemplates, + }; + } + + private makeGroupedRowCells( + group: ResourceLoader, + baseRepeatCount: number, + cssClasses: GroupCssClasses, + cellTemplate: TemplateBase | null | undefined, + baseRepeatByDate: number, + ): GroupCell[] { + const effectiveRepeatByDate = baseRepeatByDate || 1; + const totalRepeatCount = baseRepeatCount * effectiveRepeatByDate; + + const cells: GroupCell[] = []; + const { items } = group; + const itemCount = items.length; + + for (let i = 0; i < totalRepeatCount; i += 1) { + for (let j = 0; j < itemCount; j += 1) { + let $container = $('
'); + let boundTemplate: TemplateCallback | null = null; + + if (cellTemplate?.render) { + const model = group.data + ? Object.assign(items[j], { data: group.data[j] }) + : items[j]; + const templateOptions = { + model, + container: getPublicElement($container), + index: i * itemCount + j, + }; + + boundTemplate = cellTemplate.render.bind(cellTemplate, templateOptions); + } else { + $container.text(items[j].text); + $container = $('
').append($container); + } + + $container.addClass(cssClasses.groupHeaderContentClass ?? ''); + + const cssClass = isFunction(cssClasses.groupHeaderClass) + ? cssClasses.groupHeaderClass(j) + : cssClasses.groupHeaderClass; + + const cell: GroupCell = { element: $('').addClass(cssClass).append($container) }; + if (boundTemplate) { + cell.template = boundTemplate; + } + + cells.push(cell); + } + } + + return cells; + } +} + +export default { + tableCreator: new SchedulerTableCreator(), +}; diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/agenda.ts b/packages/devextreme/js/__internal/scheduler/workspaces/agenda.ts index 42f5e5b066a9..654066de8ec0 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/agenda.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/agenda.ts @@ -20,8 +20,8 @@ import { GROUP_ROW_CLASS, TIME_PANEL_CLASS, } from '../classes'; -import tableCreatorModule, { type GroupRows } from '../m_table_creator'; import { agendaUtils, formatWeekday, getVerticalGroupCountClass } from '../r1/utils/index'; +import tableCreatorModule, { type GroupRows } from '../table_creator'; import type { ResourceId } from '../utils/loader/types'; import { VIEWS } from '../utils/options/constants_view'; import { reduceResourcesTree } from '../utils/resource_manager/agenda_group_utils'; @@ -262,8 +262,8 @@ class SchedulerAgenda extends WorkSpace { groupRowClass: GROUP_ROW_CLASS, groupCellClass: this.getGroupHeaderClass(), groupCellCustomContent( - cell: HTMLDivElement, - cellTextElement: HTMLElement, + cell: HTMLElement, + cellTextElement: Text, index: number, node: GroupNode, ) { @@ -295,7 +295,6 @@ class SchedulerAgenda extends WorkSpace { cell.appendChild(container); }, - cellTemplate, }); return { diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts index 90884d0a8a8e..08bd8e31304c 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts @@ -72,8 +72,8 @@ import { Cache } from '../global_cache'; import AppointmentDragBehavior from '../m_appointment_drag_behavior'; import { CompactAppointmentsHelper } from '../m_compact_appointments_helper'; import type { SubscribeKey, SubscribeMethods } from '../m_subscribes'; -import tableCreatorModule, { type GroupRows } from '../m_table_creator'; import VerticalShader from '../shaders/current_time_shader_vertical'; +import tableCreatorModule, { type GroupRows } from '../table_creator'; import type { ViewCellData } from '../types'; import { utils } from '../utils'; import type { ResourceLoader } from '../utils/loader/resource_loader'; diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/timeline.ts b/packages/devextreme/js/__internal/scheduler/workspaces/timeline.ts index a847ee8da4a0..56d2e19fd3f6 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/timeline.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/timeline.ts @@ -16,8 +16,8 @@ import { GROUP_HEADER_CONTENT_CLASS, GROUP_ROW_CLASS, } from '../classes'; -import tableCreatorModule, { type GroupRows } from '../m_table_creator'; import HorizontalShader from '../shaders/current_time_shader_horizontal'; +import tableCreatorModule, { type GroupRows } from '../table_creator'; import type { ResourceLoader } from '../utils/loader/resource_loader'; import { getFirstVisibleDate } from '../utils/skipped_days'; import timezoneUtils from '../utils_time_zone'; diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/tableCreator.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/tableCreator.tests.js index 3bc128b26854..c90c1f942c8d 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/tableCreator.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/tableCreator.tests.js @@ -1,4 +1,4 @@ -import tableCreatorModule from '__internal/scheduler/m_table_creator'; +import tableCreatorModule from '__internal/scheduler/table_creator'; const { tableCreator } = tableCreatorModule;