igniteui-angular
Version:
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
1 lines • 662 kB
Source Map (JSON)
{"version":3,"file":"igniteui-angular-grids-grid.mjs","sources":["../../../projects/igniteui-angular/grids/grid/src/grid-api.service.ts","../../../projects/igniteui-angular/grids/grid/src/groupby-row.component.ts","../../../projects/igniteui-angular/grids/grid/src/groupby-row.component.html","../../../projects/igniteui-angular/grids/grid/src/grid.details.pipe.ts","../../../projects/igniteui-angular/grids/grid/src/grid.summary.pipe.ts","../../../projects/igniteui-angular/grids/grid/src/grid.pipes.ts","../../../projects/igniteui-angular/grids/grid/src/expandable-cell.component.ts","../../../projects/igniteui-angular/grids/grid/src/expandable-cell.component.html","../../../projects/igniteui-angular/grids/grid/src/grid-row.component.ts","../../../projects/igniteui-angular/grids/grid/src/grid-row.component.html","../../../projects/igniteui-angular/grids/grid/src/grid-base.directive.ts","../../../projects/igniteui-angular/grids/grid/src/grid.component.ts","../../../projects/igniteui-angular/grids/grid/src/grid.component.html","../../../projects/igniteui-angular/grids/grid/src/grid.module.ts","../../../projects/igniteui-angular/grids/grid/src/igniteui-angular-grids-grid.ts"],"sourcesContent":["import { GridBaseAPIService } from 'igniteui-angular/grids/core';\nimport { Injectable } from '@angular/core';\nimport { GridServiceType, GridType } from 'igniteui-angular/grids/core';\nimport { cloneArray, DataUtil, IGroupByExpandState, IGroupByRecord, IGroupingExpression } from 'igniteui-angular/core';\n\n@Injectable()\nexport class IgxGridAPIService extends GridBaseAPIService<GridType> implements GridServiceType {\n\n public groupBy(expression: IGroupingExpression): void {\n const groupingState = cloneArray(this.grid.groupingExpressions);\n this.prepare_grouping_expression([groupingState], expression);\n this.grid.groupingExpressions = groupingState;\n this.arrange_sorting_expressions();\n }\n\n public groupBy_multiple(expressions: IGroupingExpression[]): void {\n const groupingState = cloneArray(this.grid.groupingExpressions);\n\n for (const each of expressions) {\n this.prepare_grouping_expression([groupingState], each);\n }\n\n this.grid.groupingExpressions = groupingState;\n this.arrange_sorting_expressions();\n }\n\n public override clear_groupby(name?: string | Array<string>) {\n const groupingState = cloneArray(this.grid.groupingExpressions);\n\n if (name) {\n const names = typeof name === 'string' ? [name] : name;\n const groupedCols = groupingState.filter((state) => names.indexOf(state.fieldName) < 0);\n this.grid.groupingExpressions = groupedCols;\n names.forEach((colName) => {\n const grExprIndex = groupingState.findIndex((exp) => exp.fieldName === colName);\n const grpExpandState = this.grid.groupingExpansionState;\n /* remove expansion states related to the cleared group\n and all with deeper hierarchy than the cleared group */\n const newExpandState = grpExpandState.filter((val) => val.hierarchy && val.hierarchy.length <= grExprIndex);\n /* Do not set the new instance produced by filter\n when there are no differences between expansion states */\n if (newExpandState.length !== grpExpandState.length) {\n this.grid.groupingExpansionState = newExpandState;\n }\n });\n } else {\n // clear all\n this.grid.groupingExpressions = [];\n this.grid.groupingExpansionState = [];\n }\n }\n\n public groupBy_get_expanded_for_group(groupRow: IGroupByRecord): IGroupByExpandState {\n const grState = this.grid.groupingExpansionState;\n const hierarchy = DataUtil.getHierarchy(groupRow);\n return grState.find((state) =>\n DataUtil.isHierarchyMatch(\n state.hierarchy || [{ fieldName: groupRow.expression.fieldName, value: groupRow.value }],\n hierarchy,\n this.grid.groupingExpressions));\n }\n\n public groupBy_is_row_in_group(groupRow: IGroupByRecord, rowID): boolean {\n const grid = this.grid;\n let rowInGroup = false;\n groupRow.records.forEach(row => {\n if (grid.primaryKey ? row[grid.primaryKey] === rowID : row === rowID) {\n rowInGroup = true;\n }\n });\n return rowInGroup;\n }\n\n public groupBy_toggle_group(groupRow: IGroupByRecord) {\n const grid = this.grid;\n if (grid.gridAPI.crudService.cellInEditMode) {\n this.crudService.endEdit(false);\n }\n\n const expansionState = grid.groupingExpansionState;\n const state: IGroupByExpandState = this.groupBy_get_expanded_for_group(groupRow);\n if (state) {\n state.expanded = !state.expanded;\n } else {\n expansionState.push({\n expanded: !grid.groupsExpanded,\n hierarchy: DataUtil.getHierarchy(groupRow)\n });\n }\n this.grid.groupingExpansionState = [...expansionState];\n if (grid.rowEditable) {\n grid.repositionRowEditingOverlay(grid.gridAPI.crudService.rowInEditMode);\n }\n }\n public set_grouprow_expansion_state(groupRow: IGroupByRecord, value: boolean) {\n if (this.grid.isExpandedGroup(groupRow) !== value) {\n this.groupBy_toggle_group(groupRow);\n }\n }\n\n public groupBy_fully_expand_group(groupRow: IGroupByRecord) {\n const state: IGroupByExpandState = this.groupBy_get_expanded_for_group(groupRow);\n const expanded = state ? state.expanded : this.grid.groupsExpanded;\n if (!expanded) {\n this.groupBy_toggle_group(groupRow);\n }\n if (groupRow.groupParent) {\n this.groupBy_fully_expand_group(groupRow.groupParent);\n }\n }\n\n public groupBy_select_all_rows_in_group(groupRow: IGroupByRecord, clearPrevSelection: boolean) {\n this.grid.selectionService.selectRowsWithNoEvent(this.grid.primaryKey ?\n groupRow.records.map(x => x[this.grid.primaryKey]) : groupRow.records, clearPrevSelection);\n }\n\n public groupBy_deselect_all_rows_in_group(groupRow: IGroupByRecord) {\n this.grid.selectionService.deselectRowsWithNoEvent(this.grid.primaryKey ?\n groupRow.records.map(x => x[this.grid.primaryKey]) : groupRow.records);\n }\n\n public arrange_sorting_expressions() {\n const groupingState = this.grid.groupingExpressions;\n const sortingState = cloneArray(this.grid.sortingExpressions);\n for (const grExpr of groupingState) {\n const sortExprIndex = sortingState.findIndex((exp) => exp.fieldName === grExpr.fieldName);\n if (sortExprIndex > -1) {\n sortingState.splice(sortExprIndex, 1);\n }\n }\n this.grid.sortingExpressions = sortingState;\n }\n\n public get_groupBy_record_id(gRow: IGroupByRecord): string {\n let recordId = '{ ';\n const hierrarchy = DataUtil.getHierarchy(gRow);\n\n for (let i = 0; i < hierrarchy.length; i++) {\n const groupByKey = hierrarchy[i];\n recordId += `'${groupByKey.fieldName}': '${groupByKey.value}'`;\n\n if (i < hierrarchy.length - 1) {\n recordId += ', ';\n }\n }\n recordId += ' }';\n\n return recordId;\n }\n\n public override remove_grouping_expression(fieldName: string) {\n const groupingExpressions = this.grid.groupingExpressions;\n const index = groupingExpressions.findIndex((expr) => expr.fieldName === fieldName);\n if (index !== -1) {\n groupingExpressions.splice(index, 1);\n }\n }\n}\n","import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, HostBinding, HostListener, Input, ViewChild, TemplateRef, OnDestroy, inject } from '@angular/core';\nimport { NgTemplateOutlet, DecimalPipe, DatePipe, getLocaleCurrencyCode, PercentPipe, CurrencyPipe } from '@angular/common';\nimport { takeUntil } from 'rxjs/operators';\nimport { Subject } from 'rxjs';\nimport {\n GridSelectionMode,\n GridType,\n IGX_GRID_BASE,\n IgxColumnFormatterPipe,\n IgxFilteringService,\n IgxGridSelectionService,\n ISelectionNode\n} from 'igniteui-angular/grids/core';\nimport { IgxGridRowComponent } from './grid-row.component';\nimport { IgxIconComponent } from 'igniteui-angular/icon';\nimport { IgxBadgeComponent } from 'igniteui-angular/badge';\nimport { IgxCheckboxComponent } from 'igniteui-angular/checkbox';\nimport { GridColumnDataType, IGroupByRecord } from 'igniteui-angular/core';\n\n@Component({\n changeDetection: ChangeDetectionStrategy.OnPush,\n selector: 'igx-grid-groupby-row',\n templateUrl: './groupby-row.component.html',\n imports: [\n NgTemplateOutlet,\n DecimalPipe,\n DatePipe,\n PercentPipe,\n CurrencyPipe,\n IgxIconComponent,\n IgxBadgeComponent,\n IgxCheckboxComponent,\n IgxColumnFormatterPipe\n ]\n})\nexport class IgxGridGroupByRowComponent implements OnDestroy {\n public grid = inject<GridType>(IGX_GRID_BASE);\n public gridSelection = inject(IgxGridSelectionService);\n public element = inject(ElementRef);\n public cdr = inject(ChangeDetectorRef);\n public filteringService = inject(IgxFilteringService);\n\n /**\n * @hidden\n */\n @Input()\n public hideGroupRowSelectors: boolean;\n\n /**\n * @hidden\n */\n @Input()\n public rowDraggable: boolean;\n\n /**\n * Sets the index of the row.\n * ```html\n * <igx-grid-groupby-row [gridID]=\"id\" [index]=\"rowIndex\" [groupRow]=\"rowData\" #row></igx-grid-groupby-row>\n * ```\n */\n @Input()\n public index: number;\n\n /**\n * Sets the id of the grid the row belongs to.\n * ```html\n * <igx-grid-groupby-row [gridID]=\"id\" [index]=\"rowIndex\" [groupRow]=\"rowData\" #row></igx-grid-groupby-row>\n * ```\n */\n @Input()\n public gridID: string;\n\n /**\n * The group record the component renders for.\n * ```typescript\n * <igx-grid-groupby-row [gridID]=\"id\" [index]=\"rowIndex\" [groupRow]=\"rowData\" #row></igx-grid-groupby-row>\n * ```\n */\n @Input()\n public groupRow: IGroupByRecord;\n\n /**\n * Returns a reference of the content of the group.\n * ```typescript\n * const groupRowContent = this.grid1.rowList.first.groupContent;\n * ```\n */\n @ViewChild('groupContent', { static: true })\n public groupContent: ElementRef;\n\n /**\n * @hidden\n */\n @Input()\n protected isFocused = false;\n\n /**\n * @hidden\n */\n @ViewChild('defaultGroupByExpandedTemplate', { read: TemplateRef, static: true })\n protected defaultGroupByExpandedTemplate: TemplateRef<any>;\n\n /**\n * @hidden\n */\n @ViewChild('defaultGroupByCollapsedTemplate', { read: TemplateRef, static: true })\n protected defaultGroupByCollapsedTemplate: TemplateRef<any>;\n\n /**\n * @hidden\n */\n protected destroy$ = new Subject<void>();\n\n /**\n * @hidden\n */\n protected defaultCssClass = 'igx-grid__group-row';\n\n /**\n * @hidden\n */\n protected paddingIndentationCssClass = 'igx-grid__group-row--padding-level';\n\n /**\n * Returns whether the row is focused.\n * ```\n * let gridRowFocused = this.grid1.rowList.first.focused;\n * ```\n */\n public get focused(): boolean {\n return this.isActive();\n }\n\n /** @hidden @internal */\n public get currencyCode(): string {\n return this.groupRow.column.pipeArgs.currencyCode ?\n this.groupRow.column.pipeArgs.currencyCode : getLocaleCurrencyCode(this.grid.locale);\n }\n\n constructor() {\n this.gridSelection.selectedRowsChange.pipe(takeUntil(this.destroy$)).subscribe(() => {\n this.cdr.markForCheck();\n });\n }\n\n\n @HostListener('pointerdown')\n public activate() {\n this.grid.navigation.setActiveNode({ row: this.index });\n }\n\n @HostListener('click', ['$event'])\n public onClick(event: MouseEvent) {\n this.grid.rowClick.emit({\n row: this.grid.createRow(this.index),\n event\n });\n }\n\n /**\n * @hidden\n * @internal\n */\n public ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n /**\n * Returns whether the group row is expanded.\n * ```typescript\n * const groupRowExpanded = this.grid1.rowList.first.expanded;\n * ```\n */\n @HostBinding('attr.aria-expanded')\n public get expanded(): boolean {\n return this.grid.isExpandedGroup(this.groupRow);\n }\n\n /**\n * @hidden\n */\n @HostBinding('attr.aria-describedby')\n public get describedBy(): string {\n const grRowExpr = this.groupRow.expression !== undefined ? this.groupRow.expression.fieldName : '';\n return this.gridID + '_' + grRowExpr;\n }\n\n @HostBinding('attr.data-rowIndex')\n public get dataRowIndex() {\n return this.index;\n }\n\n /**\n * Returns a reference to the underlying HTML element.\n * ```typescript\n * const groupRowElement = this.nativeElement;\n * ```\n */\n public get nativeElement(): any {\n return this.element.nativeElement;\n }\n\n @HostBinding('attr.id')\n public get attrCellID() {\n return `${this.gridID}_${this.index}`;\n }\n\n /**\n * Returns the style classes applied to the group rows.\n * ```typescript\n * const groupCssStyles = this.grid1.rowList.first.styleClasses;\n * ```\n */\n @HostBinding('class')\n public get styleClasses(): string {\n return `${this.defaultCssClass} ` + `${this.paddingIndentationCssClass}-` + this.groupRow.level +\n (this.isActive() ? ` ${this.defaultCssClass}--active` : '');\n }\n\n public isActive() {\n return this.grid.navigation.activeNode ? this.grid.navigation.activeNode.row === this.index : false;\n }\n\n /**\n * @hidden @internal\n */\n public getRowID(rowData): IgxGridRowComponent {\n return this.grid.primaryKey ? rowData[this.grid.primaryKey] : rowData;\n }\n\n /**\n * @hidden @internal\n */\n public onGroupSelectorClick(event) {\n if (!this.grid.isMultiRowSelectionEnabled) {\n return;\n }\n event.stopPropagation();\n if (this.areAllRowsInTheGroupSelected) {\n this.gridSelection.deselectRows(this.groupRow.records.map(x => this.getRowID(x)));\n } else {\n this.gridSelection.selectRows(this.groupRow.records.map(x => this.getRowID(x)));\n }\n }\n\n /**\n * Toggles the group row.\n * ```typescript\n * this.grid1.rowList.first.toggle()\n * ```\n */\n public toggle() {\n this.grid.toggleGroup(this.groupRow);\n }\n\n public get iconTemplate() {\n if (this.expanded) {\n return this.grid.rowExpandedIndicatorTemplate || this.defaultGroupByExpandedTemplate;\n } else {\n return this.grid.rowCollapsedIndicatorTemplate || this.defaultGroupByCollapsedTemplate;\n }\n }\n\n protected get selectionNode(): ISelectionNode {\n return {\n row: this.index,\n column: this.gridSelection.activeElement ? this.gridSelection.activeElement.column : 0\n };\n }\n\n /**\n * @hidden @internal\n */\n public get dataType(): any {\n const column = this.groupRow.column;\n return (column && column.dataType) || GridColumnDataType.String;\n }\n\n /**\n * @hidden @internal\n */\n public get formatter(): any {\n const column = this.groupRow.column;\n return (column && column.formatter) || null;\n }\n\n /**\n * @hidden @internal\n */\n public get areAllRowsInTheGroupSelected(): boolean {\n return this.groupRow.records.every(x => this.gridSelection.isRowSelected(this.getRowID(x)));\n }\n\n /**\n * @hidden @internal\n */\n public get selectedRowsInTheGroup(): any[] {\n const selectedIds = new Set(this.gridSelection.filteredSelectedRowIds);\n return this.groupRow.records.filter(rowID => selectedIds.has(this.getRowID(rowID)));\n }\n\n /**\n * @hidden @internal\n */\n public get groupByRowCheckboxIndeterminateState(): boolean {\n if (this.selectedRowsInTheGroup.length > 0) {\n return !this.areAllRowsInTheGroupSelected;\n }\n return false;\n }\n\n /**\n * @hidden @internal\n */\n public get groupByRowSelectorBaseAriaLabel(): string {\n const ariaLabel: string = this.areAllRowsInTheGroupSelected ?\n this.grid.resourceStrings.igx_grid_groupByArea_deselect_message : this.grid.resourceStrings.igx_grid_groupByArea_select_message;\n return ariaLabel.replace('{0}', this.groupRow.expression.fieldName).replace('{1}', this.groupRow.value);\n }\n\n /**\n * @hidden @internal\n */\n public get showRowSelectors(): boolean {\n return this.grid.rowSelection !== GridSelectionMode.none && !this.hideGroupRowSelectors;\n }\n\n}\n","<ng-container #defaultGroupRow>\n\n @if (rowDraggable) {\n <div class=\"igx-grid__drag-indicator igx-grid__tr-action\">\n <igx-icon family=\"default\" name=\"drag_indicator\" [style.visibility]=\"'hidden'\"></igx-icon>\n </div>\n }\n\n @if (showRowSelectors) {\n <div class=\"igx-grid__cbx-selection igx-grid__tr-action\" (pointerdown)=\"$event.preventDefault()\"\n (click)=\"onGroupSelectorClick($event)\">\n <ng-template #groupByRowSelector *ngTemplateOutlet=\"\n this.grid.groupByRowSelectorTemplate ? this.grid.groupByRowSelectorTemplate : groupByRowSelectorBaseTemplate;\n context: { $implicit: {\n selectedCount: selectedRowsInTheGroup.length,\n totalCount: this.groupRow.records.length,\n groupRow: this.groupRow }}\">\n </ng-template>\n </div>\n }\n\n <div (click)=\"toggle()\" class=\"igx-grid__grouping-indicator\">\n <ng-container *ngTemplateOutlet=\"iconTemplate; context: { $implicit: this }\">\n </ng-container>\n </div>\n\n <div class=\"igx-grid__group-content\" #groupContent>\n <ng-container\n *ngTemplateOutlet=\"grid.groupRowTemplate ? grid.groupRowTemplate : defaultGroupByTemplate; context: { $implicit: groupRow }\">\n </ng-container>\n </div>\n\n <ng-template #defaultGroupByExpandedTemplate>\n <igx-icon family=\"default\" name=\"tree_collapse\"></igx-icon>\n </ng-template>\n\n <ng-template #defaultGroupByCollapsedTemplate>\n <igx-icon family=\"default\" name=\"tree_expand\"></igx-icon>\n </ng-template>\n\n\n <ng-template #defaultGroupByTemplate>\n <div class=\"igx-group-label\">\n <igx-icon family=\"default\" name=\"group_work\" class=\"igx-group-label__icon\"></igx-icon>\n <span class=\"igx-group-label__column-name\">\n {{ groupRow.column && groupRow.column.header ?\n groupRow.column.header :\n (groupRow.expression ? groupRow.expression.fieldName : '') }}:\n </span>\n\n <span class=\"igx-group-label__text\">{{\n formatter\n ? (groupRow.value | columnFormatter:formatter:groupRow.records[0]:null)\n : dataType === \"number\"\n ? (groupRow.value | number:groupRow.column.pipeArgs.digitsInfo:grid.locale)\n : (dataType === 'date' || dataType === 'time' || dataType === 'dateTime')\n ? (groupRow.value | date:groupRow.column.pipeArgs.format:groupRow.column.pipeArgs.timezone:grid.locale)\n : dataType === 'currency'\n ? (groupRow.value | currency:currencyCode:groupRow.column.pipeArgs.display:groupRow.column.pipeArgs.digitsInfo:grid.locale)\n : dataType === 'percent'\n ? (groupRow.value | percent:groupRow.column.pipeArgs.digitsInfo:grid.locale)\n : groupRow.value\n }}</span>\n\n <igx-badge [value]=\"groupRow.records ? groupRow.records.length : 0\" class='igx-group-label__count-badge'>\n </igx-badge>\n </div>\n </ng-template>\n <ng-template #groupByRowSelectorBaseTemplate let-context>\n <div class=\"igx-grid__cbx-padding\">\n <igx-checkbox [tabindex]=\"-1\" [readonly]=\"true\" [checked]=\"areAllRowsInTheGroupSelected\"\n [disableRipple]=\"true\" [indeterminate]=\"groupByRowCheckboxIndeterminateState\"\n [disabled]=\"this.grid.rowSelection === 'single'\" [aria-label]=\"groupByRowSelectorBaseAriaLabel\"\n #groupByRowCheckbox>\n </igx-checkbox>\n </div>\n </ng-template>\n</ng-container>\n","import { PipeTransform, Pipe, inject } from '@angular/core';\nimport { GridType, IGX_GRID_BASE } from 'igniteui-angular/grids/core';\n\n/** @hidden */\n@Pipe({\n name: 'gridDetails',\n standalone: true\n})\nexport class IgxGridDetailsPipe implements PipeTransform {\n private grid = inject<GridType>(IGX_GRID_BASE);\n\n\n public transform(collection: any[], hasDetails: boolean, expansionStates: Map<any, boolean>, _pipeTrigger: number) {\n if (!hasDetails) {\n return collection;\n }\n const res = this.addDetailRows(collection, expansionStates);\n return res;\n }\n\n protected addDetailRows(collection: any[], _expansionStates: Map<any, boolean>) {\n const result = [];\n collection.forEach((v) => {\n result.push(v);\n if (!this.grid.isGroupByRecord(v) && !this.grid.isSummaryRow(v) &&\n this.grid.gridAPI.get_row_expansion_state(v)) {\n const detailsObj = { detailsData: v };\n result.push(detailsObj);\n }\n });\n return result;\n }\n}\n","import { Pipe, PipeTransform, inject } from '@angular/core';\nimport { GridSummaryPosition, GridType, IGX_GRID_BASE } from 'igniteui-angular/grids/core';\nimport { GridSummaryCalculationMode, IGroupByRecord, IGroupByResult, ISummaryRecord } from 'igniteui-angular/core';\n\n/** @hidden */\ninterface ISkipRecord { skip?: boolean }\n\n/** @hidden */\n@Pipe({\n name: 'gridSummary',\n standalone: true\n})\nexport class IgxGridSummaryPipe implements PipeTransform {\n private grid = inject<GridType>(IGX_GRID_BASE);\n\n\n public transform(collection: IGroupByResult,\n hasSummary: boolean,\n summaryCalculationMode: GridSummaryCalculationMode,\n summaryPosition: GridSummaryPosition,\n id: string, showSummary, _: number, __: number): any[] {\n\n if (!collection.data || !hasSummary || summaryCalculationMode === GridSummaryCalculationMode.rootLevelOnly) {\n return collection.data;\n }\n\n return this.addSummaryRows(id, collection, summaryPosition, showSummary);\n }\n\n private addSummaryRows(gridId: string, collection: IGroupByResult, summaryPosition: GridSummaryPosition, showSummary): any[] {\n const recordsWithSummary = [];\n const lastChildMap = new Map<any, IGroupByRecord[]>();\n const maxSummaryHeight = this.grid.summaryService.calcMaxSummaryHeight();\n\n if (collection.metadata.length && !this.grid.isGroupByRecord(collection.data[0]) &&\n this.grid.isGroupByRecord(collection.metadata[0]) && summaryPosition === GridSummaryPosition.bottom) {\n const groups: Array<IGroupByRecord & ISkipRecord> = [];\n groups.push(collection.metadata[0]);\n while (groups[groups.length - 1].groupParent) {\n groups.push(groups[groups.length - 1].groupParent);\n }\n groups.reverse();\n groups.forEach(g => g.skip = true);\n collection.data.splice(0, 0, ...groups);\n }\n for (const record of collection.data) {\n let skipAdd = false;\n let recordId;\n let groupByRecord: IGroupByRecord = null;\n if (this.grid.isGroupByRecord(record)) {\n skipAdd = !!record.skip;\n record.skip = null;\n groupByRecord = record as IGroupByRecord;\n recordId = this.grid.gridAPI.get_groupBy_record_id(groupByRecord);\n } else {\n recordId = this.grid.gridAPI.get_row_id(record);\n }\n if (!skipAdd) {\n recordsWithSummary.push(record);\n }\n\n if (summaryPosition === GridSummaryPosition.bottom && showSummary &&\n (groupByRecord && !this.grid.isExpandedGroup(groupByRecord))) {\n const records = this.removeDeletedRecord(this.grid, groupByRecord.records.slice());\n const summaries = this.grid.summaryService.calculateSummaries(recordId, records);\n const summaryRecord: ISummaryRecord = {\n summaries,\n max: maxSummaryHeight\n };\n recordsWithSummary.push(summaryRecord);\n }\n if (summaryPosition === GridSummaryPosition.bottom && lastChildMap.has(recordId)) {\n const groupRecords = lastChildMap.get(recordId);\n\n for (const groupRecord of groupRecords) {\n const groupRecordId = this.grid.gridAPI.get_groupBy_record_id(groupRecord);\n const records = this.removeDeletedRecord(this.grid, groupRecord.records.slice());\n const summaries = this.grid.summaryService.calculateSummaries(groupRecordId, records, groupRecord);\n const summaryRecord: ISummaryRecord = {\n summaries,\n max: maxSummaryHeight\n };\n recordsWithSummary.push(summaryRecord);\n }\n }\n\n const showSummaries = showSummary ? false : (groupByRecord && !this.grid.isExpandedGroup(groupByRecord));\n if (groupByRecord === null || showSummaries) {\n continue;\n }\n\n if (summaryPosition === GridSummaryPosition.top) {\n const records = this.removeDeletedRecord(this.grid, groupByRecord.records.slice());\n const summaries = this.grid.summaryService.calculateSummaries(recordId, records, groupByRecord);\n const summaryRecord: ISummaryRecord = {\n summaries,\n max: maxSummaryHeight\n };\n recordsWithSummary.push(summaryRecord);\n } else if (summaryPosition === GridSummaryPosition.bottom) {\n let lastChild = groupByRecord;\n\n while (lastChild.groups && lastChild.groups.length > 0 && this.grid.isExpandedGroup(lastChild)) {\n lastChild = lastChild.groups[lastChild.groups.length - 1];\n }\n\n let lastChildId;\n if (this.grid.isExpandedGroup(lastChild)) {\n lastChildId = this.grid.gridAPI.get_row_id(lastChild.records[lastChild.records.length - 1]);\n } else {\n lastChildId = this.grid.gridAPI.get_groupBy_record_id(lastChild);\n }\n\n let groupRecords = lastChildMap.get(lastChildId);\n if (!groupRecords) {\n groupRecords = [];\n lastChildMap.set(lastChildId, groupRecords);\n }\n groupRecords.unshift(groupByRecord);\n }\n }\n return recordsWithSummary;\n }\n\n private removeDeletedRecord(grid: GridType, data: any[]) {\n if (!grid.transactions.enabled) {\n return data;\n }\n const deletedRows = grid.transactions.getTransactionLog().filter(t => t.type === 'delete').map(t => t.id);\n deletedRows.forEach(rowID => {\n const tempData = grid.primaryKey ? data.map(rec => rec[grid.primaryKey]) : data;\n const index = tempData.indexOf(rowID);\n if (index !== -1) {\n data.splice(index, 1);\n }\n });\n return data;\n }\n}\n","import { inject, Pipe, PipeTransform } from '@angular/core';\nimport { IGridSortingStrategy, IGridGroupingStrategy, cloneArray, DataUtil, FilteringExpressionsTree, FilterUtil, IFilteringExpressionsTree, IFilteringStrategy, IGridMergeStrategy, IGroupByExpandState, IGroupingExpression, ISortingExpression, IGroupByResult, ColumnType, IMergeByResult } from 'igniteui-angular/core';\nimport { GridCellMergeMode, RowPinningPosition, GridType, IGX_GRID_BASE } from 'igniteui-angular/grids/core';\n\n/**\n * @hidden\n */\n@Pipe({\n name: 'gridSort',\n standalone: true\n})\nexport class IgxGridSortingPipe implements PipeTransform {\n private grid = inject<GridType>(IGX_GRID_BASE);\n\n public transform(collection: any[], sortExpressions: ISortingExpression[], groupExpressions: IGroupingExpression[], sorting: IGridSortingStrategy,\n id: string, pipeTrigger: number, pinned?): any[] {\n let result: any[];\n const expressions = groupExpressions.concat(sortExpressions);\n if (!expressions.length) {\n result = collection;\n } else {\n result = DataUtil.sort(cloneArray(collection), expressions, sorting, this.grid);\n }\n this.grid.setFilteredSortedData(result, pinned);\n\n return result;\n }\n}\n\n/**\n * @hidden\n */\n@Pipe({\n name: 'gridGroupBy',\n standalone: true\n})\nexport class IgxGridGroupingPipe implements PipeTransform {\n private grid = inject<GridType>(IGX_GRID_BASE);\n\n\n public transform(collection: any[], expression: IGroupingExpression | IGroupingExpression[],\n expansion: IGroupByExpandState | IGroupByExpandState[],\n groupingStrategy: IGridGroupingStrategy, defaultExpanded: boolean,\n id: string, groupsRecords: any[], _pipeTrigger: number): IGroupByResult {\n\n const state = { expressions: [], expansion: [], defaultExpanded };\n state.expressions = this.grid.groupingExpressions;\n let result: IGroupByResult;\n const fullResult: IGroupByResult = { data: [], metadata: [] };\n\n if (!state.expressions.length) {\n // empty the array without changing reference\n groupsRecords.splice(0, groupsRecords.length);\n result = {\n data: collection,\n metadata: collection\n };\n } else {\n state.expansion = this.grid.groupingExpansionState;\n state.defaultExpanded = this.grid.groupsExpanded;\n result = DataUtil.group(cloneArray(collection), state, groupingStrategy, this.grid, groupsRecords, fullResult);\n }\n this.grid.groupingFlatResult = result.data;\n this.grid.groupingResult = fullResult.data;\n this.grid.groupingMetadata = fullResult.metadata;\n return result;\n }\n}\n\n@Pipe({\n name: 'gridCellMerge',\n standalone: true\n})\nexport class IgxGridCellMergePipe implements PipeTransform {\n\n private grid = inject<GridType>(IGX_GRID_BASE);\n\n public transform(collection: any, colsToMerge: ColumnType[], mergeMode: GridCellMergeMode, mergeStrategy: IGridMergeStrategy, _pipeTrigger: number) {\n if (colsToMerge.length === 0) {\n return collection;\n }\n const result = DataUtil.merge(collection, colsToMerge, mergeStrategy, [], this.grid);\n return result;\n }\n}\n\n@Pipe({\n name: 'gridUnmergeActive',\n standalone: true\n})\nexport class IgxGridUnmergeActivePipe implements PipeTransform {\n\n private grid = inject<GridType>(IGX_GRID_BASE);\n\n public transform(collection: any, colsToMerge: ColumnType[], activeRowIndexes: number[], pinned: boolean, _pipeTrigger: number) {\n if (colsToMerge.length === 0) {\n return collection;\n }\n if (this.grid.hasPinnedRecords && !pinned && this.grid.pinning.rows !== RowPinningPosition.Bottom) {\n activeRowIndexes = activeRowIndexes.map(x => x - this.grid.pinnedRecordsCount);\n }\n activeRowIndexes = Array.from(new Set(activeRowIndexes)).filter(x => !isNaN(x));\n const rootsToUpdate = [];\n activeRowIndexes.forEach(index => {\n const target = collection[index];\n if (target && target.cellMergeMeta) {\n colsToMerge.forEach(col => {\n const colMeta = target.cellMergeMeta.get(col.field);\n const root = colMeta.root || (colMeta.rowSpan > 1 ? target : null);\n if (root) {\n rootsToUpdate.push(root);\n }\n });\n }\n });\n const uniqueRoots = Array.from(new Set(rootsToUpdate));\n if (uniqueRoots.length === 0) {\n // if nothing to update, return\n return collection;\n }\n\n let result = cloneArray(collection) as any;\n uniqueRoots.forEach(x => {\n const index = collection.indexOf(x);\n const colKeys = [...x.cellMergeMeta.keys()];\n const cols = colsToMerge.filter(col => colKeys.indexOf(col.field) !== -1);\n for (const col of cols) {\n const childData = x.cellMergeMeta.get(col.field).childRecords;\n const childRecs = childData.map(rec => rec.recordRef);\n if(childRecs.length === 0) {\n // nothing to unmerge\n continue;\n }\n const unmergedData = DataUtil.merge([x.recordRef, ...childRecs], [col], this.grid.mergeStrategy, activeRowIndexes.map(ri => ri - index), this.grid);\n for (let i = 0; i < unmergedData.length; i++) {\n const unmergedRec = unmergedData[i];\n const origRecord = result[index + i];\n if (unmergedRec.cellMergeMeta?.get(col.field)) {\n // clone of object, since we don't want to pollute the original fully merged collection.\n const objCopy = {\n recordRef: origRecord.recordRef,\n ghostRecord: origRecord.ghostRecord,\n cellMergeMeta: new Map<string, IMergeByResult>(origRecord.cellMergeMeta.entries())\n };\n // update copy with new meta from unmerged data record, but just for this column\n objCopy.cellMergeMeta?.set(col.field, unmergedRec.cellMergeMeta.get(col.field));\n result[index + i] = objCopy;\n } else {\n // this is the unmerged record, with no merge metadata\n result[index + i] = unmergedRec;\n }\n }\n }\n });\n return result;\n }\n}\n\n/**\n * @hidden\n */\n@Pipe({\n name: 'gridPaging',\n standalone: true\n})\nexport class IgxGridPagingPipe implements PipeTransform {\n private grid = inject<GridType>(IGX_GRID_BASE);\n\n\n public transform(collection: IGroupByResult, enabled: boolean, page = 0, perPage = 15, _: number): IGroupByResult {\n if (!enabled || this.grid.pagingMode !== 'local') {\n return collection;\n }\n const state = {\n index: page,\n recordsPerPage: perPage\n };\n const total = this.grid._totalRecords >= 0 ? this.grid._totalRecords : collection.data?.length;\n DataUtil.correctPagingState(state, total);\n\n const result = {\n data: DataUtil.page(cloneArray(collection.data), state, total),\n metadata: DataUtil.page(cloneArray(collection.metadata), state, total)\n };\n if (this.grid.page !== state.index) {\n this.grid.page = state.index;\n }\n this.grid.pagingState = state;\n return result;\n }\n}\n\n/**\n * @hidden\n */\n@Pipe({\n name: 'gridFiltering',\n standalone: true\n})\nexport class IgxGridFilteringPipe implements PipeTransform {\n private grid = inject<GridType>(IGX_GRID_BASE);\n\n\n public transform(collection: any[], expressionsTree: IFilteringExpressionsTree,\n filterStrategy: IFilteringStrategy,\n advancedExpressionsTree: IFilteringExpressionsTree, id: string, pipeTrigger: number, filteringPipeTrigger: number, pinned?) {\n const state = {\n expressionsTree,\n strategy: filterStrategy,\n advancedExpressionsTree\n };\n\n if (FilteringExpressionsTree.empty(state.expressionsTree) && FilteringExpressionsTree.empty(state.advancedExpressionsTree)) {\n return collection;\n }\n\n const result = FilterUtil.filter(cloneArray(collection), state, this.grid);\n this.grid.setFilteredData(result, pinned);\n return result;\n }\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n Input,\n OnInit,\n TemplateRef,\n ViewChild,\n DOCUMENT,\n inject\n} from '@angular/core';\nimport { NgClass, NgTemplateOutlet, DecimalPipe, PercentPipe, CurrencyPipe, DatePipe } from '@angular/common';\nimport {\n IgxColumnFormatterPipe,\n IgxGridCellComponent,\n IgxGridCellImageAltPipe,\n IgxStringReplacePipe\n} from 'igniteui-angular/grids/core';\nimport { HammerGesturesManager } from 'igniteui-angular/core';\nimport { FormsModule, ReactiveFormsModule } from '@angular/forms';\nimport { IgxChipComponent } from 'igniteui-angular/chips';\nimport { IgxDateTimeEditorDirective, IgxFocusDirective, IgxTextHighlightDirective, IgxTooltipDirective, IgxTooltipTargetDirective } from 'igniteui-angular/directives';\nimport { IgxIconComponent } from 'igniteui-angular/icon';\nimport { IgxInputDirective, IgxInputGroupComponent, IgxPrefixDirective, IgxSuffixDirective } from 'igniteui-angular/input-group';\nimport { IgxCheckboxComponent } from 'igniteui-angular/checkbox';\nimport { IgxDatePickerComponent } from 'igniteui-angular/date-picker';\nimport { IgxTimePickerComponent } from 'igniteui-angular/time-picker';\n\n@Component({\n changeDetection: ChangeDetectionStrategy.OnPush,\n selector: 'igx-expandable-grid-cell',\n templateUrl: 'expandable-cell.component.html',\n providers: [HammerGesturesManager],\n imports: [IgxChipComponent, IgxTextHighlightDirective, IgxIconComponent, NgClass, FormsModule, ReactiveFormsModule, IgxInputGroupComponent, IgxInputDirective, IgxFocusDirective, IgxCheckboxComponent, IgxDatePickerComponent, IgxTimePickerComponent, IgxDateTimeEditorDirective, IgxPrefixDirective, IgxSuffixDirective, NgTemplateOutlet, IgxTooltipTargetDirective, IgxTooltipDirective, IgxGridCellImageAltPipe, IgxStringReplacePipe, IgxColumnFormatterPipe, DecimalPipe, PercentPipe, CurrencyPipe, DatePipe]\n})\nexport class IgxGridExpandableCellComponent extends IgxGridCellComponent implements OnInit {\n public document = inject(DOCUMENT);\n\n /**\n * @hidden\n */\n @Input()\n public expanded = false;\n\n @ViewChild('indicator', { read: ElementRef })\n public indicator: ElementRef;\n\n @ViewChild('indentationDiv', { read: ElementRef })\n public indentationDiv: ElementRef;\n\n /**\n * @hidden\n */\n @ViewChild('defaultExpandedTemplate', { read: TemplateRef, static: true })\n protected defaultExpandedTemplate: TemplateRef<any>;\n\n /**\n * @hidden\n */\n @ViewChild('defaultCollapsedTemplate', { read: TemplateRef, static: true })\n protected defaultCollapsedTemplate: TemplateRef<any>;\n\n /**\n * @hidden\n */\n public toggle(event: Event) {\n event.stopPropagation();\n const expansionState = this.grid.gridAPI.get_row_expansion_state(this.intRow.data);\n this.grid.gridAPI.set_row_expansion_state(this.intRow.key, !expansionState, event);\n }\n\n /**\n * @hidden\n */\n public onIndicatorFocus() {\n this.grid.gridAPI.update_cell(this.grid.crudService.cell);\n }\n\n /**\n * @hidden\n */\n public override calculateSizeToFit(range: any): number {\n let leftPadding = 0;\n if (this.indentationDiv) {\n const indentationStyle = this.document.defaultView.getComputedStyle(this.indentationDiv.nativeElement);\n leftPadding = parseFloat(indentationStyle.paddingLeft);\n }\n const contentWidth = this.platformUtil.getNodeSizeViaRange(range, this.nativeElement);\n return contentWidth + leftPadding;\n }\n\n /**\n * @hidden\n */\n public get iconTemplate() {\n if (this.expanded) {\n return this.grid.rowExpandedIndicatorTemplate || this.defaultExpandedTemplate;\n } else {\n return this.grid.rowCollapsedIndicatorTemplate || this.defaultCollapsedTemplate;\n }\n }\n\n /**\n * @hidden\n */\n public get showExpanderIndicator() {\n const isGhost = this.intRow.pinned && this.intRow.disabled;\n return !this.editMode && (!this.intRow.pinned || isGhost);\n }\n}\n","<ng-template #defaultPinnedIndicator>\n @if (displayPinnedChip) {\n <igx-chip class=\"igx-grid__td--pinned-chip\" [disabled]=\"true\" [style.--ig-size]=\"1\">{{ grid.resourceStrings.igx_grid_pinned_row_indicator }}</igx-chip>\n }\n</ng-template>\n<ng-template #defaultCell>\n @if (column.dataType !== 'boolean' && column.dataType !== 'image' || (column.dataType === 'boolean' && this.formatter)) {\n <div\n igxTextHighlight class=\"igx-grid__td-text\"\n [cssClass]=\"highlightClass\"\n [class.igx-grid__td--number]=\"column.dataType === 'number' || column.dataType === 'percent' || column.dataType === 'currency'\"\n [activeCssClass]=\"activeHighlightClass\"\n [groupName]=\"gridID\"\n [value]=\"formatter ? (value | columnFormatter:formatter:rowData)\n : column.dataType === 'number'\n ? (value | number:column.pipeArgs.digitsInfo:grid.locale)\n : (column.dataType === 'date' || column.dataType === 'time' || column.dataType === 'dateTime')\n ? (value | date:column.pipeArgs.format:column.pipeArgs.timezone:grid.locale)\n : column.dataType === 'currency'\n ? (value | currency:currencyCode:column.pipeArgs.display:column.pipeArgs.digitsInfo:grid.locale)\n : column.dataType === 'percent'\n ? (value | percent:column.pipeArgs.digitsInfo:grid.locale)\n : value\"\n [row]=\"rowData\"\n [column]=\"this.column.field\"\n [containerClass]=\"'igx-grid__td-text'\"\n [metadata]=\"searchMetadata\">\n {{ formatter ? (value | columnFormatter:formatter:rowData) : column.dataType === \"number\"\n ? (value | number:column.pipeArgs.digitsInfo:grid.locale) : (column.dataType === 'date' || column.dataType === 'time' || column.dataType === 'dateTime')\n ? (value | date:column.pipeArgs.format:column.pipeArgs.timezone:grid.locale) : column.dataType === 'currency'\n ? (value | currency:currencyCode:column.pipeArgs.display:column.pipeArgs.digitsInfo:grid.locale) : column.dataType === 'percent'\n ? (value | percent:column.pipeArgs.digitsInfo:grid.locale) : value}}</div>\n }\n\n @if (column.dataType === 'boolean' && !this.formatter) {\n <div [class.igx-grid__td--bool]=\"column.dataType === 'boolean'\">\n <igx-icon\n family=\"default\"\n [name]=\"value ? 'confirm' : 'close'\"\n [ngClass]=\"{ 'igx-icon--success': value, 'igx-icon--error': !value }\">\n </igx-icon>\n </div>\n }\n @if (column.dataType === 'image') {\n <img [src]=\"value\" [alt]=\"value | igxCellImageAlt\" />\n }\n</ng-template>\n<ng-template #addRowCell let-cell=\"cell\">\n @if (column.dataType !== 'boolean' || (column.dataType === 'boolean' && this.formatter)) {\n <div\n igxTextHighlight class=\"igx-grid__td-text\"\n [cssClass]=\"highlightClass\"\n [activeCssClass]=\"activeHighlightClass\"\n [groupName]=\"gridID\"\n [value]=\"formatter ? (value | columnFormatter:formatter:rowData) : column.dataType === 'number' ?\n (value | number:column.pipeArgs.digitsInfo:grid.locale) : (column.dataType === 'date' || column.dataType === 'time' || column.dataType === 'dateTime') ?\n (value | date:column.pipeArgs.format:column.pipeArgs.timezone:grid.locale) : column.dataType === 'currency'?\n (value | currency:currencyCode:column.pipeArgs.display:column.pipeArgs.digitsInfo:grid.locale) : column.dataType === 'percent' ?\n (value | percent:column.pipeArgs.digitsInfo:grid.locale) : value\"\n [row]=\"rowData\"\n [column]=\"this.column.field\"\n [containerClass]=\"'igx-grid__td-text'\"\n [metadata]=\"searchMetadata\">\n {{ value ? value : (column.header || column.field) }}\n </div>\n }\n</ng-template>\n<ng-template #inlineEditor let-cell=\"cell\">\n @if (column.dataType === 'string' || column.dataType === 'image') {\n <ng-container [formGroup]=\"formGroup\">\n <igx-input-group [style.--ig-size]=\"1\" >\n <input\n igxInput\n [attr.aria-describedby]=\"ariaDescribeBy\"\n [attr.aria-invalid]=\"isInvalid\"\n [igxFocus]=\"true\"\n [formControl]=\"formControl\"\n (compositionstart)=\"grid.crudService.isInCompositionMode = true\"\n (compositionend)=\"grid.crudService.isInCompositionMode = false\"\n />\n </igx-input-group>\n </ng-container>\n }\n @if (column.dataType === 'number') {\n <igx-input-group [style.--ig-size]=\"1\" [formGroup]=\"formGroup\">\n <input\n igxInput\n [attr.aria-describedby]=\"ariaDescribeBy\"\n [attr.aria-invalid]=\"isInvalid\"\n [igxFocus]=\"true\"\n [step]=\"step\"\n type=\"number\"\n [formControl]=\"formControl\"\n />\n </igx-input-group>\n }\n @if (column.dataType === 'boolean') {\n <ng-container [formGroup]=\"formGroup\">\n <igx-checkbox\n [checked]=\"editValue\"\n [igxFocus]=\"true\"\n [disableRipple]=\"true\"\n [formControl]=\"formControl\"\n ></igx-checkbox>\n </ng-container>\n }\n @if (column.dataType === 'date') {\n <ng-container [formGroup]=\"formGroup\">\n <igx-date-picker\n [style.width.%]=\"100\"\n [outlet]=\"grid.outlet\"\n mode=\"dropdown\"\n [locale]=\"grid.locale\"\n [weekStart]=\"column.pipeArgs.weekStart\"\n [(value)]=\"editValue\"\n [igxFocus]=\"true\"\n [formControl]=\"formControl\"\n [inputFormat]=\"column.editorOptions?.dateTimeFormat\"\n [displayFormat]=\"column.pipeArgs.format\"\n >\n </igx-date-picker>\n </ng-container>\n }\n @if (column.dataType === 'time') {\n <ng-container [formGroup]=\"formGroup\">\n <igx-time-picker\n [style.width.%]=\"100\"\n [outlet]=\"grid.outlet\"\n mode=\"dropdown\"\n [locale]=\"grid.locale\"\n [(value)]=\"editValue\"\n [igxFocus]=\"true\"\n [formControl]=\"formControl\"\n [inputFormat]=\"column.editorOptions?.dateTimeFormat\"\n [displayFormat]=\"column.pipeArgs.format\"\n ></igx-time-picker>\n </ng-container>\n }\n @if (column.dataType === 'dateTime') {\n <igx-input-group [formGroup]=\"formGroup\">\n <input\n type=\"text\"\n [attr.aria-describedby]=\"ariaDescribeBy\"\n [attr.aria-invalid]=\"isInvalid\"\n [formControl]=\"formControl\"\n igxInput\n [locale]=\"grid.locale\"\n [igxDateTimeEditor]=\"column.editorOptions?.dateTimeFormat\"\n [defaultFormatType]=\"column.dataType\"\n [displayFormat]=\"column.pipeArgs.format\"\n [igxFocus]=\"true\"\n />\n </igx-input-group>\n }\n @if (column.dataType === 'currency') {\n <igx-input-group [style.--ig-size]=\"1\" [formGroup]=\"formGroup\">\n @if (grid.currencyPositionLeft) {\n <igx-prefix>{{ currencyCodeSymbol }}</igx-prefix>\n }\n <input\n igxInput\n [attr.aria-describedby]=\"ariaDescribeBy\"\n [attr.aria-invalid]=\"isInvalid\"\n [igxFocus]=\"true\"\n [step]=\"step\"\n typ