UNPKG

igniteui-angular

Version:

Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps

1,001 lines (995 loc) 211 kB
import * as i0 from '@angular/core'; import { Injectable, inject, TemplateRef, Directive, Pipe, Input, ChangeDetectionStrategy, Component, forwardRef, HostBinding, ViewChildren, IterableDiffers, booleanAttribute, ContentChild, ViewChild, CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { NgClass, NgStyle, NgTemplateOutlet, DecimalPipe, PercentPipe, CurrencyPipe, DatePipe } from '@angular/common'; import { GridBaseAPIService, IgxGridSelectionService, GridSelectionMode, IGX_GRID_BASE, GridSummaryPosition, IgxTreeGridRow, IgxGridCellImageAltPipe, IgxStringReplacePipe, IgxColumnFormatterPipe, IgxRowDirective, IgxRowDragDirective, IgxGridCellComponent, IgxGridNotGroupedPipe, IgxGridCellStylesPipe, IgxGridCellStyleClassesPipe, IgxGridDataMapperPipe, IgxGridTransactionStatePipe, IgxGroupByAreaDirective, IgxGroupAreaDropDirective, IgxGroupByMetaPipe, IgxGridTransaction, IgxGridCell, IgxSummaryRow, IgxGridHeaderRowComponent, IgxGridBodyDirective, IgxGridDragSelectDirective, IgxColumnMovingDropDirective, IgxSummaryRowComponent, IgxRowEditTabStopDirective, IgxGridColumnResizerComponent, IgxHasVisibleColumnsPipe, IgxGridRowPinningPipe, IgxGridRowClassesPipe, IgxGridRowStylesPipe, IgxSummaryDataPipe, IgxGridCRUDService, IgxGridValidationService, IgxGridSummaryService, IgxGridNavigationService, IGX_GRID_SERVICE_BASE, IgxFilteringService, IgxColumnResizingService, IgxRowAddTextDirective, IgxRowEditActionsDirective, IgxRowEditTextDirective, IgxGridFooterComponent, IgxAdvancedFilteringDialogComponent, IgxRowExpandedIndicatorDirective, IgxRowCollapsedIndicatorDirective, IgxHeaderExpandedIndicatorDirective, IgxHeaderCollapsedIndicatorDirective, IgxExcelStyleHeaderIconDirective, IgxSortAscendingHeaderIconDirective, IgxSortDescendingHeaderIconDirective, IgxSortHeaderIconDirective, IgxDragIndicatorIconDirective, IgxRowDragGhostDirective, IgxGridStateDirective, IgxGridPinningActionsComponent, IgxGridEditingActionsComponent, IgxGridActionsBaseDirective, IgxGridActionButtonComponent, IgxGridHeaderComponent, IgxGridHeaderGroupComponent, IgxFilterCellTemplateDirective, IgxSummaryTemplateDirective, IgxCellTemplateDirective, IgxCellValidationErrorDirective, IgxCellHeaderTemplateDirective, IgxCellFooterTemplateDirective, IgxCellEditorTemplateDirective, IgxCollapsibleIndicatorTemplateDirective, IgxColumnComponent, IgxColumnGroupComponent, IgxColumnLayoutComponent, IgxColumnActionsComponent, IgxColumnHidingDirective, IgxColumnPinningDirective, IgxRowSelectorDirective, IgxGroupByRowSelectorDirective, IgxHeadSelectorDirective, IgxCSVTextDirective, IgxExcelTextDirective, IgxGridToolbarActionsComponent, IgxGridToolbarAdvancedFilteringComponent, IgxGridToolbarComponent, IgxGridToolbarExporterComponent, IgxGridToolbarHidingComponent, IgxGridToolbarPinningComponent, IgxGridToolbarTitleComponent, IgxGridToolbarDirective, IgxGridExcelStyleFilteringComponent, IgxExcelStyleHeaderComponent, IgxExcelStyleSortingComponent, IgxExcelStylePinningComponent, IgxExcelStyleHidingComponent, IgxExcelStyleSelectingComponent, IgxExcelStyleClearFiltersComponent, IgxExcelStyleConditionalFilterComponent, IgxExcelStyleMovingComponent, IgxExcelStyleSearchComponent, IgxExcelStyleColumnOperationsTemplateDirective, IgxExcelStyleFilterOperationsTemplateDirective, IgxExcelStyleLoadingValuesTemplateDirective, IgxColumnRequiredValidatorDirective, IgxColumnMinValidatorDirective, IgxColumnMaxValidatorDirective, IgxColumnEmailValidatorDirective, IgxColumnMinLengthValidatorDirective, IgxColumnMaxLengthValidatorDirective, IgxColumnPatternValidatorDirective } from 'igniteui-angular/grids/core'; import { TransactionType, GridColumnDataType, DataUtil, cloneArray, IgxDataRecordSorting, TreeGridFilteringStrategy, FilterUtil, mergeObjects, GridSummaryCalculationMode, FilteringExpressionsTree, cloneHierarchicalArray, HammerGesturesManager, IgxHierarchicalTransactionFactory, DefaultTreeGridMergeStrategy, TransactionEventOrigin, IgxOverlayOutletDirective, IgxSorting, formatDate } from 'igniteui-angular/core'; import { IgxGridExpandableCellComponent, IgxGridBaseDirective, IgxGridCellMergePipe, IgxGridUnmergeActivePipe } from 'igniteui-angular/grids/grid'; import { takeUntil, first } from 'rxjs/operators'; import * as i1 from '@angular/forms'; import { ReactiveFormsModule } from '@angular/forms'; import { IgxChipComponent, IgxChipsAreaComponent } from 'igniteui-angular/chips'; import { IgxTextHighlightDirective, IgxFocusDirective, IgxDateTimeEditorDirective, IgxTooltipTargetDirective, IgxTooltipDirective, IgxTextSelectionDirective, IgxGridForOfDirective, IgxDropDirective, IgxTemplateOutletDirective, IgxToggleDirective, IgxButtonDirective, IgxRippleDirective, IgxScrollInertiaDirective, IgxForOfSyncService, IgxForOfScrollSyncService } from 'igniteui-angular/directives'; import { IgxIconComponent } from 'igniteui-angular/icon'; import { IgxInputGroupComponent, IgxInputDirective, IgxPrefixDirective, IgxSuffixDirective } from 'igniteui-angular/input-group'; import { IgxCheckboxComponent } from 'igniteui-angular/checkbox'; import { IgxDatePickerComponent } from 'igniteui-angular/date-picker'; import { IgxTimePickerComponent } from 'igniteui-angular/time-picker'; import { IgxCircularProgressBarComponent } from 'igniteui-angular/progressbar'; import { IgxSnackbarComponent } from 'igniteui-angular/snackbar'; import { Subject } from 'rxjs'; import { IgxPaginatorComponent, IgxPageNavigationComponent, IgxPageSizeSelectorComponent, IgxPaginatorContentDirective, IgxPaginatorDirective } from 'igniteui-angular/paginator'; class IgxTreeGridAPIService extends GridBaseAPIService { get_all_data(transactions) { const grid = this.grid; let data = grid && grid.flatData ? grid.flatData : []; data = transactions ? grid.dataWithAddedInTransactionRows : data; return data; } get_summary_data() { const grid = this.grid; const data = grid.processedRootRecords?.filter(row => row.isFilteredOutParent === undefined || row.isFilteredOutParent === false) .map(rec => rec.data); if (data && grid.transactions.enabled) { const deletedRows = grid.transactions.getTransactionLog().filter(t => t.type === TransactionType.DELETE).map(t => t.id); deletedRows.forEach(rowID => { const tempData = grid.primaryKey ? data.map(rec => rec[grid.primaryKey]) : data; const index = tempData.indexOf(rowID); if (index !== -1) { data.splice(index, 1); } }); } return data; } allow_expansion_state_change(rowID, expanded) { const grid = this.grid; const row = grid.records.get(rowID); if (row.expanded === expanded || ((!row.children || !row.children.length) && (!grid.loadChildrenOnDemand || (grid.hasChildrenKey && !row.data[grid.hasChildrenKey])))) { return false; } return true; } expand_path_to_record(record) { const grid = this.grid; const expandedStates = grid.expansionStates; while (record.parent) { record = record.parent; const expanded = this.get_row_expansion_state(record); if (!expanded) { expandedStates.set(record.key, true); } } grid.expansionStates = expandedStates; if (grid.rowEditable) { grid.gridAPI.crudService.endEdit(false); } } get_row_expansion_state(record) { const grid = this.grid; const states = grid.expansionStates; const expanded = states.get(record.key); if (expanded !== undefined) { return expanded; } else { return record.children && record.children.length && record.level < grid.expansionDepth; } } should_apply_number_style(column) { return column.dataType === GridColumnDataType.Number && column.visibleIndex !== 0; } deleteRowById(rowID) { const treeGrid = this.grid; const flatDataWithCascadeOnDeleteAndTransactions = treeGrid.primaryKey && treeGrid.foreignKey && treeGrid.cascadeOnDelete && treeGrid.transactions.enabled; if (flatDataWithCascadeOnDeleteAndTransactions) { treeGrid.transactions.startPending(); } const record = super.deleteRowById(rowID); if (flatDataWithCascadeOnDeleteAndTransactions) { treeGrid.transactions.endPending(true); } return record; } deleteRowFromData(rowID, index) { const treeGrid = this.grid; const record = treeGrid.records.get(rowID); if (treeGrid.primaryKey && treeGrid.foreignKey) { index = treeGrid.primaryKey ? treeGrid.data.map(c => c[treeGrid.primaryKey]).indexOf(rowID) : treeGrid.data.indexOf(rowID); super.deleteRowFromData(rowID, index); if (treeGrid.cascadeOnDelete) { if (record && record.children) { for (const child of record.children) { super.deleteRowById(child.key); } } } } else { const collection = record.parent ? record.parent.data[treeGrid.childDataKey] : treeGrid.data; index = treeGrid.primaryKey ? collection.map(c => c[treeGrid.primaryKey]).indexOf(rowID) : collection.indexOf(rowID); const selectedChildren = []; this.get_selected_children(record, selectedChildren); if (selectedChildren.length > 0) { treeGrid.deselectRows(selectedChildren); } if (treeGrid.transactions.enabled) { const path = treeGrid.generateRowPath(rowID); treeGrid.transactions.add({ id: rowID, type: TransactionType.DELETE, newValue: null, path }, collection[index]); } else { collection.splice(index, 1); } this.grid.validation.clear(rowID); } } get_selected_children(record, selectedRowIDs) { const grid = this.grid; if (!record.children || record.children.length === 0) { return; } for (const child of record.children) { if (grid.selectionService.isRowSelected(child.key)) { selectedRowIDs.push(child.key); } this.get_selected_children(child, selectedRowIDs); } } row_deleted_transaction(rowID) { return this.row_deleted_parent(rowID) || super.row_deleted_transaction(rowID); } get_rec_by_id(rowID) { return this.grid.records.get(rowID); } /** * Returns the index of the record in the data view by pk or -1 if not found or primaryKey is not set. * * @param pk * @param dataCollection */ get_rec_index_by_id(pk, dataCollection) { dataCollection = dataCollection || this.grid.data; return this.grid.primaryKey ? dataCollection.findIndex(rec => rec.data[this.grid.primaryKey] === pk) : -1; } addRowToData(data, parentRowID) { if (parentRowID !== undefined && parentRowID !== null) { const state = this.grid.transactions.getState(parentRowID); // we should not allow adding of rows as child of deleted row if (state && state.type === TransactionType.DELETE) { throw Error(`Cannot add child row to deleted parent row`); } const parentRecord = this.grid.records.get(parentRowID); if (!parentRecord) { throw Error('Invalid parent row ID!'); } this.grid.summaryService.clearSummaryCache({ rowID: parentRecord.key }); if (this.grid.primaryKey && this.grid.foreignKey) { data[this.grid.foreignKey] = parentRowID; super.addRowToData(data); } else { const parentData = parentRecord.data; const childKey = this.grid.childDataKey; if (this.grid.transactions.enabled) { const rowId = this.grid.primaryKey ? data[this.grid.primaryKey] : data; const path = []; path.push(...this.grid.generateRowPath(parentRowID)); path.push(parentRowID); this.grid.transactions.add({ id: rowId, path, newValue: data, type: TransactionType.ADD }, null); } else { if (!parentData[childKey]) { parentData[childKey] = []; } parentData[childKey].push(data); } } } else { super.addRowToData(data); } } filterDataByExpressions(expressionsTree) { const records = this.filterTreeDataByExpressions(expressionsTree); const data = []; this.getFlatDataFromFilteredRecords(records, data); return data; } sortDataByExpressions(data, expressions) { const records = DataUtil.sort(cloneArray(data), expressions, this.grid.sortStrategy ?? new IgxDataRecordSorting(), this.grid); return records.map(r => r.data); } filterTreeDataByExpressions(expressionsTree) { let records = this.grid.rootRecords; if (expressionsTree.filteringOperands.length) { const state = { expressionsTree, strategy: this.grid.filterStrategy ?? new TreeGridFilteringStrategy() }; records = FilterUtil.filter(cloneArray(records), state, this.grid); } return records; } update_row_in_array(value, rowID, index) { const grid = this.grid; if (grid.primaryKey && grid.foreignKey) { super.update_row_in_array(value, rowID, index); } else { const record = grid.records.get(rowID); const childData = record.parent ? record.parent.data[grid.childDataKey] : grid.data; index = grid.primaryKey ? childData.map(c => c[grid.primaryKey]).indexOf(rowID) : childData.indexOf(rowID); childData[index] = value; } } /** * Updates related row of provided grid's data source with provided new row value * * @param grid Grid to update data for * @param rowID ID of the row to update * @param rowValueInDataSource Initial value of the row as it is in data source * @param rowCurrentValue Current value of the row as it is with applied previous transactions * @param rowNewValue New value of the row */ updateData(grid, rowID, rowValueInDataSource, rowCurrentValue, rowNewValue) { if (grid.transactions.enabled) { const path = grid.generateRowPath(rowID); const transaction = { id: rowID, type: TransactionType.UPDATE, newValue: rowNewValue, path }; grid.transactions.add(transaction, rowCurrentValue); } else { mergeObjects(rowValueInDataSource, rowNewValue); } } row_deleted_parent(rowID) { const grid = this.grid; if (!grid) { return false; } if ((grid.cascadeOnDelete && grid.foreignKey) || grid.childDataKey) { let node = grid.records.get(rowID); while (node) { const state = grid.transactions.getState(node.key); if (state && state.type === TransactionType.DELETE) { return true; } node = node.parent; } } return false; } getFlatDataFromFilteredRecords(records, data) { if (!records || records.length === 0) { return; } for (const record of records) { if (!record.isFilteredOutParent) { data.push(record); } this.getFlatDataFromFilteredRecords(record.children, data); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridAPIService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridAPIService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridAPIService, decorators: [{ type: Injectable }] }); /** * @hidden */ class IgxRowLoadingIndicatorTemplateDirective { constructor() { this.template = inject(TemplateRef); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxRowLoadingIndicatorTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.2", type: IgxRowLoadingIndicatorTemplateDirective, isStandalone: true, selector: "[igxRowLoadingIndicator]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxRowLoadingIndicatorTemplateDirective, decorators: [{ type: Directive, args: [{ selector: '[igxRowLoadingIndicator]', standalone: true }] }] }); class IgxTreeGridSelectionService extends IgxGridSelectionService { /** Select specified rows. No event is emitted. */ selectRowsWithNoEvent(rowIDs, clearPrevSelection) { if (this.grid && this.grid.rowSelection === GridSelectionMode.multipleCascade) { this.cascadeSelectRowsWithNoEvent(rowIDs, clearPrevSelection); return; } super.selectRowsWithNoEvent(rowIDs, clearPrevSelection); } /** Deselect specified rows. No event is emitted. */ deselectRowsWithNoEvent(rowIDs) { if (this.grid.rowSelection === GridSelectionMode.multipleCascade) { this.cascadeDeselectRowsWithNoEvent(rowIDs); return; } super.deselectRowsWithNoEvent(rowIDs); } emitRowSelectionEvent(newSelection, added, removed, event) { if (this.grid.rowSelection === GridSelectionMode.multipleCascade) { this.emitCascadeRowSelectionEvent(newSelection, added, removed, event); return; } super.emitRowSelectionEvent(newSelection, added, removed, event); } updateCascadeSelectionOnFilterAndCRUD(parents, crudRowID, visibleRowIDs = null) { if (visibleRowIDs === null) { // if the tree grid has flat structure // do not explicitly handle the selection state of the rows if (!parents.size) { return; } visibleRowIDs = new Set(this.getRowIDs(this.allData)); this.rowsToBeSelected = new Set(this.rowSelection); this.rowsToBeIndeterminate = new Set(this.indeterminateRows); if (crudRowID) { this.rowSelection.delete(crudRowID); } } if (!parents.size) { this.rowSelection = new Set(this.rowsToBeSelected); this.indeterminateRows = new Set(this.rowsToBeIndeterminate); // TODO: emit selectionChangeD event, calculate its args through the handleAddedAndRemovedArgs method this.clearHeaderCBState(); this.selectedRowsChange.next(this.getSelectedRows()); return; } const newParents = new Set(); parents.forEach(parent => { this.handleRowSelectionState(parent, visibleRowIDs); if (parent && parent.parent) { newParents.add(parent.parent); } }); this.updateCascadeSelectionOnFilterAndCRUD(newParents, null, visibleRowIDs); } cascadeSelectRowsWithNoEvent(rowIDs, clearPrevSelection) { if (clearPrevSelection) { this.indeterminateRows.clear(); this.rowSelection.clear(); this.calculateRowsNewSelectionState({ added: rowIDs, removed: [] }); } else { const oldSelection = this.getSelectedRows(); const newSelection = [...oldSelection, ...rowIDs]; const args = { oldSelection, newSelection }; // retrieve only the rows without their parents/children which has to be added to the selection this.handleAddedAndRemovedArgs(args); this.calculateRowsNewSelectionState(args); } this.rowSelection = new Set(this.rowsToBeSelected); this.indeterminateRows = new Set(this.rowsToBeIndeterminate); this.clearHeaderCBState(); this.selectedRowsChange.next(this.getSelectedRows()); } cascadeDeselectRowsWithNoEvent(rowIDs) { const args = { added: [], removed: rowIDs }; this.calculateRowsNewSelectionState(args); this.rowSelection = new Set(this.rowsToBeSelected); this.indeterminateRows = new Set(this.rowsToBeIndeterminate); this.clearHeaderCBState(); this.selectedRowsChange.next(this.getSelectedRows()); } get selectionService() { return this.grid.selectionService; } emitCascadeRowSelectionEvent(newSelection, added, removed, event) { const currSelection = this.getSelectedRows(); if (this.areEqualCollections(currSelection, newSelection)) { return; } const args = { owner: this.grid, oldSelection: this.getSelectedRowsData(), newSelection, added, removed, event, cancel: false }; this.calculateRowsNewSelectionState(args, !!this.grid.primaryKey); args.newSelection = Array.from(this.grid.gridAPI.get_all_data().filter(r => this.rowsToBeSelected.has(this.grid.primaryKey ? r[this.grid.primaryKey] : r))); // retrieve rows/parents/children which has been added/removed from the selection this.handleAddedAndRemovedArgs(args); this.grid.rowSelectionChanging.emit(args); if (args.cancel) { return; } const newSelectionIDs = args.newSelection.map(r => this.grid.primaryKey ? r[this.grid.primaryKey] : r); // if args.newSelection hasn't been modified if (this.areEqualCollections(Array.from(this.rowsToBeSelected), newSelectionIDs)) { this.rowSelection = new Set(this.rowsToBeSelected); this.indeterminateRows = new Set(this.rowsToBeIndeterminate); this.clearHeaderCBState(); this.selectedRowsChange.next(this.getSelectedRows()); } else { // select the rows within the modified args.newSelection with no event this.cascadeSelectRowsWithNoEvent(newSelectionIDs, true); } } /** * retrieve the rows which should be added/removed to/from the old selection */ handleAddedAndRemovedArgs(args) { const newSelectionSet = new Set(args.newSelection); const oldSelectionSet = new Set(args.oldSelection); args.removed = args.oldSelection.filter(x => !newSelectionSet.has(x)); args.added = args.newSelection.filter(x => !oldSelectionSet.has(x)); } /** * adds to rowsToBeProcessed set all visible children of the rows which was initially within the rowsToBeProcessed set * * @param rowsToBeProcessed set of the rows (without their parents/children) to be selected/deselected * @param visibleRowIDs list of all visible rowIds * @returns a new set with all direct parents of the rows within rowsToBeProcessed set */ collectRowsChildrenAndDirectParents(rowsToBeProcessed, visibleRowIDs, adding, shouldConvert) { const processedRowsParents = new Set(); Array.from(rowsToBeProcessed).forEach((row) => { const rowID = shouldConvert ? row[this.grid.primaryKey] : row; this.selectDeselectRow(rowID, adding); const rowTreeRecord = this.grid.gridAPI.get_rec_by_id(rowID); const rowAndAllChildren = this.get_all_children(rowTreeRecord); rowAndAllChildren.forEach(r => { if (visibleRowIDs.has(r.key)) { this.selectDeselectRow(r.key, adding); } }); if (rowTreeRecord && rowTreeRecord.parent) { processedRowsParents.add(rowTreeRecord.parent); } }); return processedRowsParents; } /** * populates the rowsToBeSelected and rowsToBeIndeterminate sets * with the rows which will be eventually in selected/indeterminate state */ calculateRowsNewSelectionState(args, shouldConvert = false) { this.rowsToBeSelected = new Set(args.oldSelection ? shouldConvert ? args.oldSelection.map(r => r[this.grid.primaryKey]) : args.oldSelection : this.getSelectedRows()); this.rowsToBeIndeterminate = new Set(this.getIndeterminateRows()); const visibleRowIDs = new Set(this.getRowIDs(this.allData)); const removed = new Set(args.removed); const added = new Set(args.added); if (removed && removed.size) { let removedRowsParents = new Set(); removedRowsParents = this.collectRowsChildrenAndDirectParents(removed, visibleRowIDs, false, shouldConvert); Array.from(removedRowsParents).forEach((parent) => { this.handleParentSelectionState(parent, visibleRowIDs); }); } if (added && added.size) { let addedRowsParents = new Set(); addedRowsParents = this.collectRowsChildrenAndDirectParents(added, visibleRowIDs, true, shouldConvert); Array.from(addedRowsParents).forEach((parent) => { this.handleParentSelectionState(parent, visibleRowIDs); }); } } /** * recursively handle the selection state of the direct and indirect parents */ handleParentSelectionState(treeRow, visibleRowIDs) { if (!treeRow) { return; } this.handleRowSelectionState(treeRow, visibleRowIDs); if (treeRow.parent) { this.handleParentSelectionState(treeRow.parent, visibleRowIDs); } } /** * Handle the selection state of a given row based the selection states of its direct children */ handleRowSelectionState(treeRow, visibleRowIDs) { let visibleChildren = []; if (treeRow && treeRow.children) { visibleChildren = treeRow.children.filter(child => visibleRowIDs.has(child.key)); } if (visibleChildren.length) { if (visibleChildren.every(row => this.rowsToBeSelected.has(row.key))) { this.selectDeselectRow(treeRow.key, true); } else if (visibleChildren.some(row => this.rowsToBeSelected.has(row.key) || this.rowsToBeIndeterminate.has(row.key))) { this.rowsToBeIndeterminate.add(treeRow.key); this.rowsToBeSelected.delete(treeRow.key); } else { this.selectDeselectRow(treeRow.key, false); } } else { // if the children of the row has been deleted and the row was selected do not change its state if (this.isRowSelected(treeRow.key)) { this.selectDeselectRow(treeRow.key, true); } else { this.selectDeselectRow(treeRow.key, false); } } } get_all_children(record) { const children = []; if (record && record.children && record.children.length) { for (const child of record.children) { children.push(...this.get_all_children(child)); children.push(child); } } return children; } selectDeselectRow(rowID, select) { if (select) { this.rowsToBeSelected.add(rowID); this.rowsToBeIndeterminate.delete(rowID); } else { this.rowsToBeSelected.delete(rowID); this.rowsToBeIndeterminate.delete(rowID); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridSelectionService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridSelectionService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridSelectionService, decorators: [{ type: Injectable }] }); /** @hidden */ class IgxTreeGridSummaryPipe { constructor() { this.grid = inject(IGX_GRID_BASE); } transform(flatData, hasSummary, summaryCalculationMode, summaryPosition, showSummaryOnCollapse, _, __) { if (!flatData || !hasSummary || summaryCalculationMode === GridSummaryCalculationMode.rootLevelOnly) { return flatData; } return this.addSummaryRows(this.grid, flatData, summaryPosition, showSummaryOnCollapse); } addSummaryRows(grid, collection, summaryPosition, showSummaryOnCollapse) { const recordsWithSummary = []; const maxSummaryHeight = grid.summaryService.calcMaxSummaryHeight(); for (const record of collection) { recordsWithSummary.push(record); const isCollapsed = !record.expanded && record.children && record.children.length > 0 && showSummaryOnCollapse; if (isCollapsed) { let childData = record.children.filter(r => !r.isFilteredOutParent).map(r => r.data); childData = this.removeDeletedRecord(grid, record.key, childData); const summaries = grid.summaryService.calculateSummaries(record.key, childData); const summaryRecord = { summaries, max: maxSummaryHeight, cellIndentation: record.level + 1 }; recordsWithSummary.push(summaryRecord); } const isExpanded = record.children && record.children.length > 0 && record.expanded; if (summaryPosition === GridSummaryPosition.bottom && !isExpanded) { let childRecord = record; let parent = record.parent; while (parent) { const children = parent.children; if (children[children.length - 1] === childRecord) { let childData = children.filter(r => !r.isFilteredOutParent).map(r => r.data); childData = this.removeDeletedRecord(grid, parent.key, childData); const summaries = grid.summaryService.calculateSummaries(parent.key, childData); const summaryRecord = { summaries, max: maxSummaryHeight, cellIndentation: parent.level + 1 }; recordsWithSummary.push(summaryRecord); childRecord = parent; parent = childRecord.parent; } else { break; } } } else if (summaryPosition === GridSummaryPosition.top && isExpanded) { let childData = record.children.filter(r => !r.isFilteredOutParent).map(r => r.data); childData = this.removeDeletedRecord(grid, record.key, childData); const summaries = grid.summaryService.calculateSummaries(record.key, childData); const summaryRecord = { summaries, max: maxSummaryHeight, cellIndentation: record.level + 1 }; recordsWithSummary.push(summaryRecord); } } return recordsWithSummary; } removeDeletedRecord(grid, rowId, data) { if (!grid.transactions.enabled || !grid.cascadeOnDelete) { return data; } const deletedRows = grid.transactions.getTransactionLog().filter(t => t.type === 'delete').map(t => t.id); let row = grid.records.get(rowId); if (!row && deletedRows.lenght === 0) { return []; } row = row.children ? row : row.parent; while (row) { rowId = row.key; if (deletedRows.indexOf(rowId) !== -1) { return []; } row = row.parent; } deletedRows.forEach(rowID => { const tempData = grid.primaryKey ? data.map(rec => rec[grid.primaryKey]) : data; const index = tempData.indexOf(rowID); if (index !== -1) { data.splice(index, 1); } }); return data; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridSummaryPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridSummaryPipe, isStandalone: true, name: "treeGridSummary" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridSummaryPipe, decorators: [{ type: Pipe, args: [{ name: 'treeGridSummary', standalone: true }] }] }); /** @hidden */ class IgxTreeGridFilteringPipe { constructor() { this.grid = inject(IGX_GRID_BASE); } transform(hierarchyData, expressionsTree, filterStrategy, advancedFilteringExpressionsTree, _, __, pinned) { const state = { expressionsTree, advancedExpressionsTree: advancedFilteringExpressionsTree, strategy: new TreeGridFilteringStrategy() }; if (filterStrategy) { state.strategy = filterStrategy; } if (FilteringExpressionsTree.empty(state.expressionsTree) && FilteringExpressionsTree.empty(state.advancedExpressionsTree)) { this.grid.setFilteredData(null, pinned); return hierarchyData; } const result = this.filter(hierarchyData, state, this.grid); const filteredData = []; this.expandAllRecursive(this.grid, result, this.grid.expansionStates, filteredData); this.grid.setFilteredData(filteredData, pinned); return result; } expandAllRecursive(grid, data, expandedStates, filteredData) { for (const rec of data) { filteredData.push(rec.data); if (rec.children && rec.children.length > 0) { expandedStates.set(rec.key, true); this.expandAllRecursive(grid, rec.children, expandedStates, filteredData); } } } filter(data, state, grid) { return state.strategy.filter(data, state.expressionsTree, state.advancedExpressionsTree, grid); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridFilteringPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridFilteringPipe, isStandalone: true, name: "treeGridFiltering" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridFilteringPipe, decorators: [{ type: Pipe, args: [{ name: 'treeGridFiltering', standalone: true }] }] }); /** * @hidden */ class IgxTreeGridHierarchizingPipe { constructor() { this.grid = inject(IGX_GRID_BASE); } transform(collection, primaryKey, foreignKey, childDataKey, _) { let hierarchicalRecords = []; const treeGridRecordsMap = new Map(); const flatData = []; if (!collection || !collection.length) { this.grid.flatData = collection; this.grid.records = treeGridRecordsMap; this.grid.rootRecords = collection; return collection; } if (childDataKey) { hierarchicalRecords = this.hierarchizeRecursive(collection, primaryKey, childDataKey, undefined, flatData, 0, treeGridRecordsMap); } else if (primaryKey) { hierarchicalRecords = this.hierarchizeFlatData(collection, primaryKey, foreignKey, treeGridRecordsMap, flatData); } this.grid.flatData = this.grid.transactions.enabled ? flatData.filter(rec => { const state = this.grid.transactions.getState(this.getRowID(primaryKey, rec)); return !state || state.type !== TransactionType.ADD; }) : flatData; this.grid.records = treeGridRecordsMap; this.grid.rootRecords = hierarchicalRecords; return hierarchicalRecords; } getRowID(primaryKey, rowData) { return primaryKey ? rowData[primaryKey] : rowData; } /** * Converts a flat array of data into a hierarchical (tree) structure, * preserving the original order of the records among siblings. * * It uses a two-pass approach: * 1. Creates all ITreeGridRecord objects and populates the Map for quick lookup. * 2. Links the records by iterating again, ensuring children are added to * their parent's children array in the order they appeared in the * original collection. * * @param collection The flat array of data to be hierarchized. This is the array whose order should be preserved. * @param primaryKey The name of the property in the data objects that serves as the unique identifier (e.g., 'id'). * @param foreignKey The name of the property in the data objects that links to the parent's primary key (e.g., 'parentId'). * @param map A pre-existing Map object (key: primaryKey value, value: ITreeGridRecord) used to store and quickly look up all created records. * @param flatData The original flat data array. Used for passing to the setIndentationLevels method (not directly used for hierarchy building). * @returns An array of ITreeGridRecord objects representing the root nodes of the hierarchy, ordered as they appeared in the original collection. */ hierarchizeFlatData(collection, primaryKey, foreignKey, map, flatData) { collection.forEach(row => { const record = { key: this.getRowID(primaryKey, row), data: row, children: [] }; map.set(row[primaryKey], record); }); const result = []; collection.forEach(row => { const record = map.get(row[primaryKey]); const parent = map.get(row[foreignKey]); if (parent) { record.parent = parent; parent.children.push(record); } else { result.push(record); } }); this.setIndentationLevels(result, 0, flatData); return result; } setIndentationLevels(collection, indentationLevel, flatData) { for (const record of collection) { record.level = indentationLevel; record.expanded = this.grid.gridAPI.get_row_expansion_state(record); flatData.push(record.data); if (record.children && record.children.length > 0) { this.setIndentationLevels(record.children, indentationLevel + 1, flatData); } } } hierarchizeRecursive(collection, primaryKey, childDataKey, parent, flatData, indentationLevel, map) { const result = []; for (const item of collection) { const record = { key: this.getRowID(primaryKey, item), data: item, parent, level: indentationLevel }; record.expanded = this.grid.gridAPI.get_row_expansion_state(record); flatData.push(item); map.set(record.key, record); record.children = item[childDataKey] ? this.hierarchizeRecursive(item[childDataKey], primaryKey, childDataKey, record, flatData, indentationLevel + 1, map) : undefined; result.push(record); } return result; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridHierarchizingPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridHierarchizingPipe, isStandalone: true, name: "treeGridHierarchizing" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridHierarchizingPipe, decorators: [{ type: Pipe, args: [{ name: 'treeGridHierarchizing', standalone: true }] }] }); /** * @hidden */ class IgxTreeGridFlatteningPipe { constructor() { this.grid = inject(IGX_GRID_BASE); } transform(collection, expandedLevels, expandedStates, _) { const data = []; this.grid.processedRootRecords = collection; this.grid.processedRecords = new Map(); this.getFlatDataRecursive(collection, data, expandedLevels, expandedStates, true); this.grid.processedExpandedFlatData = data.map(r => r.data); return data; } getFlatDataRecursive(collection, data, expandedLevels, expandedStates, parentExpanded) { if (!collection || !collection.length) { return; } for (const hierarchicalRecord of collection) { if (parentExpanded) { data.push(hierarchicalRecord); } hierarchicalRecord.expanded = this.grid.gridAPI.get_row_expansion_state(hierarchicalRecord); this.updateNonProcessedRecordExpansion(this.grid, hierarchicalRecord); this.grid.processedRecords.set(hierarchicalRecord.key, hierarchicalRecord); this.getFlatDataRecursive(hierarchicalRecord.children, data, expandedLevels, expandedStates, parentExpanded && hierarchicalRecord.expanded); } } updateNonProcessedRecordExpansion(grid, record) { const rec = grid.records.get(record.key); rec.expanded = record.expanded; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridFlatteningPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridFlatteningPipe, isStandalone: true, name: "treeGridFlattening" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridFlatteningPipe, decorators: [{ type: Pipe, args: [{ name: 'treeGridFlattening', standalone: true }] }] }); /** @hidden */ class IgxTreeGridSortingPipe { constructor() { this.grid = inject(IGX_GRID_BASE); } transform(hierarchicalData, sortExpressions, groupExpressions, sorting, _, pinned) { const expressions = groupExpressions ? groupExpressions.concat(sortExpressions) : sortExpressions; let result; if (!expressions.length) { result = hierarchicalData; } else { result = DataUtil.treeGridSort(hierarchicalData, expressions, sorting, this.grid); } const filteredSortedData = []; this.flattenTreeGridRecords(result, filteredSortedData); this.grid.setFilteredSortedData(filteredSortedData, pinned); return result; } flattenTreeGridRecords(records, flatData) { if (records && records.length) { for (const record of records) { flatData.push(record.data); this.flattenTreeGridRecords(record.children, flatData); } } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridSortingPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridSortingPipe, isStandalone: true, name: "treeGridSorting" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridSortingPipe, decorators: [{ type: Pipe, args: [{ name: 'treeGridSorting', standalone: true }] }] }); /** @hidden */ class IgxTreeGridPagingPipe { constructor() { this.grid = inject(IGX_GRID_BASE); } transform(collection, enabled, page = 0, perPage = 15, _) { if (!enabled || this.grid.pagingMode !== 'local') { return collection; } const len = this.grid._totalRecords >= 0 ? this.grid._totalRecords : collection.length; const totalPages = Math.ceil(len / perPage); const state = { index: (totalPages > 0 && page >= totalPages) ? totalPages - 1 : page, recordsPerPage: perPage }; const result = DataUtil.page(cloneArray(collection), state, len); this.grid.pagingState = state; this.grid.page = state.index; return result; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridPagingPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridPagingPipe, isStandalone: true, name: "treeGridPaging" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridPagingPipe, decorators: [{ type: Pipe, args: [{ name: 'treeGridPaging', standalone: true }] }] }); /** @hidden */ class IgxTreeGridTransactionPipe { constructor() { this.grid = inject(IGX_GRID_BASE); } transform(collection, _) { if (this.grid.transactions.enabled) { const aggregatedChanges = this.grid.transactions.getAggregatedChanges(true); if (aggregatedChanges.length > 0) { const primaryKey = this.grid.primaryKey; if (!primaryKey) { return collection; } const childDataKey = this.grid.childDataKey; if (childDataKey) { const hierarchicalDataClone = cloneHierarchicalArray(collection, childDataKey); return DataUtil.mergeHierarchicalTransactions(hierarchicalDataClone, aggregatedChanges, childDataKey, this.grid.primaryKey, this.grid.dataCloneStrategy); } else { const flatDataClone = cloneArray(collection); return DataUtil.mergeTransactions(flatDataClone, aggregatedChanges, this.grid.primaryKey, this.grid.dataCloneStrategy); } } } return collection; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridTransactionPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridTransactionPipe, isStandalone: true, name: "treeGridTransaction" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridTransactionPipe, decorators: [{ type: Pipe, args: [{ name: 'treeGridTransaction', standalone: true }] }] }); /** * This pipe maps the original record to ITreeGridRecord format used in TreeGrid. */ class IgxTreeGridNormalizeRecordsPipe { constructor() { this.grid = inject(IGX_GRID_BASE); } transform(_, __) { const primaryKey = this.grid.primaryKey; // using flattened data because origin data may be hierarchical. const flatData = this.grid.flatData; const res = flatData ? flatData.map(rec => ({ rowID: this.grid.primaryKey ? rec[primaryKey] : rec, data: rec, level: 0, children: [] })) : []; return res; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridNormalizeRecordsPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridNormalizeRecordsPipe, isStandalone: true, name: "treeGridNormalizeRecord" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxTreeGridNormalizeRecordsPipe, decorators: [{ type: Pipe, args: [{ name: 'treeGri