UNPKG

@progress/kendo-angular-grid

Version:

Kendo UI Grid for Angular - high performance data grid with paging, filtering, virtualization, CRUD, and more.

997 lines (996 loc) 49.9 kB
/**----------------------------------------------------------------------------------------- * 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, 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 { RowspanService } from './rowspan.service'; 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"; import * as i11 from "./rowspan.service"; const columnCellIndex = (cell, cells) => { let cellIndex = 0; for (let idx = 0; idx < cells.length; idx++) { if (cells[idx] === cell) { 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; rowspanService; columns = []; allColumns; groups = []; detailTemplate; noRecordsTemplate; data; skip = 0; selectable; filterable; noRecordsText; isLocked = false; isLoading; isVirtual; cellLoadingTemplate; skipGroupDecoration = false; showGroupFooters = 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; dataArray; rerender = false; constructor(detailsService, groupsService, changeNotification, editService, ctx, ngZone, renderer, element, domEvents, selectionService, cellSelectionService, columnInfoService, navigationService, rowspanService) { 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.rowspanService = rowspanService; 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; } get cachedDataArray() { if (!this.dataArray) { this.dataArray = this.data.map(item => item); } return this.dataArray; } // 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.lockedColumnsCount - allColumnsCount; const headerFooterColumnsCount = this.totalColumnsCount - this.lockedColumnsCount - allColumns.length; return item && this.isDataItem(item) ? contentColumnsCount : headerFooterColumnsCount; } shouldSkipCell(rowIndex, colIndex) { return this.rowspanService.shouldSkip(rowIndex, colIndex); } getRowspan(row, column, colIndex) { if (this.rerender) { this.dataArray = null; this.rerender = false; } const rowspan = column.cellRowspan(row, column, this.cachedDataArray); if (rowspan > 1) { this.rowspanService.addCells(row.index, colIndex, rowspan); } this.ngZone.runOutsideAngular(() => setTimeout(() => { this.rerender = true; this.rowspanService.reset(); })); return rowspan; } isAriaSelected(item, column) { return this.cellSelectionService.isCellSelected(item, column) || this.isRowSelected(item) ? 'true' : 'false'; } toggleRow(index, dataItem) { this.detailsService.toggleRow(index, dataItem); return false; } isExpanded(viewItem) { return this.detailsService.isExpanded(viewItem.index, viewItem.data); } detailButtonIconName(viewItem) { const expanded = this.isExpanded(viewItem); return expanded ? 'minus' : 'plus'; } detailButtonSvgIcon(viewItem) { const expanded = this.isExpanded(viewItem); return expanded ? this.minusIcon : this.plusIcon; } detailButtonTitle(viewItem) { const messageKey = this.isExpanded(viewItem) ? 'detailCollapse' : 'detailExpand'; return this.ctx.localization.get(messageKey); } isGroup(item) { return item.type === 'group'; } isDataItem(item) { return !this.isGroup(item) && !this.isFooter(item); } isFooter(item) { return item.type === 'footer'; } isFooterItemInExpandedGroup(item) { const footerItem = { data: item.data, index: item.groupIndex, parentGroup: item.group.parentGroup }; return this.isInExpandedGroup(footerItem); } isDataItemInExpandedGroup(item) { const dataItem = { data: item.group.data, index: item.groupIndex, parentGroup: item.group.parentGroup }; return this.isInExpandedGroup(dataItem); } isInExpandedGroup(item) { return this.groupsService.isInExpandedGroup(item); } isParentGroupExpanded(item) { return this.groupsService.isInExpandedGroup(item.parentGroup); } 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) { pos *= 2; } const absoluteRowIndex = 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 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.childColumns) : [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); let cell, row, body, gridElement; let currentTarget = target; do { cell = closest(currentTarget, matchesNodeName('td')); row = closest(cell, matchesNodeName('tr')); body = closest(row, matchesNodeName('tbody')); currentTarget = body; gridElement = closestInScope(currentTarget, matchesClasses('k-grid'), element); } while (body && body !== element && !gridElement); if (cell && !hasClasses(cell, NON_DATA_CELL_CLASSES) && !hasClasses(row, NON_DATA_ROW_CLASSES) && body === element && !gridElement) { this.editService.preventCellClose(); const focusable = target !== cell && isFocusableWithTabKey(target, false); if (!focusable && !matchesNodeName('label')(target) && !hasClasses(target, IGNORE_TARGET_CLASSSES) && !closestInScope(target, matchesClasses(IGNORE_CONTAINER_CLASSES), cell)) { const args = this.cellClickArgs(cell, row, eventArg); const selectionEnabled = this.selectable && this.selectable.enabled !== false; 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.keyCode === Keys.Enter) { this.clickHandler(args); } } cellClickArgs(cell, row, eventArg) { const index = columnCellIndex(cell, row.cells); const column = this.columns.toArray()[index]; const columnIndex = this.lockedColumnsCount + index; let rowIndex = row.getAttribute('data-kendo-grid-item-index'); rowIndex = rowIndex ? parseInt(rowIndex, 10) : -1; const dataItem = rowIndex === -1 ? this.editService.newDataItem : this.data.at(rowIndex - this.skip); 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 }, { token: i11.RowspanService }], 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", data: "data", skip: "skip", selectable: "selectable", filterable: "filterable", noRecordsText: "noRecordsText", isLocked: "isLocked", isLoading: "isLoading", isVirtual: "isVirtual", cellLoadingTemplate: "cellLoadingTemplate", skipGroupDecoration: "skipGroupDecoration", showGroupFooters: "showGroupFooters", 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" [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" kendoGridLogicalCell [logicalRowIndex]="addRowLogicalIndex()" [logicalColIndex]="0" aria-selected="false" > </td> <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> </tr> </ng-container> <tr *ngIf="data?.length === 0 || data === null" 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 data; trackBy: trackByWrapper; let rowIndex = index;"> <tr *ngIf="isGroup(item) && isParentGroupExpanded($any(item)) && showGroupHeader(item)" 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" [totalColumns]="totalColumns" [logicalCellsCount]="columns.length" [logicalSlaveCellsCount]="groupHeaderSlaveCellsCount"> </tr> <tr *ngIf="isDataItem(item) && (!$any(item).group || isDataItemInExpandedGroup($any(item)))" kendoGridLogicalRow [dataRowIndex]="$any(item).index" [dataItem]="item.data" [logicalRowIndex]="logicalRowIndex(rowIndex)" [logicalSlaveRow]="lockedColumnsCount > 0" [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]="isDataItem(item) && isExpanded(item)" [class.k-grid-edit-row]="isEditingRow($any(item).index)" [attr.aria-selected]="lockedColumnsCount < 1 ? 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)"> <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" 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 *ngFor="let column of columns; let columnIndex = index; trackBy: trackByColumns;"> <td *ngIf="column.cellRowspan ? !shouldSkipCell(rowIndex, lockedColumnsCount + columnIndex) : true" kendoGridCell [rowIndex]="$any(item).index" [columnIndex]="lockedColumnsCount + columnIndex" [attr.data-kendo-grid-column-index]="lockedColumnsCount + columnIndex" [column]="column" [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]="column.cellRowspan ? getRowspan({ index: rowIndex, dataItem: item }, column, lockedColumnsCount + columnIndex) : 1" [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)" > </td> </ng-container> </tr> <tr *ngIf="isDataItem(item) && (!$any(item).group || isDataItemInExpandedGroup($any(item))) && detailTemplate?.templateRef && detailTemplate.showIf(item.data, $any(item).index) && isExpanded(item)" class="k-detail-row" kendoGridLogicalRow [dataRowIndex]="$any(item).index" [dataItem]="item.data" [logicalRowIndex]="logicalRowIndex(rowIndex) + 1" [logicalSlaveRow]="false" [logicalCellsCount]="1" > <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> </tr> <tr *ngIf="isFooter(item) && $any(item).group && (isFooterItemInExpandedGroup($any(item)) || (showGroupFooters && isParentGroupExpanded($any(item).group))) && !$any(item.data).hideFooter" class="k-group-footer" kendoGridLogicalRow [logicalRowIndex]="logicalRowIndex(rowIndex)" [logicalSlaveRow]="lockedColumnsCount > 0" [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" kendoGridLogicalCell [logicalRowIndex]="logicalRowIndex(rowIndex)" [logicalColIndex]="0" aria-selected="false" > </td> <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> </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", "columnIndex", "isNew", "isLoading", "isVirtual", "loadingTemplate", "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" [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" kendoGridLogicalCell [logicalRowIndex]="addRowLogicalIndex()" [logicalColIndex]="0" aria-selected="false" > </td> <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> </tr> </ng-container> <tr *ngIf="data?.length === 0 || data === null" 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 data; trackBy: trackByWrapper; let rowIndex = index;"> <tr *ngIf="isGroup(item) && isParentGroupExpanded($any(item)) && showGroupHeader(item)" 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" [totalColumns]="totalColumns" [logicalCellsCount]="columns.length" [logicalSlaveCellsCount]="groupHeaderSlaveCellsCount"> </tr> <tr *ngIf="isDataItem(item) && (!$any(item).group || isDataItemInExpandedGroup($any(item)))" kendoGridLogicalRow [dataRowIndex]="$any(item).index" [dataItem]="item.data" [logicalRowIndex]="logicalRowIndex(rowIndex)" [logicalSlaveRow]="lockedColumnsCount > 0" [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]="isDataItem(item) && isExpanded(item)" [class.k-grid-edit-row]="isEditingRow($any(item).index)" [attr.aria-selected]="lockedColumnsCount < 1 ? 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)"> <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" 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 *ngFor="let column of columns; let columnIndex = index; trackBy: trackByColumns;"> <td *ngIf="column.cellRowspan ? !shouldSkipCell(rowIndex, lockedColumnsCount + columnIndex) : true" kendoGridCell [rowIndex]="$any(item).index" [columnIndex]="lockedColumnsCount + columnIndex" [attr.data-kendo-grid-column-index]="lockedColumnsCount + columnIndex" [column]="column" [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]="column.cellRowspan ? getRowspan({ index: rowIndex, dataItem: item }, column, lockedColumnsCount + columnIndex) : 1" [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)" > </td> </ng-container> </tr> <tr *ngIf="isDataItem(item) && (!$any(item).group || isDataItemInExpandedGroup($any(item))) && detailTemplate?.templateRef && detailTemplate.showIf(item.data, $any(item).index) && isExpanded(item)" class="k-detail-row" kendoGridLogicalRow [dataRowIndex]="$any(item).index" [dataItem]="item.data" [logicalRowIndex]="logicalRowIndex(rowIndex) + 1" [logicalSlaveRow]="false" [logicalCellsCount]="1" > <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> </tr> <tr *ngIf="isFooter(item) && $any(item).group && (isFooterItemInExpandedGroup($any(item)) || (showGroupFooters && isParentGroupExpanded($any(item).group))) && !$any(item.data).hideFooter" class="k-group-footer" kendoGridLogicalRow [logicalRowIndex]="logicalRowIndex(rowIndex)" [logicalSlaveRow]="lockedColumnsCount > 0" [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" kendoGridLogicalCell [logicalRowIndex]="logicalRowIndex(rowIndex)" [logicalColIndex]="0" aria-selected="false" > </td> <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> </tr> </ng-container> <kendo-resize-sensor *ngIf="rowSticky" (resize)="resizeHandler()"></kendo-resize-sensor> `, standalone: true, imports: [ NgIf, LogicalRowDirective, NgFor, LogicalCellDirective, CellComponent, NgClass, NgStyle, TemplateContextDirective, GroupHeaderComponent, IconWrapperComponent, NgTemplateOutlet, ResizeSensorComponent ] }] }], ctorParameters: function () { return [{ type: i1.DetailsService }, { type: i2.GroupsService }, { type: i3.ChangeNotificationService }, { type: i4.EditService }, { type: i5.ContextService }, { type: i0.NgZone }, { type: i0.Renderer2 }, { type: i0.ElementRef }, { type: i6.DomEventsService }, { type: i7.SelectionService }, { type: i8.CellSelectionService }, { type: i9.ColumnInfoService }, { type: i10.NavigationService }, { type: i11.RowspanService }]; }, propDecorators: { columns: [{ type: Input }], allColumns: [{ type: Input }], groups: [{ type: Input }], detailTemplate: [{ type: Input }], noRecordsTemplate: [{ type: Input }], data: [{ type: Input }], skip: [{ type: Input }], selectable: [{ type: Input }], filterable: [{ type: Input }], noRecordsText: [{ type: Input }], isLocked: [{ type: Input }], isLoading: [{ type: Input }], isVirtual: [{ type: Input }], cellLoadingTemplate: [{ type: Input }], skipGroupDecoration: [{ type: Input }], showGroupFooters: [{ type: Input }], lockedColumnsCount: [{ type: Input }], totalColumnsCount: [{ type: Input }], virtualColumns: [{ type: Input }], trackBy: [{ type: Input }], rowSticky: [{ type: Input }], totalColumns: [{ type: Input }], rowClass: [{ type: Input }], hostClass: [{ type: HostBinding, args: ['class.k-table-tbody'] }] } });