@progress/kendo-angular-grid
Version:
Kendo UI Grid for Angular - high performance data grid with paging, filtering, virtualization, CRUD, and more.
961 lines (959 loc) • 59.2 kB
JavaScript
/**-----------------------------------------------------------------------------------------
* Copyright © 2025 Progress Software Corporation. All rights reserved.
* Licensed under commercial license. See LICENSE.md in the project root for more information
*-------------------------------------------------------------------------------------------*/
import { nodesToArray } from './../utils';
import { Component, Input, NgZone, Renderer2, ElementRef, TemplateRef, HostBinding } from '@angular/core';
import { DetailTemplateDirective } from './details/detail-template.directive';
import { GroupsService } from '../grouping/groups.service';
import { ChangeNotificationService } from '../data/change-notification.service';
import { isChanged, isPresent } from '../utils';
import { NoRecordsTemplateDirective } from './no-records-template.directive';
import { EditService } from '../editing/edit.service';
import { columnsSpan, columnsToRender } from "../columns/column-common";
import { closest, closestInScope, findElement, hasClasses, isFocusableWithTabKey, matchesClasses, matchesNodeName } from './common/dom-queries';
import { DomEventsService } from '../common/dom-events.service';
import { SelectionService } from "../selection/selection.service";
import { ColumnInfoService } from "../common/column-info.service";
import { hasFilterRow } from '../filtering/filterable';
import { NavigationService } from '../navigation/navigation.service';
import { isDocumentAvailable, Keys, ResizeSensorComponent, TemplateContextDirective } from '@progress/kendo-angular-common';
import { defaultTrackBy } from '../common/default-track-by';
import { DetailsService } from './details/details.service';
import { NON_DATA_CELL_CLASSES, NON_DATA_ROW_CLASSES, IGNORE_TARGET_CLASSSES, IGNORE_CONTAINER_CLASSES } from './constants';
import { CellSelectionService } from '../selection/cell-selection.service';
import { minusIcon, plusIcon } from '@progress/kendo-svg-icons';
import { ContextService } from '../common/provider.service';
import { ColumnsContainer } from '../columns/columns-container';
import { SpanColumnComponent } from '../columns/span-column.component';
import { IconWrapperComponent } from '@progress/kendo-angular-icons';
import { GroupHeaderComponent } from '../grouping/group-header.component';
import { CellComponent } from './cell.component';
import { LogicalCellDirective } from '../navigation/logical-cell.directive';
import { LogicalRowDirective } from '../navigation/logical-row.directive';
import { NgIf, NgFor, NgClass, NgStyle, NgTemplateOutlet } from '@angular/common';
import * as i0 from "@angular/core";
import * as i1 from "./details/details.service";
import * as i2 from "../grouping/groups.service";
import * as i3 from "../data/change-notification.service";
import * as i4 from "../editing/edit.service";
import * as i5 from "../common/provider.service";
import * as i6 from "../common/dom-events.service";
import * as i7 from "../selection/selection.service";
import * as i8 from "../selection/cell-selection.service";
import * as i9 from "../common/column-info.service";
import * as i10 from "../navigation/navigation.service";
const columnCellIndex = (cell, cells, isStacked) => {
let cellIndex = 0;
for (let idx = 0; idx < cells.length; idx++) {
const isChildCell = cell.parentNode === cells[idx];
const isActualCell = cell === cells[idx];
const matches = isActualCell || (isStacked && isChildCell);
if (matches) {
return cellIndex;
}
if (!hasClasses(cells[idx], 'k-hierarchy-cell k-group-cell')) {
cellIndex++;
}
}
};
/**
* @hidden
*/
export class TableBodyComponent {
detailsService;
groupsService;
changeNotification;
editService;
ctx;
ngZone;
renderer;
element;
domEvents;
selectionService;
cellSelectionService;
columnInfoService;
navigationService;
columns = [];
allColumns;
groups = [];
detailTemplate;
noRecordsTemplate;
rowsToRender;
skip = 0;
selectable;
filterable;
noRecordsText;
isLocked = false;
isLoading;
isVirtual;
cellLoadingTemplate;
skipGroupDecoration = false;
lockedColumnsCount = 0;
totalColumnsCount = 0;
virtualColumns;
trackBy = defaultTrackBy;
rowSticky;
totalColumns;
rowClass = () => null;
hostClass = true;
groupHeaderSlaveCellsCount;
groupHeaderColumns;
clickSubscription;
touchSubscription;
l10nSubscription;
cellKeydownSubscription;
clickTimeout;
minusIcon = minusIcon;
plusIcon = plusIcon;
constructor(detailsService, groupsService, changeNotification, editService, ctx, ngZone, renderer, element, domEvents, selectionService, cellSelectionService, columnInfoService, navigationService) {
this.detailsService = detailsService;
this.groupsService = groupsService;
this.changeNotification = changeNotification;
this.editService = editService;
this.ctx = ctx;
this.ngZone = ngZone;
this.renderer = renderer;
this.element = element;
this.domEvents = domEvents;
this.selectionService = selectionService;
this.cellSelectionService = cellSelectionService;
this.columnInfoService = columnInfoService;
this.navigationService = navigationService;
this.noRecordsText = this.ctx.localization.get('noRecords');
this.cellKeydownSubscription = this.navigationService.cellKeydown.subscribe((args) => this.cellKeydownHandler(args));
this.trackByWrapper = this.trackByWrapper.bind(this);
this.trackByColumns = this.trackByColumns.bind(this);
}
get newDataItem() {
return this.editService.newDataItem;
}
// Number of unlocked columns in the next table, if any
unlockedColumnsCount(item) {
const allColumns = this.allColumns || this.columns;
let allColumnsCount = allColumns.length;
allColumns.forEach(column => {
if (column.isSpanColumn) {
allColumnsCount += column.colspan - 1;
}
});
const contentColumnsCount = this.totalColumnsCount - (this.isStackedMode ? 0 : this.lockedColumnsCount) - allColumnsCount;
const headerFooterColumnsCount = this.totalColumnsCount - (this.isStackedMode ? 0 : this.lockedColumnsCount) - allColumns.length;
return item && item.type === 'data' ? contentColumnsCount : headerFooterColumnsCount;
}
isAriaSelected(item, column) {
return this.cellSelectionService.isCellSelected(item, column) ||
this.isRowSelected(item) ? 'true' : 'false';
}
toggleRow(index, dataItem) {
this.detailsService.toggleRow(index, dataItem);
return false;
}
detailButtonIconName(viewItem) {
return viewItem.isExpanded ? 'minus' : 'plus';
}
detailButtonSvgIcon(viewItem) {
return viewItem.isExpanded ? this.minusIcon : this.plusIcon;
}
detailButtonTitle(viewItem) {
const messageKey = viewItem.isExpanded ? 'detailCollapse' : 'detailExpand';
return this.ctx.localization.get(messageKey);
}
isOdd(item) {
return item.index % 2 !== 0;
}
isSelectable(args) {
const rowSelectable = this.isRowSelectable(args);
const selectionEnabled = this.selectable && this.selectable.enabled !== false;
return selectionEnabled && rowSelectable;
}
isRowSelected(item) {
return this.selectionService.isSelected(item.index);
}
isRowSelectable(args) {
return this.selectionService.settings?.isRowSelectable(args) || this.cellSelectionService.settings?.isRowSelectable(args);
}
trackByWrapper(index, item) {
if (item.type === 'data') {
item.isEditing = this.editService.hasEdited(item.index);
}
return this.trackBy(index, item);
}
trackByColumns(index, item) {
return this.virtualColumns ? index : item;
}
ngDoCheck() {
if (this.hasGroupHeaderColumn) {
this.groupHeaderColumns = columnsToRender(this.skipGroupDecoration ? this.columns : this.columns.toArray().slice(1));
}
else {
this.groupHeaderColumns = [];
}
if (this.isLocked) {
this.groupHeaderSlaveCellsCount =
this.hasGroupHeaderColumn ? this.columnsContainer.nonLockedColumnsToRender.length : 1;
}
else {
this.groupHeaderSlaveCellsCount = 0;
}
}
ngAfterViewChecked() {
if (this.rowSticky) {
this.applyStickyRowsStyling();
}
}
ngOnChanges(changes) {
if (isChanged('columns', changes, false)) {
this.changeNotification.notify();
}
}
logicalRowIndex(rowIndex) {
let pos = this.skip + rowIndex;
if (this.hasDetailTemplate && !this.isStackedMode) {
pos *= 2;
}
const absoluteRowIndex = this.isStackedMode ? pos : 1 + pos;
const addRowOffset = this.editService.hasNewItem ? 1 : 0;
const filterRowOffset = hasFilterRow(this.filterable) ? 1 : 0;
const headerRowCount = this.columnInfoService.totalLevels + filterRowOffset + addRowOffset;
return absoluteRowIndex + headerRowCount;
}
addRowLogicalIndex() {
return this.columnInfoService.totalLevels + 1 +
(hasFilterRow(this.filterable) ? 1 : 0);
}
logicalColIndex(column) {
if (!isPresent(column.leafIndex)) {
return -1;
}
return column.leafIndex + (this.hasDetailTemplate ? 1 : 0);
}
ngOnInit() {
this.ngZone.runOutsideAngular(() => {
const clickHandler = this.clickHandler.bind(this);
const mousedownSubscription = this.renderer.listen(this.element.nativeElement, 'mousedown', clickHandler);
const mouseupSubscription = this.renderer.listen(this.element.nativeElement, 'mouseup', clickHandler);
const clickSubscription = this.renderer.listen(this.element.nativeElement, 'click', clickHandler);
const contextmenuSubscription = this.renderer.listen(this.element.nativeElement, 'contextmenu', clickHandler);
const touchstartSubscription = this.renderer.listen(this.element.nativeElement, 'touchstart', clickHandler);
const touchendSubscription = this.renderer.listen(this.element.nativeElement, 'touchend', clickHandler);
this.clickSubscription = () => {
mousedownSubscription();
mouseupSubscription();
clickSubscription();
contextmenuSubscription();
};
this.touchSubscription = () => {
touchstartSubscription();
touchendSubscription();
};
});
let originalNoRecordText = this.ctx.localization.get('noRecords');
this.l10nSubscription = this.ctx.localization.changes.subscribe(() => {
if (this.noRecordsText === originalNoRecordText) {
this.noRecordsText = this.ctx.localization.get('noRecords');
originalNoRecordText = this.noRecordsText;
}
});
}
ngOnDestroy() {
if (this.clickSubscription) {
this.clickSubscription();
}
if (this.touchSubscription) {
this.touchSubscription();
}
if (this.l10nSubscription) {
this.l10nSubscription.unsubscribe();
}
this.cellKeydownSubscription.unsubscribe();
clearTimeout(this.clickTimeout);
}
isEditingCell(index, column) {
return this.editService.isEditing() && this.editService.isEditedColumn(index, column);
}
isEditingRow(index) {
return this.editService.isEditing() && this.editService.hasEdited(index);
}
get isStackedMode() {
return this.ctx.grid?.isStacked;
}
get hasGroupHeaderColumn() {
return this.columnsContainer.hasGroupHeaderColumn;
}
get columnsContainer() {
return this.columnInfoService.columnsContainer;
}
get columnsSpan() {
return columnsSpan(this.columns);
}
get allColumnsSpan() {
return columnsSpan(this.allColumns || this.columns);
}
get colSpan() {
return this.columnsSpan + this.groups.length + (this.hasDetailTemplate ? 1 : 0);
}
get footerColumns() {
const colsToRender = Array.from(this.columns).reduce((cols, col) => {
const newCols = (col instanceof SpanColumnComponent) ? Array.from(col.children) : [col];
return [...cols, ...newCols];
}, []);
return colsToRender;
}
showGroupHeader(item) {
return !item.data.skipHeader;
}
addStickyColumnStyles(column) {
const stickyStyles = this.columnInfoService.stickyColumnsStyles(column);
return { ...column.style, ...stickyStyles };
}
resizeHandler() {
this.applyStickyRowsStyling();
}
get hasDetailTemplate() {
return isPresent(this.detailTemplate);
}
clickHandler(eventArg) {
const element = this.element.nativeElement;
const target = this.eventTarget(eventArg);
const selectionEnabled = this.selectable && this.selectable.enabled !== false;
if (eventArg.code === Keys.Space) {
if (!selectionEnabled) {
return;
}
const cellComparer = this.isStackedMode ? closest(target, matchesNodeName('td')) : closest(target, (el) => { matchesNodeName('td') && hasClasses(el, 'k-grid-stack-content'); });
const isCellFocused = cellComparer?.classList.contains('k-focus');
const isShiftOrCtrlPressed = eventArg.shiftKey || eventArg.ctrlKey || eventArg.metaKey;
if (isCellFocused && !isShiftOrCtrlPressed) {
eventArg.preventDefault();
}
}
let row, body, gridElement;
const currentTarget = target;
const gridTbody = closest(currentTarget, (el) => matchesNodeName('tbody')(el) && el === element);
if (!gridTbody) {
return;
}
let cell = closest(currentTarget, (el) => {
if (!matchesNodeName('td')(el)) {
return false;
}
const parentRow = el.parentElement;
if (!parentRow || !matchesNodeName('tr')(parentRow)) {
return false;
}
return parentRow.parentElement === element;
});
if (cell) {
row = cell.parentElement;
body = element;
gridElement = null;
}
if (cell && !hasClasses(cell, NON_DATA_CELL_CLASSES) &&
!hasClasses(row, NON_DATA_ROW_CLASSES) &&
body === element && !gridElement) {
const isCellTarget = hasClasses(currentTarget, 'k-grid-stack-cell');
if (this.isStackedMode) {
cell = closest(currentTarget, (el) => {
if (!closestInScope(target, matchesClasses('k-grid-stack-row'), cell)) {
return false;
}
const parentRow = isCellTarget ? currentTarget : el.parentElement;
return (parentRow && hasClasses(parentRow, 'k-grid-stack-cell'));
});
if (isCellTarget) {
cell = findElement(currentTarget, (el) => hasClasses(el, 'k-grid-stack-content'));
}
if (cell) {
row = cell.parentElement.parentElement;
}
}
this.editService.preventCellClose();
const focusable = (!isCellTarget && target !== cell) && isFocusableWithTabKey(target, false);
const ignoreContainer = isCellTarget ? true : !closestInScope(target, matchesClasses(IGNORE_CONTAINER_CLASSES), cell);
if (!focusable && !matchesNodeName('label')(target) && !hasClasses(target, IGNORE_TARGET_CLASSSES) && ignoreContainer) {
const args = this.cellClickArgs(cell, row, eventArg);
if (!args) {
return;
}
if (selectionEnabled && !this.isRowSelectable({ index: args.rowIndex, dataItem: args.dataItem })) {
return;
}
if (eventArg.type === 'mousedown' || eventArg.type === 'touchstart') {
this.domEvents.cellMousedown.emit(args);
}
else if (eventArg.type === 'mouseup' || eventArg.type === 'touchend') {
this.domEvents.cellMouseup.emit(args);
}
else {
if (args.isEditedColumn || !this.editService.closeCell(eventArg)) {
if (eventArg.type === 'click') {
this.clickTimeout = setTimeout(() => {
this.emitCellClick(args);
}, 0);
}
else {
this.emitCellClick(args);
}
}
}
}
}
}
emitCellClick(args) {
this.domEvents.cellClick.emit(Object.assign(args, {
isEdited: args.isEditedRow || args.isEditedColumn
}));
}
cellKeydownHandler(args) {
if (args.code === Keys.Enter || args.code === Keys.NumpadEnter || args.code === Keys.Space || (this.navigationService.tableCellEntered && args.code === Keys.F2)) {
this.clickHandler(args);
}
}
cellClickArgs(cell, row, eventArg) {
const cells = this.isStackedMode ? row.querySelectorAll('.k-grid-stack-cell') : row.cells;
const index = columnCellIndex(cell, cells, this.isStackedMode);
if (!isPresent(index)) {
return;
}
const column = this.columns.toArray()[index];
const columnIndex = (this.isStackedMode ? 0 : this.lockedColumnsCount) + index;
let rowIndex = (this.isStackedMode ? row.parentElement.parentElement : row).getAttribute('data-kendo-grid-item-index');
rowIndex = rowIndex ? parseInt(rowIndex, 10) : -1;
const dataItem = rowIndex === -1 ? this.editService.newDataItem : this.rowsToRender.find(item => +item.index === rowIndex && item.type === 'data')?.data;
const isEditedColumn = this.editService.isEditedColumn(rowIndex, column);
const isEditedRow = this.editService.isEdited(rowIndex);
const type = eventArg.type === 'keydown' ? 'click' : eventArg.type;
return {
column: column,
columnIndex: columnIndex,
dataItem: dataItem,
isEditedColumn: isEditedColumn,
isEditedRow: isEditedRow,
originalEvent: eventArg,
rowIndex: rowIndex,
type: type
};
}
eventTarget(args) {
if (!isDocumentAvailable()) {
return;
}
if (args.type === 'touchend') {
const touch = args.changedTouches[0];
return document.elementFromPoint(touch.clientX, touch.clientY);
}
return args.target;
}
applyStickyRowsStyling() {
if (!isDocumentAvailable()) {
return;
}
const stickyRows = nodesToArray(this.element.nativeElement.querySelectorAll('.k-grid-row-sticky'));
const length = stickyRows.length;
if (length) {
let accumulatedHeight = 0;
const stickyRowsOffsets = [];
stickyRows.forEach(row => {
const rowHeight = row.getBoundingClientRect().height;
stickyRowsOffsets.push({ accumulatedHeight, rowHeight });
accumulatedHeight += rowHeight;
});
stickyRows.forEach((row, index) => {
this.renderer.setStyle(row, 'top', `${stickyRowsOffsets[index].accumulatedHeight}px`);
this.renderer.setStyle(row, 'bottom', `${accumulatedHeight - stickyRowsOffsets[index].accumulatedHeight - stickyRowsOffsets[index].rowHeight}px`);
});
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TableBodyComponent, deps: [{ token: i1.DetailsService }, { token: i2.GroupsService }, { token: i3.ChangeNotificationService }, { token: i4.EditService }, { token: i5.ContextService }, { token: i0.NgZone }, { token: i0.Renderer2 }, { token: i0.ElementRef }, { token: i6.DomEventsService }, { token: i7.SelectionService }, { token: i8.CellSelectionService }, { token: i9.ColumnInfoService }, { token: i10.NavigationService }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: TableBodyComponent, isStandalone: true, selector: "[kendoGridTableBody]", inputs: { columns: "columns", allColumns: "allColumns", groups: "groups", detailTemplate: "detailTemplate", noRecordsTemplate: "noRecordsTemplate", rowsToRender: "rowsToRender", skip: "skip", selectable: "selectable", filterable: "filterable", noRecordsText: "noRecordsText", isLocked: "isLocked", isLoading: "isLoading", isVirtual: "isVirtual", cellLoadingTemplate: "cellLoadingTemplate", skipGroupDecoration: "skipGroupDecoration", lockedColumnsCount: "lockedColumnsCount", totalColumnsCount: "totalColumnsCount", virtualColumns: "virtualColumns", trackBy: "trackBy", rowSticky: "rowSticky", totalColumns: "totalColumns", rowClass: "rowClass" }, host: { properties: { "class.k-table-tbody": "this.hostClass" } }, usesOnChanges: true, ngImport: i0, template: `
<ng-container *ngIf="editService.hasNewItem">
<tr class="k-grid-add-row k-grid-edit-row k-master-row"
kendoGridLogicalRow
[logicalRowIndex]="addRowLogicalIndex()"
[logicalSlaveRow]="lockedColumnsCount > 0 && !isStackedMode"
[logicalCellsCount]="columns.length"
[logicalSlaveCellsCount]="unlockedColumnsCount()"
[totalColumns]="totalColumns">
<ng-container *ngIf="!skipGroupDecoration">
<td class="k-group-cell k-table-td k-table-group-td" *ngFor="let g of groups" role="presentation"></td>
</ng-container>
<td class="k-hierarchy-cell k-table-td"
*ngIf="detailTemplate?.templateRef && !isStackedMode"
kendoGridLogicalCell
[logicalRowIndex]="addRowLogicalIndex()"
[logicalColIndex]="0"
aria-selected="false"
>
</td>
<ng-container *ngIf="isStackedMode; else columnsTemplate">
<td *ngFor="let item of rowsToRender; trackBy: trackByWrapper; let rowIndex = index;"
class="k-table-td"
kendoGridCell
[rowIndex]="-1"
[columnIndex]="0"
[isNew]="true"
[columns]="allColumns"
[dataItem]="newDataItem"
kendoGridLogicalCell
[logicalRowIndex]="addRowLogicalIndex()"
[logicalColIndex]="0">
</td>
</ng-container>
<ng-template #columnsTemplate>
<td *ngFor="let column of columns; let columnIndex = index; trackBy: trackByColumns;"
class="k-table-td"
kendoGridCell
[rowIndex]="-1"
[columnIndex]="lockedColumnsCount + columnIndex"
[isNew]="true"
[column]="column"
[dataItem]="newDataItem"
[class.k-grid-content-sticky]="column.sticky"
[ngClass]="column.cssClass"
[style.left]="column.sticky ? '0' : undefined"
[ngStyle]="column.sticky ? addStickyColumnStyles(column) : column.style"
[attr.colspan]="column.colspan"
[attr.role]="column.tableCellsRole"
kendoGridLogicalCell
[logicalRowIndex]="addRowLogicalIndex()"
[logicalColIndex]="logicalColIndex(column)"
[colSpan]="column.colspan">
</td>
</ng-template>
</tr>
</ng-container>
<tr *ngIf="!rowsToRender?.length" class="k-grid-norecords" role="row">
<td [attr.colspan]="colSpan" class="k-table-td">
<ng-template
*ngIf="noRecordsTemplate?.templateRef"
[templateContext]="{
templateRef: noRecordsTemplate?.templateRef
}">
</ng-template>
<ng-container *ngIf="!noRecordsTemplate?.templateRef">
{{noRecordsText}}
</ng-container>
</td>
</tr>
<ng-container *ngFor="let item of rowsToRender; trackBy: trackByWrapper; let rowIndex = index;">
<tr *ngIf="item.type === 'group'"
kendoGridGroupHeader
[columns]="columns"
[groups]="groups"
[item]="$any(item)"
[hasDetails]="!!detailTemplate?.templateRef"
[skipGroupDecoration]="skipGroupDecoration"
[hasGroupHeaderColumn]="hasGroupHeaderColumn"
[groupHeaderColumns]="groupHeaderColumns"
[rowIndex]="rowIndex + 1"
[totalColumnsCount]="totalColumnsCount"
kendoGridLogicalRow
[logicalRowIndex]="logicalRowIndex(rowIndex)"
[logicalSlaveRow]="lockedColumnsCount > 0 && !isStackedMode"
[totalColumns]="totalColumns"
[logicalCellsCount]="columns.length"
[logicalSlaveCellsCount]="groupHeaderSlaveCellsCount">
</tr>
<tr *ngIf="item.showDataItem"
kendoGridLogicalRow
[dataRowIndex]="$any(item).index"
[dataItem]="item.data"
[logicalRowIndex]="logicalRowIndex(rowIndex)"
[logicalSlaveRow]="lockedColumnsCount > 0 && !isStackedMode"
[totalColumns]="totalColumns"
[logicalCellsCount]="columns.length"
[logicalSlaveCellsCount]="unlockedColumnsCount(item)"
class="{{ isOdd(item) ? 'k-table-alt-row' : ''}}"
[class.k-grid-row-sticky]="rowSticky ? rowSticky({ dataItem: item.data, index: $any(item).index }) : false"
[ngClass]="rowClass({ dataItem: item.data, index: $any(item).index })"
[class.k-master-row]="true"
[class.k-expanded]="item.isExpanded && !isStackedMode"
[class.k-grid-edit-row]="isEditingRow($any(item).index)"
[attr.aria-selected]="(lockedColumnsCount < 1 || isStackedMode) ? isSelectable({ dataItem: item.data, index: $any(item).index }) && isRowSelected(item) : undefined"
[attr.data-kendo-grid-item-index]="$any(item).index"
[class.k-selected]="isSelectable({ dataItem: item.data, index: $any(item).index }) && isRowSelected(item)"
[class.k-highlighted]="item.isHighlighted">
<ng-container *ngIf="!skipGroupDecoration">
<td class="k-group-cell k-table-td k-table-group-td" *ngFor="let g of groups" role="presentation"></td>
</ng-container>
<td class="k-hierarchy-cell k-table-td"
*ngIf="detailTemplate?.templateRef && !isStackedMode"
kendoGridLogicalCell
[logicalRowIndex]="logicalRowIndex(rowIndex)"
[logicalColIndex]="0"
[dataRowIndex]="$any(item).index"
[dataItem]="item.data"
[detailExpandCell]="true"
aria-selected="false"
role="gridcell">
<a
*ngIf="detailTemplate.showIf(item.data, $any(item).index)"
[attr.title]="detailButtonTitle(item)"
[attr.aria-label]="detailButtonTitle(item)"
href="#" tabindex="-1" (click)="toggleRow($any(item).index, item.data)">
<kendo-icon-wrapper
[name]="detailButtonIconName(item)"
[svgIcon]="detailButtonSvgIcon(item)"></kendo-icon-wrapper>
</a>
</td>
<ng-container *ngIf="isStackedMode; else columnsTemplate">
<td kendoGridCell
[rowIndex]="$any(item).index"
[detailTemplate]="detailTemplate"
[item]="item"
[columnIndex]="0"
[attr.data-kendo-grid-column-index]="0"
[columns]="columns"
[dataItem]="item.data"
[isLoading]="isLoading"
[isVirtual]="isVirtual"
[loadingTemplate]="cellLoadingTemplate"
kendoGridLogicalCell
[logicalRowIndex]="logicalRowIndex(rowIndex)"
[logicalColIndex]="0"
[dataRowIndex]="$any(item).index"
[dataItem]="item.data"
[colIndex]="0"
class="k-table-td"
[class.k-touch-action-none]="isSelectable({ dataItem: item.data, index: $any(item).index }) && $any(selectable).drag">
</td>
</ng-container>
<ng-template #columnsTemplate>
<ng-container *ngFor="let column of columns; let columnIndex = index; trackBy: trackByColumns;">
<td *ngIf="!item.cells?.[lockedColumnsCount + columnIndex]?.skip"
kendoGridCell
[rowIndex]="$any(item).index"
[columnIndex]="lockedColumnsCount + columnIndex"
[attr.data-kendo-grid-column-index]="lockedColumnsCount + columnIndex"
[column]="column"
[columns]="allColumns"
[dataItem]="item.data"
[isLoading]="isLoading"
[isVirtual]="isVirtual"
[loadingTemplate]="cellLoadingTemplate"
kendoGridLogicalCell
[logicalRowIndex]="logicalRowIndex(rowIndex)"
[logicalColIndex]="logicalColIndex(column)"
[dataRowIndex]="$any(item).index"
[dataItem]="item.data"
[colIndex]="columnIndex"
[colSpan]="column.colspan"
[rowSpan]="item.cells[lockedColumnsCount + columnIndex]?.rowspan"
[attr.role]="column.tableCellsRole"
class="k-table-td"
[attr.aria-selected]="lockedColumnsCount < 1 && isSelectable({ dataItem: item.data, index: $any(item).index }) ? isAriaSelected(item, column) : undefined"
[class.k-grid-content-sticky]="column.sticky"
[class.k-touch-action-none]="isSelectable({ dataItem: item.data, index: $any(item).index }) && $any(selectable).drag"
[ngClass]="column.cssClass"
[class.k-grid-edit-cell]="isEditingCell($any(item).index, column)"
[ngStyle]="column.sticky ? addStickyColumnStyles(column) : column.style"
[attr.colspan]="column.colspan"
[class.k-selected]="isSelectable && cellSelectionService.isCellSelected(item, column)"
[class.k-highlighted]="item.cells[lockedColumnsCount + columnIndex]?.isHighlighted">
</td>
</ng-container>
</ng-template>
</tr>
<tr *ngIf="item.showDetailRow"
class="k-detail-row"
kendoGridLogicalRow
[dataRowIndex]="$any(item).index"
[dataItem]="item.data"
[logicalRowIndex]="isStackedMode ? logicalRowIndex(rowIndex) : logicalRowIndex(rowIndex) + 1"
[logicalSlaveRow]="false"
[logicalCellsCount]="1">
<ng-container *ngIf="!isStackedMode">
<td class="k-group-cell k-table-td k-table-group-td" *ngFor="let g of groups"></td>
<td class="k-hierarchy-cell k-table-td"></td>
<td class="k-detail-cell k-table-td"
[attr.colspan]="columnsSpan"
kendoGridLogicalCell
[logicalRowIndex]="logicalRowIndex(rowIndex) + 1"
[logicalColIndex]="0"
[dataRowIndex]="$any(item).index"
[dataItem]="item.data"
[colIndex]="0"
[colSpan]="allColumnsSpan + 1"
role="gridcell" aria-selected="false">
<ng-template
[ngTemplateOutlet]="detailTemplate.templateRef"
[ngTemplateOutletContext]="{
dataItem: item.data,
rowIndex: $any(item).index,
$implicit: item.data
}">
</ng-template>
</td>
</ng-container>
<ng-container *ngIf="isStackedMode">
<td class="k-detail-cell k-table-td"
[attr.colspan]="columnsSpan"
kendoGridLogicalCell
[logicalRowIndex]="logicalRowIndex(rowIndex)"
[logicalColIndex]="0"
[dataRowIndex]="$any(item).index"
[dataItem]="item.data"
[colIndex]="0"
[colSpan]="allColumnsSpan + 1"
role="gridcell"
aria-selected="false">
<ng-template
[ngTemplateOutlet]="detailTemplate.templateRef"
[ngTemplateOutletContext]="{
dataItem: item.data,
rowIndex: $any(item).index,
$implicit: item.data
}">
</ng-template>
</td>
</ng-container>
</tr>
<tr *ngIf="item.type === 'footer'"
class="k-group-footer"
kendoGridLogicalRow
[logicalRowIndex]="logicalRowIndex(rowIndex)"
[logicalSlaveRow]="lockedColumnsCount > 0 && !isStackedMode"
[totalColumns]="totalColumns"
[logicalCellsCount]="columns.length"
[logicalSlaveCellsCount]="unlockedColumnsCount(item)">
<ng-container *ngIf="!skipGroupDecoration">
<td class="k-group-cell k-table-td k-table-group-td" *ngFor="let g of groups"></td>
</ng-container>
<td class="k-hierarchy-cell k-table-td"
*ngIf="detailTemplate?.templateRef && !isStackedMode"
kendoGridLogicalCell
[logicalRowIndex]="logicalRowIndex(rowIndex)"
[logicalColIndex]="0"
aria-selected="false">
</td>
<ng-container *ngIf="!isStackedMode">
<td kendoGridLogicalCell
[logicalRowIndex]="logicalRowIndex(rowIndex)"
[logicalColIndex]="logicalColIndex(column)"
[attr.data-skip]="skipGroupDecoration"
class="k-table-td"
*ngFor="let column of footerColumns; let columnIndex = index; trackBy: trackByColumns;">
<ng-template
[templateContext]="{
templateRef: $any(column).groupFooterTemplateRef,
group: $any(item.data),
field: $any(column).field,
column: column,
aggregates: $any(item.data)?.aggregates,
$implicit: $any(item.data)?.aggregates
}">
</ng-template>
</td>
</ng-container>
<ng-container *ngIf="isStackedMode">
<td kendoGridLogicalCell
[logicalRowIndex]="logicalRowIndex(rowIndex)"
[logicalColIndex]="hasDetailTemplate ? 1 : 0"
[attr.data-skip]="skipGroupDecoration"
class="k-table-td">
<div class="k-grid-column-template">
<ng-container *ngFor="let col of footerColumns">
<div class="k-column-template-item" *ngIf="$any(col).groupFooterTemplateRef">
<ng-template
[templateContext]="{
templateRef: $any(col).groupFooterTemplateRef,
group: $any(item.data),
field: $any(col).field,
column: col,
aggregates: $any(item.data)?.aggregates,
$implicit: $any(item.data)?.aggregates
}">
</ng-template>
</div>
</ng-container>
</div>
</td>
</ng-container>
</tr>
</ng-container>
<kendo-resize-sensor *ngIf="rowSticky" (resize)="resizeHandler()"></kendo-resize-sensor>
`, isInline: true, dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: LogicalRowDirective, selector: "[kendoGridLogicalRow]", inputs: ["logicalRowIndex", "logicalSlaveRow", "logicalCellsCount", "logicalSlaveCellsCount", "dataRowIndex", "dataItem", "totalColumns"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: LogicalCellDirective, selector: "[kendoGridLogicalCell]", inputs: ["logicalColIndex", "logicalRowIndex", "logicalSlaveCell", "colIndex", "colSpan", "rowSpan", "groupItem", "dataRowIndex", "dataItem", "detailExpandCell", "headerLabelText"] }, { kind: "component", type: CellComponent, selector: "[kendoGridCell]", inputs: ["column", "columns", "columnIndex", "isNew", "isLoading", "isVirtual", "loadingTemplate", "detailTemplate", "item", "rowIndex", "dataItem"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: TemplateContextDirective, selector: "[templateContext]", inputs: ["templateContext"] }, { kind: "component", type: GroupHeaderComponent, selector: "[kendoGridGroupHeader]", inputs: ["rowIndex", "logicalRowIndex", "item", "skipGroupDecoration", "hasDetails", "totalColumnsCount", "hasGroupHeaderColumn", "groupHeaderColumns", "columns", "groups"] }, { kind: "component", type: IconWrapperComponent, selector: "kendo-icon-wrapper", inputs: ["name", "svgIcon", "innerCssClass", "customFontClass", "size"], exportAs: ["kendoIconWrapper"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ResizeSensorComponent, selector: "kendo-resize-sensor", inputs: ["rateLimit"], outputs: ["resize"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TableBodyComponent, decorators: [{
type: Component,
args: [{
selector: '[kendoGridTableBody]',
template: `
<ng-container *ngIf="editService.hasNewItem">
<tr class="k-grid-add-row k-grid-edit-row k-master-row"
kendoGridLogicalRow
[logicalRowIndex]="addRowLogicalIndex()"
[logicalSlaveRow]="lockedColumnsCount > 0 && !isStackedMode"
[logicalCellsCount]="columns.length"
[logicalSlaveCellsCount]="unlockedColumnsCount()"
[totalColumns]="totalColumns">
<ng-container *ngIf="!skipGroupDecoration">
<td class="k-group-cell k-table-td k-table-group-td" *ngFor="let g of groups" role="presentation"></td>
</ng-container>
<td class="k-hierarchy-cell k-table-td"
*ngIf="detailTemplate?.templateRef && !isStackedMode"
kendoGridLogicalCell
[logicalRowIndex]="addRowLogicalIndex()"
[logicalColIndex]="0"
aria-selected="false"
>
</td>
<ng-container *ngIf="isStackedMode; else columnsTemplate">
<td *ngFor="let item of rowsToRender; trackBy: trackByWrapper; let rowIndex = index;"
class="k-table-td"
kendoGridCell
[rowIndex]="-1"
[columnIndex]="0"
[isNew]="true"
[columns]="allColumns"
[dataItem]="newDataItem"
kendoGridLogicalCell
[logicalRowIndex]="addRowLogicalIndex()"
[logicalColIndex]="0">
</td>
</ng-container>
<ng-template #columnsTemplate>
<td *ngFor="let column of columns; let columnIndex = index; trackBy: trackByColumns;"
class="k-table-td"
kendoGridCell
[rowIndex]="-1"
[columnIndex]="lockedColumnsCount + columnIndex"
[isNew]="true"
[column]="column"
[dataItem]="newDataItem"
[class.k-grid-content-sticky]="column.sticky"
[ngClass]="column.cssClass"
[style.left]="column.sticky ? '0' : undefined"
[ngStyle]="column.sticky ? addStickyColumnStyles(column) : column.style"
[attr.colspan]="column.colspan"
[attr.role]="column.tableCellsRole"
kendoGridLogicalCell
[logicalRowIndex]="addRowLogicalIndex()"
[logicalColIndex]="logicalColIndex(column)"
[colSpan]="column.colspan">
</td>
</ng-template>
</tr>
</ng-container>
<tr *ngIf="!rowsToRender?.length" class="k-grid-norecords" role="row">
<td [attr.colspan]="colSpan" class="k-table-td">
<ng-template
*ngIf="noRecordsTemplate?.templateRef"
[templateContext]="{
templateRef: noRecordsTemplate?.templateRef
}">
</ng-template>
<ng-container *ngIf="!noRecordsTemplate?.templateRef">
{{noRecordsText}}
</ng-container>
</td>
</tr>
<ng-container *ngFor="let item of rowsToRender; trackBy: trackByWrapper; let rowIndex = index;">
<tr *ngIf="item.type === 'group'"
kendoGridGroupHeader
[columns]="columns"
[groups]="groups"
[item]="$any(item)"
[hasDetails]="!!detailTemplate?.templateRef"
[skipGroupDecoration]="skipGroupDecoration"
[hasGroupHeaderColumn]="hasGroupHeaderColumn"
[groupHeaderColumns]="groupHeaderColumns"
[rowIndex]="rowIndex + 1"
[totalColumnsCount]="totalColumnsCount"
kendoGridLogicalRow
[logicalRowIndex]="logicalRowIndex(rowIndex)"
[logicalSlaveRow]="lockedColumnsCount > 0 && !isStackedMode"
[totalColumns]="totalColumns"
[logicalCellsCount]="columns.length"
[logicalSlaveCellsCount]="groupHeaderSlaveCellsCount">
</tr>
<tr *ngIf="item.showDataItem"
kendoGridLogicalRow
[dataRowIndex]="$any(item).index"
[dataItem]="item.data"
[logicalRowIndex]="logicalRowIndex(rowIndex)"
[logicalSlaveRow]="lockedColumnsCount > 0 && !isStackedMode"
[totalColumns]="totalColumns"
[logicalCellsCount]="columns.length"
[logicalSlaveCellsCount]="unlockedColumnsCount(item)"
class="{{ isOdd(item) ? 'k-table-alt-row' : ''}}"
[class.k-grid-row-sticky]="rowSticky ? rowSticky({ dataItem: item.data, index: $any(item).index }) : false"
[ngClass]="rowClass({ dataItem: item.data, index: $any(item).index })"
[class.k-master-row]="true"
[class.k-expanded]="item.isExpanded && !isStackedMode"
[class.k-grid-edit-row]="isEditingRow($any(item).index)"
[attr.aria-selected]="(lockedColumnsCount < 1 || isStackedMode) ? isSelectable({ dataItem: item.data, index: $any(item).index }) && isRowSelected(item) : undefined"
[attr.data-kendo-grid-item-index]="$any(item).index"
[class.k-selected]="isSelectable({ dataItem: item.data, index: $any(item).index }) && isRowSelected(item)"
[class.k-highlighted]="item.isHighlighted">
<ng-container *ngIf="!skipGroupDecoration">
<td class="k-group-cell k-table-td k-table-group-td" *ngFor="let g of groups" role="presentation"></td>
</ng-container>
<td class="k-hierarchy-cell k-table-td"
*ngIf="detailTemplate?.templateRef && !isStackedMode"
kendoGridLogicalCell
[logicalRowIndex]="logicalRowIndex(rowIndex)"
[logicalColIndex]="0"
[dataRowIndex]="$any(item).index"
[dataItem]="item.data"
[detailExpandCell]="true"
aria-selected="false"
role="gridcell">
<a
*ngIf="detailTemplate.showIf(item.data, $any(item).index)"
[attr.title]="detailButtonTitle(item)"
[attr.aria-label]="detailButtonTitle(item)"
href="#" tabindex="-1" (click)="toggleRow($any(item).index, item.data)">
<kendo-icon-wrapper
[name]="detailButtonIconName(item)"
[svgIcon]="detailButtonSvgIcon(item)"></kendo-icon-wrapper>
</a>
</td>
<ng-container *ngIf="isStackedMode; else columnsTemplate">
<td kendoGridCell
[rowIndex]="$any(item).index"
[detailTemplate]="detailTemplate"
[item]="item"
[columnIndex]="0"
[attr.data-kendo-grid-column-index]="0"
[columns]="columns"
[dataItem]="item.data"
[isLoading]="isLoading"
[isVirtual]="isVirtual"
[loadingTemplate]="cellLoadingTemplate"
kendoGridLogicalCell
[logicalRowIndex]="logicalRowIndex(rowIndex)"
[logicalColIndex]="0"
[dataRowIndex]="$any(item).index"
[dataItem]="item.data"
[colIndex]="0"
class="k-table-td"
[class.k-touch-action-none]="isSelectable({ dataItem: item.data, index: $any(item).index }) && $any(selectable).drag">
</td>
</ng-container>
<ng-template #columnsTemplate>
<ng-container *ngFor="let column of columns; let columnIndex = index; trackBy: trackByColumns;">
<td *ngIf="!item.cells?.[lockedColumnsCount + columnIndex]?.skip"
kendoGridCell
[rowIndex]="$any(item).index"
[columnIndex]="lockedColumnsCount + columnIndex"
[attr.data-kendo-grid-column-index]="lockedColumnsCount + columnIndex"
[column]="column"
[columns]="allColumns"
[dataItem]="item.data"
[isLoading]="isLoading"
[isVirtual]="isVirtual"
[loadingTemplate]="cellLoadingTemplate"
kendoGridLogicalCell
[logicalRowIndex]="logicalRowIndex(rowIndex)"
[logicalColIndex]="logicalColIndex(column)"
[dataRowIndex]="$any(item).index"
[dataItem]="item.data"
[colIndex]="columnIndex"
[colSpan]="column.colspan"
[rowSpan]="item.cells[lockedColumnsCount + columnIndex]?.rowspan"
[attr.role]="column.tableCellsRole"
class="k-table-td"
[attr.aria-selected]="lockedColumnsCount < 1 && isSelectable({ dataItem: item.data, index: $any(item).index }) ? isAriaSelected(item, column) : undefined"
[class.k-grid-content-sticky]="column.sti