• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IgniteUI / igniteui-angular / 13369497402

17 Feb 2025 11:45AM CUT coverage: 91.402% (-0.2%) from 91.622%
13369497402

Pull #14647

github

web-flow
Merge a01f0d0d8 into 0a1fb66db
Pull Request #14647: feat(query-builder): support for nested queries and other improvements

13263 of 15595 branches covered (85.05%)

820 of 977 new or added lines in 19 files covered. (83.93%)

2 existing lines in 2 files now uncovered.

26799 of 29320 relevant lines covered (91.4%)

33752.51 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.53
/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts
1
import { DOCUMENT, formatNumber, getLocaleNumberFormat, NumberFormatStyle } from '@angular/common';
2
import {
3
    AfterContentInit,
4
    AfterViewInit,
5
    booleanAttribute,
6
    ChangeDetectorRef,
7
    ContentChild,
8
    ContentChildren,
9
    createComponent,
10
    Directive,
11
    DoCheck,
12
    ElementRef,
13
    EnvironmentInjector,
14
    EventEmitter,
15
    HostBinding,
16
    HostListener,
17
    Inject,
18
    Injector,
19
    Input,
20
    IterableChangeRecord,
21
    IterableDiffers,
22
    LOCALE_ID,
23
    NgZone,
24
    OnDestroy,
25
    OnInit,
26
    Optional,
27
    Output,
28
    QueryList,
29
    TemplateRef,
30
    ViewChild,
31
    ViewChildren,
32
    ViewContainerRef
33
} from '@angular/core';
34
import { formatDate, resizeObservable } from '../core/utils';
35
import { IgcTrialWatermark } from 'igniteui-trial-watermark';
36
import { Subject, pipe, fromEvent, animationFrameScheduler, merge } from 'rxjs';
37
import { takeUntil, first, filter, throttleTime, map, shareReplay, takeWhile } from 'rxjs/operators';
38
import { cloneArray, mergeObjects, compareMaps, resolveNestedPath, isObject, PlatformUtil } from '../core/utils';
39
import { GridColumnDataType } from '../data-operations/data-util';
40
import { FilteringLogic } from '../data-operations/filtering-expression.interface';
41
import { IGroupByRecord } from '../data-operations/groupby-record.interface';
42
import { IForOfDataChangingEventArgs, IgxGridForOfDirective } from '../directives/for-of/for_of.directive';
43
import { IgxTextHighlightService } from '../directives/text-highlight/text-highlight.service';
44
import { ISummaryExpression } from './summaries/grid-summary';
45
import { IgxGridBodyDirective, RowEditPositionStrategy } from './grid.common';
46
import type { IgxGridToolbarComponent } from './toolbar/grid-toolbar.component';
47
import { IgxToolbarToken } from './toolbar/token';
48
import { IgxRowDirective } from './row.directive';
49
import { IgxOverlayOutletDirective, IgxToggleDirective } from '../directives/toggle/toggle.directive';
50
import {
51
    FilteringExpressionsTree, IFilteringExpressionsTree, FilteringExpressionsTreeType
52
} from '../data-operations/filtering-expressions-tree';
53
import { IFilteringOperation } from '../data-operations/filtering-condition';
54
import { Transaction, TransactionType, TransactionService, State } from '../services/public_api';
55
import {
56
    IgxRowAddTextDirective,
57
    IgxRowEditTemplateDirective,
58
    IgxRowEditTabStopDirective,
59
    IgxRowEditTextDirective,
60
    IgxRowEditActionsDirective
61
} from './grid.rowEdit.directive';
62
import { IgxGridNavigationService, IActiveNode } from './grid-navigation.service';
63
import { IgxFilteringService } from './filtering/grid-filtering.service';
64
import { IgxGridFilteringCellComponent } from './filtering/base/grid-filtering-cell.component';
65
import { WatchChanges } from './watch-changes';
66
import { IgxGridHeaderGroupComponent } from './headers/grid-header-group.component';
67
import { GridResourceStringsEN, IGridResourceStrings } from '../core/i18n/grid-resources';
68
import { IgxGridSummaryService } from './summaries/grid-summary.service';
69
import { IgxSummaryRowComponent } from './summaries/summary-row.component';
70
import { IgxGridSelectionService } from './selection/selection.service';
71
import { IgxEditRow, IgxCell, IgxAddRow } from './common/crud.service';
72
import { ICachedViewLoadedEventArgs, IgxTemplateOutletDirective } from '../directives/template-outlet/template_outlet.directive';
73
import { IgxExcelStyleLoadingValuesTemplateDirective } from './filtering/excel-style/excel-style-search.component';
74
import { IgxGridColumnResizerComponent } from './resizing/resizer.component';
75
import { CharSeparatedValueData } from '../services/csv/char-separated-value-data';
76
import { IgxColumnResizingService } from './resizing/resizing.service';
77
import { FilteringStrategy, IFilteringStrategy } from '../data-operations/filtering-strategy';
78
import {
79
    IgxRowExpandedIndicatorDirective, IgxRowCollapsedIndicatorDirective, IgxHeaderExpandedIndicatorDirective,
80
    IgxHeaderCollapsedIndicatorDirective, IgxExcelStyleHeaderIconDirective, IgxSortAscendingHeaderIconDirective,
81
    IgxSortDescendingHeaderIconDirective, IgxSortHeaderIconDirective
82
} from './grid.directives';
83
import {
84
    GridKeydownTargetType,
85
    GridSelectionMode,
86
    GridSummaryPosition,
87
    GridSummaryCalculationMode,
88
    FilterMode,
89
    ColumnPinningPosition,
90
    RowPinningPosition,
91
    GridPagingMode,
92
    GridValidationTrigger,
93
    Size
94
} from './common/enums';
95
import {
96
    IGridCellEventArgs,
97
    IRowSelectionEventArgs,
98
    IPinColumnEventArgs,
99
    IRowDataEventArgs,
100
    IColumnResizeEventArgs,
101
    IColumnMovingStartEventArgs,
102
    IColumnMovingEventArgs,
103
    IColumnMovingEndEventArgs,
104
    IGridKeydownEventArgs,
105
    IRowDragStartEventArgs,
106
    IRowDragEndEventArgs,
107
    IGridClipboardEvent,
108
    IGridToolbarExportEventArgs,
109
    ISearchInfo,
110
    ICellPosition,
111
    IRowToggleEventArgs,
112
    IColumnSelectionEventArgs,
113
    IPinRowEventArgs,
114
    IGridScrollEventArgs,
115
    IActiveNodeChangeEventArgs,
116
    ISortingEventArgs,
117
    IFilteringEventArgs,
118
    IColumnVisibilityChangedEventArgs,
119
    IColumnVisibilityChangingEventArgs,
120
    IPinColumnCancellableEventArgs,
121
    IGridEditEventArgs,
122
    IRowDataCancelableEventArgs,
123
    IGridEditDoneEventArgs,
124
    IGridRowEventArgs,
125
    IGridContextMenuEventArgs,
126
    IColumnsAutoGeneratedEventArgs
127
} from './common/events';
128
import { IgxAdvancedFilteringDialogComponent } from './filtering/advanced-filtering/advanced-filtering-dialog.component';
129
import {
130
    ColumnType,
131
    GridServiceType,
132
    GridType,
133
    IGridFormGroupCreatedEventArgs,
134
    IGridValidationStatusEventArgs,
135
    IgxGridEmptyTemplateContext,
136
    IgxGridHeaderTemplateContext,
137
    IgxGridRowDragGhostContext,
138
    IgxGridRowEditActionsTemplateContext,
139
    IgxGridRowEditTemplateContext,
140
    IgxGridRowEditTextTemplateContext,
141
    IgxGridRowTemplateContext,
142
    IgxGridTemplateContext,
143
    IgxHeadSelectorTemplateContext,
144
    IgxRowSelectorTemplateContext,
145
    IGX_GRID_SERVICE_BASE,
146
    ISizeInfo,
147
    RowType,
148
    IPinningConfig,
149
    IClipboardOptions
150
} from './common/grid.interface';
151
import { DropPosition } from './moving/moving.service';
152
import { IgxHeadSelectorDirective, IgxRowSelectorDirective } from './selection/row-selectors';
153
import { IgxColumnComponent } from './columns/column.component';
154
import { IgxColumnGroupComponent } from './columns/column-group.component';
155
import { IgxRowDragGhostDirective, IgxDragIndicatorIconDirective } from './row-drag.directive';
156
import { IgxSnackbarComponent } from '../snackbar/snackbar.component';
157
import { IgxActionStripToken } from '../action-strip/token';
158
import { IgxGridRowComponent } from './grid/grid-row.component';
159
import type { IgxPaginatorComponent } from '../paginator/paginator.component';
160
import { IgxPaginatorToken } from '../paginator/token';
161
import { IgxGridHeaderRowComponent } from './headers/grid-header-row.component';
162
import { IgxGridGroupByAreaComponent } from './grouping/grid-group-by-area.component';
163
import { IgxFlatTransactionFactory, TRANSACTION_TYPE } from '../services/transaction/transaction-factory.service';
164
import { ISortingOptions } from './columns/interfaces';
165
import { GridSelectionRange, IgxGridTransaction } from './common/types';
166
import { VerticalAlignment, HorizontalAlignment, PositionSettings, OverlaySettings } from '../services/overlay/utilities';
167
import { IgxOverlayService } from '../services/overlay/overlay';
168
import { ConnectedPositioningStrategy } from '../services/overlay/position/connected-positioning-strategy';
169
import { ContainerPositionStrategy } from '../services/overlay/position/container-position-strategy';
170
import { AbsoluteScrollStrategy } from '../services/overlay/scroll/absolute-scroll-strategy';
171
import { Action, StateUpdateEvent, TransactionEventOrigin } from '../services/transaction/transaction';
172
import { ISortingExpression } from '../data-operations/sorting-strategy';
173
import { IGridSortingStrategy } from './common/strategy';
174
import { IgxGridExcelStyleFilteringComponent } from './filtering/excel-style/excel-style-filtering.component';
175
import { IgxGridHeaderComponent } from './headers/grid-header.component';
176
import { IgxGridFilteringRowComponent } from './filtering/base/grid-filtering-row.component';
177
import { DefaultDataCloneStrategy, IDataCloneStrategy } from '../data-operations/data-clone-strategy';
178
import { IgxGridCellComponent } from './cell.component';
179
import { IgxGridValidationService } from './grid/grid-validation.service';
180
import { getCurrentResourceStrings } from '../core/i18n/resources';
181
import { isTree, recreateTreeFromFields } from '../data-operations/expressions-tree-util';
182

183
interface IMatchInfoCache {
184
    row: any;
185
    index: number;
186
    column: string;
187
    metadata: Map<string, boolean>;
188
}
189

190
let FAKE_ROW_ID = -1;
2✔
191
const DEFAULT_ITEMS_PER_PAGE = 15;
2✔
192
const MINIMUM_COLUMN_WIDTH = 136;
2✔
193
// By default row editing overlay outlet is inside grid body so that overlay is hidden below grid header when scrolling.
194
// In cases when grid has 1-2 rows there isn't enough space in grid body and row editing overlay should be shown above header.
195
// Default row editing overlay height is higher then row height that is why the case is valid also for row with 2 rows.
196
// More accurate calculation is not possible, cause row editing overlay is still not shown and we don't know its height,
197
// but in the same time we need to set row editing overlay outlet before opening the overlay itself.
198
const MIN_ROW_EDITING_COUNT_THRESHOLD = 2;
2✔
199

200
/* blazorIndirectRender
201
   blazorComponent
202
   omitModule
203
   wcSkipComponentSuffix */
204
@Directive()
205
export abstract class IgxGridBaseDirective implements GridType,
2✔
206
    OnInit, DoCheck, OnDestroy, AfterContentInit, AfterViewInit {
207

208
    /**
209
     * Gets/Sets the display time for the row adding snackbar notification.
210
     *
211
     * @remarks
212
     * By default it is 6000ms.
213
     */
214
    @Input()
215
    public snackbarDisplayTime = 6000;
4,058✔
216

217
    /**
218
     * Gets/Sets whether to auto-generate the columns.
219
     *
220
     * @remarks
221
     * The default value is false. When set to true, it will override all columns declared through code or in markup.
222
     * @example
223
     * ```html
224
     * <igx-grid [data]="Data" [autoGenerate]="true"></igx-grid>
225
     * ```
226
     */
227
    @Input({ transform: booleanAttribute })
228
    public autoGenerate = false;
4,058✔
229

230
    /**
231
     * Gets/Sets a list of property keys to be excluded from the generated column collection
232
     * @remarks
233
     * The collection is only used during initialization and changing it will not cause any changes in the generated columns at runtime
234
     * unless the grid is destroyed and recreated. To modify the columns visible in the UI at runtime, please use their
235
     * [hidden](https://www.infragistics.com/products/ignite-ui-angular/docs/typescript/latest/classes/IgxColumnComponent.html#hidden) property.
236
     * @example
237
     * ```html
238
     * <igx-grid data=[Data] [autoGenerate]="true" [autoGenerateExclude]="['ProductName', 'Count']"></igx-grid>
239
     * ```
240
     * ```typescript
241
     * const Data = [{ 'Id': '1', 'ProductName': 'name1', 'Description': 'description1', 'Count': 5 }]
242
     * ```
243
     */
244
    @Input()
245
    public autoGenerateExclude: string[] = [];
4,058✔
246

247
    /**
248
     * Controls whether columns moving is enabled in the grid.
249
     *
250
     */
251
    @Input({ transform: booleanAttribute })
252
    public moving = false;
4,058✔
253

254
    /**
255
     * Gets/Sets a custom template when empty.
256
     *
257
     * @example
258
     * ```html
259
     * <igx-grid [id]="'igx-grid-1'" [data]="Data" [emptyGridTemplate]="myTemplate" [autoGenerate]="true"></igx-grid>
260
     * ```
261
     */
262
    @Input()
263
    public emptyGridTemplate: TemplateRef<void>;
264

265
    /**
266
     * Gets/Sets a custom template for adding row UI when grid is empty.
267
     *
268
     * @example
269
     * ```html
270
     * <igx-grid [id]="'igx-grid-1'" [data]="Data" [addRowEmptyTemplate]="myTemplate" [autoGenerate]="true"></igx-grid>
271
     * ```
272
     */
273
    @Input()
274
    public addRowEmptyTemplate: TemplateRef<void>;
275

276
    /**
277
     * Gets/Sets a custom template when loading.
278
     *
279
     * @example
280
     * ```html
281
     * <igx-grid [id]="'igx-grid-1'" [data]="Data" [loadingGridTemplate]="myTemplate" [autoGenerate]="true"></igx-grid>
282
     * ```
283
     */
284
    @Input()
285
    public loadingGridTemplate: TemplateRef<void>;
286

287
    /**
288
     * Get/Set IgxSummaryRow height
289
     */
290
    @Input()
291
    public set summaryRowHeight(value: number) {
292
        this._summaryRowHeight = value | 0;
23✔
293
        this.summaryService.summaryHeight = value;
23✔
294
        if (!this._init) {
23✔
295
            this.reflow();
22✔
296
        }
297
    }
298

299
    public get summaryRowHeight(): number {
300
        if (this.hasSummarizedColumns && this.rootSummariesEnabled) {
256,229✔
301
            return this._summaryRowHeight || this.summaryService.calcMaxSummaryHeight();
174,145✔
302
        }
303
        return 0;
82,084✔
304
    }
305

306
    /** @hidden @internal */
307
    public get hasColumnsToAutosize() {
308
        return this._columns.some(x => x.width === 'fit-content');
89,335✔
309
    }
310

311
    /**
312
     * Gets/Sets the data clone strategy of the grid when in edit mode.
313
     *
314
     * @example
315
     * ```html
316
     *  <igx-grid #grid [data]="localData" [dataCloneStrategy]="customCloneStrategy"></igx-grid>
317
     * ```
318
     */
319
    @Input()
320
    public get dataCloneStrategy(): IDataCloneStrategy {
321
        return this._dataCloneStrategy;
52,019✔
322
    }
323

324
    public set dataCloneStrategy(strategy: IDataCloneStrategy) {
325
        if (strategy) {
1✔
326
            this._dataCloneStrategy = strategy;
1✔
327
            this._transactions.cloneStrategy = strategy;
1✔
328
        }
329
    }
330

331
    /**
332
     * Controls the copy behavior of the grid.
333
     */
334
    @Input()
335
    public clipboardOptions: IClipboardOptions = {
4,058✔
336
        /**
337
         * Enables/disables the copy behavior
338
         */
339
        enabled: true,
340
        /**
341
         * Include the columns headers in the clipboard output.
342
         */
343
        copyHeaders: true,
344
        /**
345
         * Apply the columns formatters (if any) on the data in the clipboard output.
346
         */
347
        copyFormatters: true,
348
        /**
349
         * The separator used for formatting the copy output. Defaults to `\t`.
350
         */
351
        separator: '\t'
352
    };
353

354
    /**
355
     * Emitted after filtering is performed.
356
     *
357
     * @remarks
358
     * Returns the filtering expressions tree of the column for which filtering was performed.
359
     * @example
360
     * ```html
361
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true"
362
     *              (filteringExpressionsTreeChange)="filteringExprTreeChange($event)"></igx-grid>
363
     * ```
364
     */
365
    @Output()
366
    public filteringExpressionsTreeChange = new EventEmitter<IFilteringExpressionsTree>();
4,058✔
367

368
    /**
369
     * Emitted after advanced filtering is performed.
370
     *
371
     * @remarks
372
     * Returns the advanced filtering expressions tree.
373
     * @example
374
     * ```html
375
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true"
376
     *           (advancedFilteringExpressionsTreeChange)="advancedFilteringExprTreeChange($event)"></igx-grid>
377
     * ```
378
     */
379
    @Output()
380
    public advancedFilteringExpressionsTreeChange = new EventEmitter<IFilteringExpressionsTree>();
4,058✔
381

382
    /**
383
     * Emitted when grid is scrolled horizontally/vertically.
384
     *
385
     * @example
386
     * ```html
387
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true"
388
     *              (gridScroll)="onScroll($event)"></igx-grid>
389
     * ```
390
     */
391
    @Output()
392
    public gridScroll = new EventEmitter<IGridScrollEventArgs>();
4,058✔
393

394
    /* treatAsRef */
395
    /**
396
     * Sets a conditional class selector to the grid's row element.
397
     * Accepts an object literal, containing key-value pairs,
398
     * where the key is the name of the CSS class and the value is
399
     * either a callback function that returns a boolean, or boolean, like so:
400
     * ```typescript
401
     * callback = (row: RowType) => { return row.selected > 6; }
402
     * rowClasses = { 'className' : this.callback };
403
     * ```
404
     * ```html
405
     * <igx-grid #grid [data]="Data" [rowClasses] = "rowClasses" [autoGenerate]="true"></igx-grid>
406
     * ```
407
     *
408
     * @memberof IgxColumnComponent
409
     */
410
    @Input()
411
    public rowClasses: any;
412

413
    /* treatAsRef */
414
    /**
415
     * Sets conditional style properties on the grid row element.
416
     * It accepts an object literal where the keys are
417
     * the style properties and the value is an expression to be evaluated.
418
     * ```typescript
419
     * styles = {
420
     *  background: 'yellow',
421
     *  color: (row: RowType) => row.selected : 'red': 'white'
422
     * }
423
     * ```
424
     * ```html
425
     * <igx-grid #grid [data]="Data" [rowStyles]="styles" [autoGenerate]="true"></igx-grid>
426
     * ```
427
     *
428
     * @memberof IgxColumnComponent
429
     */
430
    @Input()
431
    public rowStyles = null;
4,058✔
432

433
    /**
434
     * Gets/Sets the primary key.
435
     *
436
     * @example
437
     * ```html
438
     * <igx-grid #grid [data]="localData" [primaryKey]="'ProductID'" [autoGenerate]="true"></igx-grid>
439
     * ```
440
     */
441
    @WatchChanges()
442
    @Input()
443
    public get primaryKey(): string {
444
        return this._primaryKey;
19,190,284✔
445
    }
446

447
    public set primaryKey(value: string) {
448
        this._primaryKey = value;
1,716✔
449
        this.checkPrimaryKeyField();
1,716✔
450
    }
451

452
    /* blazorSuppress */
453
    /**
454
     * Gets/Sets a unique values strategy used by the Excel Style Filtering
455
     *
456
     * @remarks
457
     * Provides a callback for loading unique column values on demand.
458
     * If this property is provided, the unique values it generates will be used by the Excel Style Filtering.
459
     * @example
460
     * ```html
461
     * <igx-grid [data]="localData" [filterMode]="'excelStyleFilter'" [uniqueColumnValuesStrategy]="columnValuesStrategy"></igx-grid>
462
     * ```
463
     */
464
    @Input()
465
    public uniqueColumnValuesStrategy: (column: ColumnType,
466
        filteringExpressionsTree: IFilteringExpressionsTree,
467
        done: (values: any[]) => void) => void;
468

469
    /** @hidden @internal */
470
    @ContentChildren(IgxGridExcelStyleFilteringComponent, { read: IgxGridExcelStyleFilteringComponent, descendants: false })
471
    public excelStyleFilteringComponents: QueryList<IgxGridExcelStyleFilteringComponent>;
472

473
    /** @hidden @internal */
474
    public get excelStyleFilteringComponent() {
475
        return this.excelStyleFilteringComponents?.first;
268✔
476
    }
477

478
    /** @hidden @internal */
479
    public get headerGroups() {
480
        return this.theadRow.groups;
3✔
481
    }
482

483
    /**
484
     * Emitted when a cell is clicked.
485
     *
486
     * @remarks
487
     * Returns the `IgxGridCell`.
488
     * @example
489
     * ```html
490
     * <igx-grid #grid (cellClick)="cellClick($event)" [data]="localData" [height]="'305px'" [autoGenerate]="true"></igx-grid>
491
     * ```
492
     */
493
    @Output()
494
    public cellClick = new EventEmitter<IGridCellEventArgs>();
4,058✔
495

496
    /**
497
     * Emitted when a row is clicked.
498
     *
499
     * @remarks
500
     * Returns the `IgxGridRow`.
501
     * @example
502
     * ```html
503
     * <igx-grid #grid (rowClick)="rowClick($event)" [data]="localData" [height]="'305px'" [autoGenerate]="true"></igx-grid>
504
     * ```
505
     */
506
    @Output()
507
    public rowClick = new EventEmitter<IGridRowEventArgs>();
4,058✔
508

509

510
    /**
511
     * Emitted when formGroup is created on edit of row/cell.
512
     *
513
     * @example
514
     * ```html
515
     * <igx-grid #grid (formGroupCreated)="formGroupCreated($event)" [data]="localData" [height]="'305px'" [autoGenerate]="true"></igx-grid>
516
     * ```
517
     */
518
    @Output()
519
    public formGroupCreated = new EventEmitter<IGridFormGroupCreatedEventArgs>();
4,058✔
520

521
    /**
522
     * Emitted when grid's validation status changes.
523
     *
524
     * @example
525
     * ```html
526
     * <igx-grid #grid (validationStatusChange)="validationStatusChange($event)" [data]="localData" [height]="'305px'" [autoGenerate]="true"></igx-grid>
527
     * ```
528
     */
529
    @Output()
530
    public validationStatusChange = new EventEmitter<IGridValidationStatusEventArgs>();
4,058✔
531

532
    /**
533
     * Emitted when a cell is selected.
534
     *
535
     * @remarks
536
     *  Returns the `IgxGridCell`.
537
     * @example
538
     * ```html
539
     * <igx-grid #grid (selected)="onCellSelect($event)" [data]="localData" [height]="'305px'" [autoGenerate]="true"></igx-grid>
540
     * ```
541
     */
542
    @Output()
543
    public selected = new EventEmitter<IGridCellEventArgs>();
4,058✔
544

545
    /**
546
     *  Emitted when `IgxGridRowComponent` is selected.
547
     *
548
     * @example
549
     * ```html
550
     * <igx-grid #grid (rowSelectionChanging)="rowSelectionChanging($event)" [data]="localData" [autoGenerate]="true"></igx-grid>
551
     * ```
552
     */
553
    @Output()
554
    public rowSelectionChanging = new EventEmitter<IRowSelectionEventArgs>();
4,058✔
555

556
    /**
557
     *  Emitted when `IgxColumnComponent` is selected.
558
     *
559
     * @example
560
     * ```html
561
     * <igx-grid #grid (columnSelectionChanging)="columnSelectionChanging($event)" [data]="localData" [autoGenerate]="true"></igx-grid>
562
     * ```
563
     */
564
    @Output()
565
    public columnSelectionChanging = new EventEmitter<IColumnSelectionEventArgs>();
4,058✔
566

567
    /**
568
     * Emitted before `IgxColumnComponent` is pinned.
569
     *
570
     * @remarks
571
     * The index at which to insert the column may be changed through the `insertAtIndex` property.
572
     * @example
573
     * ```typescript
574
     * public columnPinning(event) {
575
     *     if (event.column.field === "Name") {
576
     *       event.insertAtIndex = 0;
577
     *     }
578
     * }
579
     * ```
580
     */
581
    @Output()
582
    public columnPin = new EventEmitter<IPinColumnCancellableEventArgs>();
4,058✔
583

584
    /**
585
     * Emitted after `IgxColumnComponent` is pinned.
586
     *
587
     * @remarks
588
     * The index that the column is inserted at may be changed through the `insertAtIndex` property.
589
     * @example
590
     * ```typescript
591
     * public columnPinning(event) {
592
     *     if (event.column.field === "Name") {
593
     *       event.insertAtIndex = 0;
594
     *     }
595
     * }
596
     * ```
597
     */
598
    @Output()
599
    public columnPinned = new EventEmitter<IPinColumnEventArgs>();
4,058✔
600

601
    /**
602
     * Emitted when cell enters edit mode.
603
     *
604
     * @remarks
605
     * This event is cancelable.
606
     * @example
607
     * ```html
608
     * <igx-grid #grid3 (cellEditEnter)="editStart($event)" [data]="data" [primaryKey]="'ProductID'">
609
     * </igx-grid>
610
     * ```
611
     */
612
    @Output()
613
    public cellEditEnter = new EventEmitter<IGridEditEventArgs>();
4,058✔
614

615
    /**
616
     * Emitted when cell exits edit mode.
617
     *
618
     * @example
619
     * ```html
620
     * <igx-grid #grid3 (cellEditExit)="editExit($event)" [data]="data" [primaryKey]="'ProductID'">
621
     * </igx-grid>
622
     * ```
623
     */
624
    @Output()
625
    public cellEditExit = new EventEmitter<IGridEditDoneEventArgs>();
4,058✔
626

627
    /**
628
     * Emitted when cell has been edited.
629
     *
630
     * @remarks
631
     * Event is fired after editing is completed, when the cell is exiting edit mode.
632
     * This event is cancelable.
633
     * @example
634
     * ```html
635
     * <igx-grid #grid3 (cellEdit)="editDone($event)" [data]="data" [primaryKey]="'ProductID'">
636
     * </igx-grid>
637
     * ```
638
     */
639
    @Output()
640
    public cellEdit = new EventEmitter<IGridEditEventArgs>();
4,058✔
641

642
    /* blazorCSSuppress */
643
    /**
644
     * Emitted after cell has been edited and editing has been committed.
645
     *
646
     * @example
647
     * ```html
648
     * <igx-grid #grid3 (cellEditDone)="editDone($event)" [data]="data" [primaryKey]="'ProductID'">
649
     * </igx-grid>
650
     * ```
651
     */
652
    @Output()
653
    public cellEditDone = new EventEmitter<IGridEditDoneEventArgs>();
4,058✔
654

655
    /**
656
     * Emitted when a row enters edit mode.
657
     *
658
     * @remarks
659
     * Emitted when [rowEditable]="true".
660
     * This event is cancelable.
661
     * @example
662
     * ```html
663
     * <igx-grid #grid3 (rowEditEnter)="editStart($event)" [primaryKey]="'ProductID'" [rowEditable]="true">
664
     * </igx-grid>
665
     * ```
666
     */
667
    @Output()
668
    public rowEditEnter = new EventEmitter<IGridEditEventArgs>();
4,058✔
669

670
    /**
671
     * Emitted when exiting edit mode for a row.
672
     *
673
     * @remarks
674
     * Emitted when [rowEditable]="true" & `endEdit(true)` is called.
675
     * Emitted when changing rows during edit mode, selecting an un-editable cell in the edited row,
676
     * performing paging operation, column resizing, pinning, moving or hitting `Done`
677
     * button inside of the rowEditingOverlay, or hitting the `Enter` key while editing a cell.
678
     * This event is cancelable.
679
     * @example
680
     * ```html
681
     * <igx-grid #grid3 (rowEdit)="editDone($event)" [data]="data" [primaryKey]="'ProductID'" [rowEditable]="true">
682
     * </igx-grid>
683
     * ```
684
     */
685
    @Output()
686
    public rowEdit = new EventEmitter<IGridEditEventArgs>();
4,058✔
687

688
    /**
689
     * Emitted after exiting edit mode for a row and editing has been committed.
690
     *
691
     * @remarks
692
     * Emitted when [rowEditable]="true" & `endEdit(true)` is called.
693
     * Emitted when changing rows during edit mode, selecting an un-editable cell in the edited row,
694
     * performing paging operation, column resizing, pinning, moving or hitting `Done`
695
     * button inside of the rowEditingOverlay, or hitting the `Enter` key while editing a cell.
696
     * @example
697
     * ```html
698
     * <igx-grid #grid3 (rowEditDone)="editDone($event)" [data]="data" [primaryKey]="'ProductID'" [rowEditable]="true">
699
     * </igx-grid>
700
     * ```
701
     */
702
    @Output()
703
    public rowEditDone = new EventEmitter<IGridEditDoneEventArgs>();
4,058✔
704

705
    /**
706
     * Emitted when row editing is canceled.
707
     *
708
     * @remarks
709
     * Emits when [rowEditable]="true" & `endEdit(false)` is called.
710
     * Emitted when changing hitting `Esc` key during cell editing and when click on the `Cancel` button
711
     * in the row editing overlay.
712
     * @example
713
     * ```html
714
     * <igx-grid #grid3 (rowEditExit)="editExit($event)" [data]="data" [primaryKey]="'ProductID'" [rowEditable]="true">
715
     * </igx-grid>
716
     * ```
717
     */
718
    @Output()
719
    public rowEditExit = new EventEmitter<IGridEditDoneEventArgs>();
4,058✔
720

721
    /**
722
     * Emitted when a column is initialized.
723
     *
724
     * @remarks
725
     * Returns the column object.
726
     * @example
727
     * ```html
728
     * <igx-grid #grid [data]="localData" (columnInit)="initColumns($event)" [autoGenerate]="true"></igx-grid>
729
     * ```
730
     */
731
    @Output()
732
    public columnInit = new EventEmitter<IgxColumnComponent>();
4,058✔
733

734
    /* blazorInclude */
735
    /**
736
     * @hidden @internal
737
     */
738
    @Output()
739
    public columnsAutogenerated = new EventEmitter<IColumnsAutoGeneratedEventArgs>();
4,058✔
740

741
    /**
742
     * Emitted before sorting expressions are applied.
743
     *
744
     * @remarks
745
     * Returns an `ISortingEventArgs` object. `sortingExpressions` key holds the sorting expressions.
746
     * @example
747
     * ```html
748
     * <igx-grid #grid [data]="localData" [autoGenerate]="true" (sorting)="sorting($event)"></igx-grid>
749
     * ```
750
     */
751
    @Output()
752
    public sorting = new EventEmitter<ISortingEventArgs>();
4,058✔
753

754
    /**
755
     * Emitted after sorting is completed.
756
     *
757
     * @remarks
758
     * Returns the sorting expression.
759
     * @example
760
     * ```html
761
     * <igx-grid #grid [data]="localData" [autoGenerate]="true" (sortingDone)="sortingDone($event)"></igx-grid>
762
     * ```
763
     */
764
    @Output()
765
    public sortingDone = new EventEmitter<ISortingExpression | ISortingExpression[]>();
4,058✔
766

767
    /**
768
     * Emitted before filtering expressions are applied.
769
     *
770
     * @remarks
771
     * Returns an `IFilteringEventArgs` object. `filteringExpressions` key holds the filtering expressions for the column.
772
     * @example
773
     * ```html
774
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true" (filtering)="filtering($event)"></igx-grid>
775
     * ```
776
     */
777
    @Output()
778
    public filtering = new EventEmitter<IFilteringEventArgs>();
4,058✔
779

780
    /**
781
     * Emitted after filtering is performed through the UI.
782
     *
783
     * @remarks
784
     * Returns the filtering expressions tree of the column for which filtering was performed.
785
     * @example
786
     * ```html
787
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true" (filteringDone)="filteringDone($event)"></igx-grid>
788
     * ```
789
     */
790
    @Output()
791
    public filteringDone = new EventEmitter<IFilteringExpressionsTree>();
4,058✔
792

793
    /* blazorCSSuppress */
794
    /**
795
     * Emitted when a row is added.
796
     *
797
     * @remarks
798
     * Returns the data for the new `IgxGridRowComponent` object.
799
     * @example
800
     * ```html
801
     * <igx-grid #grid [data]="localData" (rowAdded)="rowAdded($event)" [height]="'305px'" [autoGenerate]="true"></igx-grid>
802
     * ```
803
     */
804
    @Output()
805
    public rowAdded = new EventEmitter<IRowDataEventArgs>();
4,058✔
806

807
    /* blazorCSSuppress */
808
    /**
809
     * Emitted when a row is deleted.
810
     *
811
     * @remarks
812
     * Returns an `IRowDataEventArgs` object.
813
     * @example
814
     * ```html
815
     * <igx-grid #grid [data]="localData" (rowDeleted)="rowDeleted($event)" [height]="'305px'" [autoGenerate]="true"></igx-grid>
816
     * ```
817
     */
818
    @Output()
819
    public rowDeleted = new EventEmitter<IRowDataEventArgs>();
4,058✔
820

821
    /**
822
     * Emmited when deleting a row.
823
     *
824
     * @remarks
825
     * This event is cancelable.
826
     * Returns an IRowDataCancellableEventArgs` object.
827
     * @example
828
     * ```html
829
     * <igx-grid #grid [data]="localData" (rowDelete)="rowDelete($event)" [height]="'305px'" [autoGenerate]="true"></igx-grid>
830
     * ```
831
     */
832
    @Output()
833
    public rowDelete = new EventEmitter<IRowDataCancelableEventArgs>();
4,058✔
834

835
    /**
836
     * Emmited just before the newly added row is commited.
837
     *
838
     * @remarks
839
     * This event is cancelable.
840
     * Returns an IRowDataCancellableEventArgs` object.
841
     * @example
842
     * ```html
843
     * <igx-grid #grid [data]="localData" (rowAdd)="rowAdd($event)" [height]="'305px'" [autoGenerate]="true"></igx-grid>
844
     * ```
845
     */
846
    @Output()
847
    public rowAdd = new EventEmitter<IRowDataCancelableEventArgs>();
4,058✔
848

849
    /**
850
     * Emitted after column is resized.
851
     *
852
     * @remarks
853
     * Returns the `IgxColumnComponent` object's old and new width.
854
     * @example
855
     * ```html
856
     * <igx-grid #grid [data]="localData" (columnResized)="resizing($event)" [autoGenerate]="true"></igx-grid>
857
     * ```
858
     */
859
    @Output()
860
    public columnResized = new EventEmitter<IColumnResizeEventArgs>();
4,058✔
861

862
    /**
863
     * Emitted when a cell or row is right clicked.
864
     *
865
     * @remarks
866
     * Returns the `IgxGridCell` object if the immediate context menu target is a cell or an `IgxGridRow` otherwise.
867
     * ```html
868
     * <igx-grid #grid [data]="localData" (contextMenu)="contextMenu($event)" [autoGenerate]="true"></igx-grid>
869
     * ```
870
     */
871
    @Output()
872
    public contextMenu = new EventEmitter<IGridContextMenuEventArgs>();
4,058✔
873

874
    /**
875
     * Emitted when a cell is double clicked.
876
     *
877
     * @remarks
878
     * Returns the `IgxGridCell` object.
879
     * @example
880
     * ```html
881
     * <igx-grid #grid [data]="localData" (doubleClick)="dblClick($event)" [autoGenerate]="true"></igx-grid>
882
     * ```
883
     */
884
    @Output()
885
    public doubleClick = new EventEmitter<IGridCellEventArgs>();
4,058✔
886

887
    /**
888
     * Emitted before column visibility is changed.
889
     *
890
     * @remarks
891
     * Args: { column: any, newValue: boolean }
892
     * @example
893
     * ```html
894
     * <igx-grid (columnVisibilityChanging)="visibilityChanging($event)"></igx-grid>
895
     * ```
896
     */
897
    @Output()
898
    public columnVisibilityChanging = new EventEmitter<IColumnVisibilityChangingEventArgs>();
4,058✔
899

900
    /**
901
     * Emitted after column visibility is changed.
902
     *
903
     * @remarks
904
     * Args: { column: IgxColumnComponent, newValue: boolean }
905
     * @example
906
     * ```html
907
     * <igx-grid (columnVisibilityChanged)="visibilityChanged($event)"></igx-grid>
908
     * ```
909
     */
910
    @Output()
911
    public columnVisibilityChanged = new EventEmitter<IColumnVisibilityChangedEventArgs>();
4,058✔
912

913
    /**
914
     * Emitted when column moving starts.
915
     *
916
     * @remarks
917
     * Returns the moved `IgxColumnComponent` object.
918
     * @example
919
     * ```html
920
     * <igx-grid (columnMovingStart)="movingStart($event)"></igx-grid>
921
     * ```
922
     */
923
    @Output()
924
    public columnMovingStart = new EventEmitter<IColumnMovingStartEventArgs>();
4,058✔
925

926
    /**
927
     * Emitted during the column moving operation.
928
     *
929
     * @remarks
930
     * Returns the source and target `IgxColumnComponent` objects. This event is cancelable.
931
     * @example
932
     * ```html
933
     * <igx-grid (columnMoving)="moving($event)"></igx-grid>
934
     * ```
935
     */
936
    @Output()
937
    public columnMoving = new EventEmitter<IColumnMovingEventArgs>();
4,058✔
938

939
    /**
940
     * Emitted when column moving ends.
941
     *
942
     * @remarks
943
     * Returns the source and target `IgxColumnComponent` objects.
944
     * @example
945
     * ```html
946
     * <igx-grid (columnMovingEnd)="movingEnds($event)"></igx-grid>
947
     * ```
948
     */
949
    @Output()
950
    public columnMovingEnd = new EventEmitter<IColumnMovingEndEventArgs>();
4,058✔
951

952
    /**
953
     * Emitted when keydown is triggered over element inside grid's body.
954
     *
955
     * @remarks
956
     * This event is fired only if the key combination is supported in the grid.
957
     * Return the target type, target object and the original event. This event is cancelable.
958
     * @example
959
     * ```html
960
     *  <igx-grid (gridKeydown)="customKeydown($event)"></igx-grid>
961
     * ```
962
     */
963
    @Output()
964
    public gridKeydown = new EventEmitter<IGridKeydownEventArgs>();
4,058✔
965

966
    /**
967
     * Emitted when start dragging a row.
968
     *
969
     * @remarks
970
     * Return the dragged row.
971
     */
972
    @Output()
973
    public rowDragStart = new EventEmitter<IRowDragStartEventArgs>();
4,058✔
974

975
    /**
976
     * Emitted when dropping a row.
977
     *
978
     * @remarks
979
     * Return the dropped row.
980
     */
981
    @Output()
982
    public rowDragEnd = new EventEmitter<IRowDragEndEventArgs>();
4,058✔
983

984
    /**
985
     * Emitted when a copy operation is executed.
986
     *
987
     * @remarks
988
     * Fired only if copy behavior is enabled through the [`clipboardOptions`]{@link IgxGridBaseDirective#clipboardOptions}.
989
     */
990
    @Output()
991
    public gridCopy = new EventEmitter<IGridClipboardEvent>();
4,058✔
992

993
    /**
994
     * @hidden @internal
995
     */
996
    @Output()
997
    public expansionStatesChange = new EventEmitter<Map<any, boolean>>();
4,058✔
998

999
    /* blazorInclude */
1000
    /** @hidden @internal */
1001
    @Output()
1002
    public selectedRowsChange = new EventEmitter<any[]>();
4,058✔
1003

1004
    /**
1005
     * Emitted when the expanded state of a row gets changed.
1006
     *
1007
     * @example
1008
     * ```html
1009
     * <igx-grid [data]="employeeData" (rowToggle)="rowToggle($event)" [autoGenerate]="true"></igx-grid>
1010
     * ```
1011
     */
1012
    @Output()
1013
    public rowToggle = new EventEmitter<IRowToggleEventArgs>();
4,058✔
1014

1015
    /**
1016
     * Emitted when the pinned state of a row is changed.
1017
     *
1018
     * @example
1019
     * ```html
1020
     * <igx-grid [data]="employeeData" (rowPinning)="rowPin($event)" [autoGenerate]="true"></igx-grid>
1021
     * ```
1022
     */
1023
    @Output()
1024
    public rowPinning = new EventEmitter<IPinRowEventArgs>();
4,058✔
1025

1026
    /**
1027
     * Emitted when the pinned state of a row is changed.
1028
     *
1029
     * @example
1030
     * ```html
1031
     * <igx-grid [data]="employeeData" (rowPinned)="rowPin($event)" [autoGenerate]="true"></igx-grid>
1032
     * ```
1033
     */
1034
    @Output()
1035
    public rowPinned = new EventEmitter<IPinRowEventArgs>();
4,058✔
1036

1037
    /**
1038
     * Emmited when the active node is changed.
1039
     *
1040
     * @example
1041
     * ```
1042
     * <igx-grid [data]="data" [autoGenerate]="true" (activeNodeChange)="activeNodeChange($event)"></igx-grid>
1043
     * ```
1044
     */
1045
    @Output()
1046
    public activeNodeChange = new EventEmitter<IActiveNodeChangeEventArgs>();
4,058✔
1047

1048
    /**
1049
     * Emitted before sorting is performed.
1050
     *
1051
     * @remarks
1052
     * Returns the sorting expressions.
1053
     * @example
1054
     * ```html
1055
     * <igx-grid #grid [data]="localData" [autoGenerate]="true" (sortingExpressionsChange)="sortingExprChange($event)"></igx-grid>
1056
     * ```
1057
     */
1058
    @Output()
1059
    public sortingExpressionsChange = new EventEmitter<ISortingExpression[]>();
4,058✔
1060

1061

1062
    /**
1063
     * Emitted when an export process is initiated by the user.
1064
     *
1065
     * @example
1066
     * ```typescript
1067
     * toolbarExporting(event: IGridToolbarExportEventArgs){
1068
     *     const toolbarExporting = event;
1069
     * }
1070
     * ```
1071
     */
1072
    @Output()
1073
    public toolbarExporting = new EventEmitter<IGridToolbarExportEventArgs>();
4,058✔
1074

1075
    /* End of toolbar related definitions */
1076

1077
    /**
1078
     * Emitted when making a range selection.
1079
     *
1080
     * @remarks
1081
     * Range selection can be made either through drag selection or through keyboard selection.
1082
     */
1083
    @Output()
1084
    public rangeSelected = new EventEmitter<GridSelectionRange>();
4,058✔
1085

1086
    /** Emitted after the ngAfterViewInit hook. At this point the grid exists in the DOM */
1087
    @Output()
1088
    public rendered = new EventEmitter<boolean>();
4,058✔
1089

1090
    /**
1091
     * @hidden @internal
1092
     */
1093
    @Output()
1094
    public localeChange = new EventEmitter<boolean>();
4,058✔
1095

1096
    /**
1097
     * Emitted before the grid's data view is changed because of a data operation, rebinding, etc.
1098
     *
1099
     * @example
1100
     * ```typescript
1101
     *  <igx-grid #grid [data]="localData" [autoGenerate]="true" (dataChanging)='handleDataChangingEvent()'></igx-grid>
1102
     * ```
1103
     */
1104
    @Output()
1105
    public dataChanging = new EventEmitter<IForOfDataChangingEventArgs>();
4,058✔
1106

1107
    /**
1108
     * Emitted after the grid's data view is changed because of a data operation, rebinding, etc.
1109
     *
1110
     * @example
1111
     * ```typescript
1112
     *  <igx-grid #grid [data]="localData" [autoGenerate]="true" (dataChanged)='handleDataChangedEvent()'></igx-grid>
1113
     * ```
1114
     */
1115
    @Output()
1116
    public dataChanged = new EventEmitter<any>();
4,058✔
1117

1118

1119
    /**
1120
     * @hidden @internal
1121
     */
1122
    @ViewChild(IgxSnackbarComponent)
1123
    public addRowSnackbar: IgxSnackbarComponent;
1124

1125
    /**
1126
     * @hidden @internal
1127
     */
1128
    @ViewChild(IgxGridColumnResizerComponent)
1129
    public resizeLine: IgxGridColumnResizerComponent;
1130

1131
    /**
1132
     * @hidden @internal
1133
     */
1134
    @ViewChild('loadingOverlay', { read: IgxToggleDirective, static: true })
1135
    public loadingOverlay: IgxToggleDirective;
1136

1137
    /**
1138
     * @hidden @internal
1139
     */
1140
    @ViewChild('igxLoadingOverlayOutlet', { read: IgxOverlayOutletDirective, static: true })
1141
    public loadingOutlet: IgxOverlayOutletDirective;
1142

1143
    /* reactContentChildren */
1144
    /* blazorInclude */
1145
    /* blazorTreatAsCollection */
1146
    /* blazorCollectionName: ColumnCollection */
1147
    /* ngQueryListName: columnList */
1148
    /**
1149
     * @hidden @internal
1150
     */
1151
    @ContentChildren(IgxColumnComponent, { read: IgxColumnComponent, descendants: true })
1152
    public columnList: QueryList<IgxColumnComponent> = new QueryList<IgxColumnComponent>();
4,058✔
1153

1154
    /* contentChildren */
1155
    /* blazorInclude */
1156
    /* blazorTreatAsCollection */
1157
    /* blazorCollectionName: ActionStripCollection */
1158
    /* blazorCollectionItemName: ActionStrip */
1159
    /* ngQueryListName: actionStripComponents */
1160
    /** @hidden @internal */
1161
    @ContentChildren(IgxActionStripToken)
1162
    protected actionStripComponents: QueryList<IgxActionStripToken>;
1163

1164
    /** @hidden @internal */
1165
    public get actionStrip() {
1166
        return this.actionStripComponents?.first;
4,729✔
1167
    }
1168

1169
    /**
1170
     * @hidden @internal
1171
     */
1172
    @ContentChild(IgxExcelStyleLoadingValuesTemplateDirective, { read: IgxExcelStyleLoadingValuesTemplateDirective, static: true })
1173
    public excelStyleLoadingValuesTemplateDirective: IgxExcelStyleLoadingValuesTemplateDirective;
1174

1175
    /** @hidden @internal */
1176
    @ViewChild('emptyFilteredGrid', { read: TemplateRef, static: true })
1177
    public emptyFilteredGridTemplate: TemplateRef<any>;
1178

1179
    /** @hidden @internal */
1180
    @ViewChild('defaultEmptyGrid', { read: TemplateRef, static: true })
1181
    public emptyGridDefaultTemplate: TemplateRef<any>;
1182

1183
    /**
1184
     * @hidden @internal
1185
     */
1186
    @ViewChild('defaultLoadingGrid', { read: TemplateRef, static: true })
1187
    public loadingGridDefaultTemplate: TemplateRef<any>;
1188

1189
    /**
1190
     * @hidden @internal
1191
     */
1192
    @ViewChild('scrollContainer', { read: IgxGridForOfDirective, static: true })
1193
    public parentVirtDir: IgxGridForOfDirective<any, any[]>;
1194

1195
    /**
1196
     * @hidden
1197
     * @internal
1198
     */
1199
    @ContentChildren(IgxHeadSelectorDirective, { read: TemplateRef, descendants: false })
1200
    public headSelectorsTemplates: QueryList<TemplateRef<IgxHeadSelectorTemplateContext>>;
1201

1202
    /**
1203
     * @hidden
1204
     * @internal
1205
     */
1206
    @ContentChildren(IgxRowSelectorDirective, { read: TemplateRef, descendants: false })
1207
    public rowSelectorsTemplates: QueryList<TemplateRef<IgxRowSelectorTemplateContext>>;
1208

1209
    /**
1210
     * @hidden
1211
     * @internal
1212
     */
1213
    @ContentChildren(IgxRowDragGhostDirective, { read: TemplateRef, descendants: false })
1214
    public dragGhostCustomTemplates: QueryList<TemplateRef<IgxGridRowDragGhostContext>>;
1215

1216

1217
    /**
1218
     * Gets the custom template, if any, used for row drag ghost.
1219
     */
1220
    @Input()
1221
    public get dragGhostCustomTemplate() {
1222
        return this._dragGhostCustomTemplate || this.dragGhostCustomTemplates?.first;
1,979✔
1223
    }
1224

1225
    /**
1226
     * Sets a custom template for the row drag ghost.
1227
     *```html
1228
     * <ng-template #template igxRowDragGhost>
1229
     *    <igx-icon>menu</igx-icon>
1230
     * </ng-template>
1231
     * ```
1232
     * ```typescript
1233
     * @ViewChild("'template'", {read: TemplateRef })
1234
     * public template: TemplateRef<any>;
1235
     * this.grid.dragGhostCustomTemplate = this.template;
1236
     * ```
1237
     */
1238
    public set dragGhostCustomTemplate(template: TemplateRef<IgxGridRowDragGhostContext>) {
1239
        this._dragGhostCustomTemplate = template;
1✔
1240
    }
1241

1242

1243
    /**
1244
     * @hidden @internal
1245
     */
1246
    @ViewChild('verticalScrollContainer', { read: IgxGridForOfDirective, static: true })
1247
    public verticalScrollContainer: IgxGridForOfDirective<any, any[]>;
1248

1249
    /**
1250
     * @hidden @internal
1251
     */
1252
    @ViewChild('verticalScrollHolder', { read: IgxGridForOfDirective, static: true })
1253
    public verticalScroll: IgxGridForOfDirective<any, any[]>;
1254

1255
    /**
1256
     * @hidden @internal
1257
     */
1258
    @ViewChild('scr', { read: ElementRef, static: true })
1259
    public scr: ElementRef;
1260

1261
    /** @hidden @internal */
1262
    @ViewChild('headSelectorBaseTemplate', { read: TemplateRef, static: true })
1263
    public headerSelectorBaseTemplate: TemplateRef<any>;
1264

1265
    /**
1266
     * @hidden @internal
1267
     */
1268
    @ViewChild('footer', { read: ElementRef })
1269
    public footer: ElementRef;
1270

1271
    /** @hidden @internal */
1272
    public get headerContainer() {
1273
        return this.theadRow?.headerForOf;
14,644✔
1274
    }
1275

1276
    /** @hidden @internal */
1277
    public get headerSelectorContainer() {
1278
        return this.theadRow?.headerSelectorContainer;
30,255✔
1279
    }
1280

1281
    /** @hidden @internal */
1282
    public get headerDragContainer() {
1283
        return this.theadRow?.headerDragContainer;
475✔
1284
    }
1285

1286
    /** @hidden @internal */
1287
    public get headerGroupContainer() {
1288
        return this.theadRow?.headerGroupContainer;
29,505✔
1289
    }
1290

1291
    /** @hidden @internal */
1292
    public get filteringRow(): IgxGridFilteringRowComponent {
1293
        return this.theadRow?.filterRow;
1,325✔
1294
    }
1295

1296
    /** @hidden @internal */
1297
    @ViewChild(IgxGridHeaderRowComponent, { static: true })
1298
    public theadRow: IgxGridHeaderRowComponent;
1299

1300
    /** @hidden @internal */
1301
    @ViewChild(IgxGridGroupByAreaComponent)
1302
    public groupArea: IgxGridGroupByAreaComponent;
1303

1304
    /**
1305
     * @hidden @internal
1306
     */
1307
    @ViewChild('tbody', { static: true })
1308
    public tbody: ElementRef;
1309

1310
    @ViewChild(IgxGridBodyDirective, { static: true, read: ElementRef })
1311
    protected tbodyContainer: ElementRef;
1312

1313
    /**
1314
     * @hidden @internal
1315
     */
1316
    @ViewChild('pinContainer', { read: ElementRef })
1317
    public pinContainer: ElementRef;
1318

1319
    /**
1320
     * @hidden @internal
1321
     */
1322
    @ViewChild('tfoot', { static: true })
1323
    public tfoot: ElementRef<HTMLElement>;
1324

1325
    /**
1326
     * @hidden @internal
1327
     */
1328
    @ViewChild('igxRowEditingOverlayOutlet', { read: IgxOverlayOutletDirective, static: true })
1329
    public rowEditingOutletDirective: IgxOverlayOutletDirective;
1330

1331
    /**
1332
     * @hidden @internal
1333
     */
1334
    @ViewChildren(IgxTemplateOutletDirective, { read: IgxTemplateOutletDirective })
1335
    public tmpOutlets: QueryList<any> = new QueryList<any>();
4,058✔
1336

1337
    /**
1338
     * @hidden
1339
     * @internal
1340
     */
1341
    @ViewChild('dragIndicatorIconBase', { read: TemplateRef, static: true })
1342
    public dragIndicatorIconBase: TemplateRef<any>;
1343

1344
    /**
1345
     * @hidden @internal
1346
     */
1347
    @ContentChildren(IgxRowEditTemplateDirective, { descendants: false, read: TemplateRef })
1348
    public rowEditCustomDirectives: QueryList<TemplateRef<IgxGridRowEditTemplateContext>>;
1349

1350
    /**
1351
     * @hidden @internal
1352
     */
1353
    @ContentChildren(IgxRowEditTextDirective, { descendants: false, read: TemplateRef })
1354
    public rowEditTextDirectives: QueryList<TemplateRef<IgxGridRowEditTextTemplateContext>>;
1355

1356
    /**
1357
     * Gets the row edit text template.
1358
     */
1359
    @Input()
1360
    public get rowEditTextTemplate(): TemplateRef<IgxGridRowEditTextTemplateContext> {
1361
        return this._rowEditTextTemplate || this.rowEditTextDirectives?.first;
5,476✔
1362
    }
1363
    /**
1364
     * Sets the row edit text template.
1365
     *```html
1366
     * <ng-template #template igxRowEditText let-rowChangesCount>
1367
     * Changes: {{rowChangesCount}}
1368
     * </ng-template>
1369
     * ```
1370
     *```typescript
1371
     * @ViewChild('template', {read: TemplateRef })
1372
     * public template: TemplateRef<any>;
1373
     * this.grid.rowEditTextTemplate = this.template;
1374
     * ```
1375
     */
1376
    public set rowEditTextTemplate(template: TemplateRef<IgxGridRowEditTextTemplateContext>) {
1377
        this._rowEditTextTemplate = template;
1✔
1378
    }
1379

1380
    /**
1381
     * @hidden @internal
1382
     */
1383
    @ContentChild(IgxRowAddTextDirective, { read: TemplateRef })
1384
    public rowAddText: TemplateRef<IgxGridEmptyTemplateContext>;
1385

1386
    /**
1387
     * Gets the row add text template.
1388
     */
1389
    @Input()
1390
    public get rowAddTextTemplate(): TemplateRef<IgxGridEmptyTemplateContext> {
1391
        return this._rowAddTextTemplate || this.rowAddText;
372✔
1392
    }
1393
    /**
1394
     * Sets the row add text template.
1395
     *```html
1396
     * <ng-template #template igxRowAddText>
1397
     * Adding Row
1398
     * </ng-template>
1399
     * ```
1400
     *```typescript
1401
     * @ViewChild('template', {read: TemplateRef })
1402
     * public template: TemplateRef<any>;
1403
     * this.grid.rowAddTextTemplate = this.template;
1404
     * ```
1405
     */
1406
    public set rowAddTextTemplate(template: TemplateRef<IgxGridEmptyTemplateContext>) {
1407
        this._rowAddTextTemplate = template;
1✔
1408
    }
1409

1410
    /**
1411
     * @hidden @internal
1412
     */
1413
    @ContentChildren(IgxRowEditActionsDirective, { descendants: false, read: TemplateRef })
1414
    public rowEditActionsDirectives: QueryList<TemplateRef<IgxGridRowEditActionsTemplateContext>>;
1415

1416
    /**
1417
     * Gets the row edit actions template.
1418
     */
1419
    @Input()
1420
    public get rowEditActionsTemplate(): TemplateRef<IgxGridRowEditActionsTemplateContext> {
1421
        return this._rowEditActionsTemplate || this.rowEditActionsDirectives?.first;
5,852✔
1422
    }
1423
    /**
1424
     * Sets the row edit actions template.
1425
     *```html
1426
     * <ng-template #template igxRowEditActions let-endRowEdit>
1427
     *     <button type="button" igxButton igxRowEditTabStop (click)="endRowEdit(false)">Cancel</button>
1428
     *     <button type="button" igxButton igxRowEditTabStop (click)="endRowEdit(true)">Apply</button>
1429
     * </ng-template>
1430
     * ```
1431
     *```typescript
1432
     * @ViewChild('template', {read: TemplateRef })
1433
     * public template: TemplateRef<any>;
1434
     * this.grid.rowEditActionsTemplate = this.template;
1435
     * ```
1436
     */
1437
    public set rowEditActionsTemplate(template: TemplateRef<IgxGridRowEditActionsTemplateContext>) {
1438
        this._rowEditActionsTemplate = template;
1✔
1439
    }
1440

1441
    /**
1442
     * The custom template, if any, that should be used when rendering a row expand indicator.
1443
     */
1444
    @ContentChild(IgxRowExpandedIndicatorDirective, { read: TemplateRef })
1445
    protected rowExpandedIndicatorDirectiveTemplate: TemplateRef<IgxGridRowTemplateContext> = null;
4,058✔
1446

1447
    /**
1448
     * Gets the row expand indicator template.
1449
    */
1450
    @Input()
1451
    public get rowExpandedIndicatorTemplate(): TemplateRef<IgxGridRowTemplateContext> {
1452
        return this._rowExpandedIndicatorTemplate || this.rowExpandedIndicatorDirectiveTemplate;
12,189✔
1453
    }
1454

1455
    /**
1456
     * Sets the row expand indicator template.
1457
     *```html
1458
     *<ng-template igxRowExpandedIndicator>
1459
     *  <igx-icon role="button">remove</igx-icon>
1460
     *</ng-template>
1461
     * ```
1462
     *```typescript
1463
     * @ViewChild('template', {read: TemplateRef })
1464
     * public template: TemplateRef<any>;
1465
     * this.grid.rowExpandedIndicatorTemplate = this.template;
1466
     * ```
1467
    */
1468
    public set rowExpandedIndicatorTemplate(template: TemplateRef<IgxGridRowTemplateContext>) {
1469
        this._rowExpandedIndicatorTemplate = template;
890✔
1470
    }
1471

1472
    /**
1473
     * The custom template, if any, that should be used when rendering a row collapse indicator.
1474
     */
1475
    @ContentChild(IgxRowCollapsedIndicatorDirective, { read: TemplateRef })
1476
    protected rowCollapsedIndicatorDirectiveTemplate: TemplateRef<IgxGridRowTemplateContext> = null;
4,058✔
1477

1478
    /**
1479
     * Gets the row collapse indicator template.
1480
    */
1481
    @Input()
1482
    public get rowCollapsedIndicatorTemplate(): TemplateRef<IgxGridRowTemplateContext> {
1483
        return this._rowCollapsedIndicatorTemplate || this.rowCollapsedIndicatorDirectiveTemplate;
49,544✔
1484
    }
1485

1486
    /**
1487
     * Sets the row collapse indicator template.
1488
     *```html
1489
     *<ng-template igxRowCollapsedIndicator>
1490
     *  <igx-icon role="button">add</igx-icon>
1491
     *</ng-template>
1492
     * ```
1493
     *```typescript
1494
     * @ViewChild('template', {read: TemplateRef })
1495
     * public template: TemplateRef<any>;
1496
     * this.grid.rowCollapsedIndicatorTemplate = this.template;
1497
     * ```
1498
    */
1499
    public set rowCollapsedIndicatorTemplate(template: TemplateRef<IgxGridRowTemplateContext>) {
1500
        this._rowCollapsedIndicatorTemplate = template;
890✔
1501
    }
1502

1503
    /**
1504
     * The custom template, if any, that should be used when rendering a header expand indicator.
1505
     */
1506
    @ContentChild(IgxHeaderExpandedIndicatorDirective, { read: TemplateRef })
1507
    protected headerExpandedIndicatorDirectiveTemplate: TemplateRef<IgxGridTemplateContext> = null;
4,058✔
1508

1509
    /**
1510
     * Gets the header expand indicator template.
1511
    */
1512
    @Input()
1513
    public get headerExpandedIndicatorTemplate(): TemplateRef<IgxGridTemplateContext> {
1514
        return this._headerExpandIndicatorTemplate || this.headerExpandedIndicatorDirectiveTemplate;
13,707✔
1515
    }
1516

1517
    /**
1518
     * Sets the header expand indicator template.
1519
     *```html
1520
     *<ng-template igxHeaderExpandedIndicator>
1521
     *  <igx-icon role="button">remove</igx-icon>
1522
     *</ng-template>
1523
     * ```
1524
     *```typescript
1525
     * @ViewChild('template', {read: TemplateRef })
1526
     * public template: TemplateRef<any>;
1527
     * this.grid.headerExpandedIndicatorTemplate = this.template;
1528
     * ```
1529
    */
1530
    public set headerExpandedIndicatorTemplate(template: TemplateRef<IgxGridTemplateContext>) {
1531
        this._headerExpandIndicatorTemplate = template;
890✔
1532
    }
1533

1534
    /**
1535
     * The custom template, if any, that should be used when rendering a header collapse indicator.
1536
     */
1537
    @ContentChild(IgxHeaderCollapsedIndicatorDirective, { read: TemplateRef })
1538
    protected headerCollapsedIndicatorDirectiveTemplate: TemplateRef<IgxGridTemplateContext> = null;
4,058✔
1539

1540
    /**
1541
     * Gets the row collapse indicator template.
1542
    */
1543
    @Input()
1544
    public get headerCollapsedIndicatorTemplate(): TemplateRef<IgxGridTemplateContext> {
1545
        return this._headerCollapseIndicatorTemplate || this.headerCollapsedIndicatorDirectiveTemplate;
1,036✔
1546
    }
1547

1548
    /**
1549
     * Sets the row collapse indicator template.
1550
     *```html
1551
     *<ng-template igxHeaderCollapsedIndicator>
1552
     *  <igx-icon role="button">add</igx-icon>
1553
     *</ng-template>
1554
     * ```
1555
     *```typescript
1556
     * @ViewChild('template', {read: TemplateRef })
1557
     * public template: TemplateRef<any>;
1558
     * this.grid.headerCollapsedIndicatorTemplate = this.template;
1559
     * ```
1560
    */
1561
    public set headerCollapsedIndicatorTemplate(template: TemplateRef<IgxGridTemplateContext>) {
1562
        this._headerCollapseIndicatorTemplate = template;
890✔
1563
    }
1564

1565
    /** @hidden @internal */
1566
    @ContentChild(IgxExcelStyleHeaderIconDirective, { read: TemplateRef })
1567
    public excelStyleHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,058✔
1568

1569
    /**
1570
     * Gets the excel style header icon.
1571
    */
1572
    @Input()
1573
    public get excelStyleHeaderIconTemplate(): TemplateRef<IgxGridHeaderTemplateContext> {
1574
        return this._excelStyleHeaderIconTemplate || this.excelStyleHeaderIconDirectiveTemplate;
12,252✔
1575
    }
1576

1577
    /**
1578
     * Sets the excel style header icon.
1579
     *```html
1580
     *<ng-template #template igxExcelStyleHeaderIcon>
1581
     * <igx-icon>filter_alt</igx-icon>
1582
     *</ng-template>
1583
     * ```
1584
     *```typescript
1585
     * @ViewChild('template', {read: TemplateRef })
1586
     * public template: TemplateRef<any>;
1587
     * this.grid.excelStyleHeaderIconTemplate = this.template;
1588
     * ```
1589
    */
1590
    public set excelStyleHeaderIconTemplate(template: TemplateRef<IgxGridHeaderTemplateContext>) {
1591
        this._excelStyleHeaderIconTemplate = template;
891✔
1592
    }
1593

1594

1595
    /**
1596
     * @hidden
1597
     * @internal
1598
     */
1599
    @ContentChild(IgxSortAscendingHeaderIconDirective, { read: TemplateRef })
1600
    public sortAscendingHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,058✔
1601

1602
    /**
1603
     * The custom template, if any, that should be used when rendering a header sorting indicator when columns are sorted in asc order.
1604
     */
1605
    @Input()
1606
    public get sortAscendingHeaderIconTemplate(): TemplateRef<IgxGridHeaderTemplateContext> {
1607
        return this._sortAscendingHeaderIconTemplate;
1,048✔
1608
    }
1609

1610
    /**
1611
     * Sets a custom template that should be used when rendering a header sorting indicator when columns are sorted in asc order.
1612
     *```html
1613
     * <ng-template #template igxSortAscendingHeaderIcon>
1614
     *    <igx-icon>expand_less</igx-icon>
1615
     * </ng-template>
1616
     * ```
1617
     * ```typescript
1618
     * @ViewChild("'template'", {read: TemplateRef })
1619
     * public template: TemplateRef<any>;
1620
     * this.grid.sortAscendingHeaderIconTemplate = this.template;
1621
     * ```
1622
     */
1623
    public set sortAscendingHeaderIconTemplate(template: TemplateRef<IgxGridHeaderTemplateContext>) {
1624
        this._sortAscendingHeaderIconTemplate = template;
896✔
1625
    }
1626

1627
    /** @hidden @internal */
1628
    @ContentChild(IgxSortDescendingHeaderIconDirective, { read: TemplateRef })
1629
    public sortDescendingHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,058✔
1630

1631
    /**
1632
     * The custom template, if any, that should be used when rendering a header sorting indicator when columns are sorted in desc order.
1633
     */
1634
    @Input()
1635
    public get sortDescendingHeaderIconTemplate() {
1636
        return this._sortDescendingHeaderIconTemplate;
954✔
1637
    }
1638

1639
    /**
1640
     * Sets a custom template that should be used when rendering a header sorting indicator when columns are sorted in desc order.
1641
     *```html
1642
     * <ng-template #template igxSortDescendingHeaderIcon>
1643
     *    <igx-icon>expand_more</igx-icon>
1644
     * </ng-template>
1645
     * ```
1646
     * ```typescript
1647
     * @ViewChild("'template'", {read: TemplateRef })
1648
     * public template: TemplateRef<any>;
1649
     * this.grid.sortDescendingHeaderIconTemplate = this.template;
1650
     * ```
1651
     */
1652
    public set sortDescendingHeaderIconTemplate(template: TemplateRef<IgxGridHeaderTemplateContext>) {
1653
        this._sortDescendingHeaderIconTemplate = template;
896✔
1654
    }
1655

1656
    /**
1657
     * @hidden
1658
     * @internal
1659
     */
1660
    @ContentChild(IgxSortHeaderIconDirective, { read: TemplateRef })
1661
    public sortHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,058✔
1662

1663
    /**
1664
     * Gets custom template, if any, that should be used when rendering a header sorting indicator when columns are not sorted.
1665
     */
1666
    @Input()
1667
    public get sortHeaderIconTemplate(): TemplateRef<IgxGridHeaderTemplateContext> {
1668
        return this._sortHeaderIconTemplate;
29,521✔
1669
    }
1670

1671
    /**
1672
     * Sets a custom template that should be used when rendering a header sorting indicator when columns are not sorted.
1673
     *```html
1674
     * <ng-template #template igxSortHeaderIcon>
1675
     *    <igx-icon>unfold_more</igx-icon>
1676
     * </ng-template>
1677
     * ```
1678
     * ```typescript
1679
     * @ViewChild("'template'", {read: TemplateRef })
1680
     * public template: TemplateRef<any>;
1681
     * this.grid.sortHeaderIconTemplate = this.template;
1682
     * ```
1683
     */
1684
    public set sortHeaderIconTemplate(template: TemplateRef<IgxGridHeaderTemplateContext>) {
1685
        this._sortHeaderIconTemplate = template;
896✔
1686
    }
1687

1688
    /**
1689
     * @hidden
1690
     * @internal
1691
     */
1692
    @ContentChildren(IgxDragIndicatorIconDirective, { read: TemplateRef, descendants: false })
1693
    public dragIndicatorIconTemplates: QueryList<TemplateRef<IgxGridEmptyTemplateContext>>;
1694

1695
    /**
1696
     * @hidden @internal
1697
     */
1698
    @ViewChildren(IgxRowEditTabStopDirective)
1699
    public rowEditTabsDEFAULT: QueryList<IgxRowEditTabStopDirective>;
1700

1701
    /**
1702
     * @hidden @internal
1703
     */
1704
    @ContentChildren(IgxRowEditTabStopDirective, { descendants: true })
1705
    public rowEditTabsCUSTOM: QueryList<IgxRowEditTabStopDirective>;
1706

1707
    /**
1708
     * @hidden @internal
1709
     */
1710
    @ViewChild('rowEditingOverlay', { read: IgxToggleDirective })
1711
    public rowEditingOverlay: IgxToggleDirective;
1712

1713
    /**
1714
     * @hidden @internal
1715
     */
1716
    @HostBinding('attr.tabindex')
1717
    public tabindex = 0;
4,058✔
1718

1719
    /**
1720
     * @hidden @internal
1721
     */
1722
    @HostBinding('attr.role')
1723
    public hostRole = 'grid';
4,058✔
1724

1725
    /* contentChildren */
1726
    /* blazorInclude */
1727
    /* blazorTreatAsCollection */
1728
    /* blazorCollectionName: GridToolbarCollection */
1729
    /* ngQueryListName: toolbar */
1730
    /** @hidden @internal */
1731
    @ContentChildren(IgxToolbarToken)
1732
    public toolbar: QueryList<IgxGridToolbarComponent>;
1733

1734
    /* contentChildren */
1735
    /* blazorInclude */
1736
    /* blazorTreatAsCollection */
1737
    /* blazorCollectionName: PaginatorCollection */
1738
    /* ngQueryListName: paginationComponents */
1739
    /** @hidden @internal */
1740
    @ContentChildren(IgxPaginatorToken)
1741
    protected paginationComponents: QueryList<IgxPaginatorComponent>;
1742

1743
    /**
1744
     * @hidden @internal
1745
     */
1746
    @ViewChild('igxFilteringOverlayOutlet', { read: IgxOverlayOutletDirective, static: true })
1747
    protected _outletDirective: IgxOverlayOutletDirective;
1748

1749
    /**
1750
     * @hidden @internal
1751
     * @igxElementsAnchor
1752
     */
1753
    @ViewChild('sink', { read: ViewContainerRef, static: true })
1754
    public anchor: ViewContainerRef;
1755

1756
    /**
1757
     * @hidden @internal
1758
     */
1759
    @ViewChild('defaultExpandedTemplate', { read: TemplateRef, static: true })
1760
    protected defaultExpandedTemplate: TemplateRef<any>;
1761

1762
    /**
1763
     * @hidden @internal
1764
     */
1765
    @ViewChild('defaultCollapsedTemplate', { read: TemplateRef, static: true })
1766
    protected defaultCollapsedTemplate: TemplateRef<any>;
1767

1768
    /**
1769
     * @hidden @internal
1770
     */
1771
    @ViewChild('defaultESFHeaderIcon', { read: TemplateRef, static: true })
1772
    protected defaultESFHeaderIconTemplate: TemplateRef<any>;
1773

1774
    @ViewChildren('summaryRow', { read: IgxSummaryRowComponent })
1775
    protected _summaryRowList: QueryList<IgxSummaryRowComponent>;
1776

1777
    @ViewChildren('row')
1778
    private _rowList: QueryList<IgxGridRowComponent>;
1779

1780
    @ViewChildren('pinnedRow')
1781
    private _pinnedRowList: QueryList<IgxGridRowComponent>;
1782

1783
    /**
1784
     * @hidden @internal
1785
     */
1786
    @ViewChild('defaultRowEditTemplate', { read: TemplateRef, static: true })
1787
    private defaultRowEditTemplate: TemplateRef<IgxGridRowEditTemplateContext>;
1788

1789
    @ViewChildren(IgxRowDirective, { read: IgxRowDirective })
1790
    private _dataRowList: QueryList<IgxRowDirective>;
1791

1792
    @HostBinding('class.igx-grid')
1793
    protected baseClass = 'igx-grid';
4,058✔
1794

1795

1796
    /**
1797
     * Gets/Sets the resource strings.
1798
     *
1799
     * @remarks
1800
     * By default it uses EN resources.
1801
     */
1802
    @Input()
1803
    public set resourceStrings(value: IGridResourceStrings) {
1804
        this._resourceStrings = Object.assign({}, this._resourceStrings, value);
2✔
1805
    }
1806

1807
    public get resourceStrings(): IGridResourceStrings {
1808
        return this._resourceStrings;
230,210✔
1809
    }
1810

1811
    /**
1812
     * Gets/Sets the filtering logic of the `IgxGridComponent`.
1813
     *
1814
     * @remarks
1815
     * The default is AND.
1816
     * @example
1817
     * ```html
1818
     * <igx-grid [data]="Data" [autoGenerate]="true" [filteringLogic]="filtering"></igx-grid>
1819
     * ```
1820
     */
1821
    @WatchChanges()
1822
    @Input()
1823
    public get filteringLogic() {
1824
        return this._filteringExpressionsTree.operator;
327✔
1825
    }
1826

1827
    public set filteringLogic(value: FilteringLogic) {
1828
        this._filteringExpressionsTree.operator = value;
3✔
1829
    }
1830

1831
    /* mustSetInCodePlatforms: WebComponents;Blazor */
1832
    /**
1833
     * Gets/Sets the filtering state.
1834
     *
1835
     * @example
1836
     * ```html
1837
     * <igx-grid #grid [data]="Data" [autoGenerate]="true" [(filteringExpressionsTree)]="model.filteringExpressions"></igx-grid>
1838
     * ```
1839
     * @remarks
1840
     * Supports two-way binding.
1841
     */
1842
    @WatchChanges()
1843
    @Input()
1844
    public get filteringExpressionsTree() {
1845
        return this._filteringExpressionsTree;
127,598✔
1846
    }
1847

1848
    public set filteringExpressionsTree(value) {
1849
        if (value && isTree(value)) {
924✔
1850
            for (let index = 0; index < value.filteringOperands.length; index++) {
924✔
1851
                if (!(isTree(value.filteringOperands[index]))) {
476✔
1852
                    const newExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, value.filteringOperands[index].fieldName);
7✔
1853
                    newExpressionsTree.filteringOperands.push(value.filteringOperands[index]);
7✔
1854
                    value.filteringOperands[index] = newExpressionsTree;
7✔
1855
                }
1856
            }
1857

1858
            value.type = FilteringExpressionsTreeType.Regular;
924✔
1859
            if (value && this.columns) {
924!
1860
                this._filteringExpressionsTree = recreateTreeFromFields(value, this.columns) as IFilteringExpressionsTree;
924✔
1861
            } else {
NEW
1862
                this._filteringExpressionsTree = value;
×
1863
            }
1864
            this.filteringPipeTrigger++;
924✔
1865
            this.filteringExpressionsTreeChange.emit(this._filteringExpressionsTree);
924✔
1866

1867
            if (this.filteringService.isFilteringExpressionsTreeEmpty(this._filteringExpressionsTree) &&
924✔
1868
                this.filteringService.isFilteringExpressionsTreeEmpty(this._advancedFilteringExpressionsTree)) {
1869
                this._filteredData = null;
468✔
1870
            }
1871

1872
            this.filteringService.refreshExpressions();
924✔
1873
            this.selectionService.clearHeaderCBState();
924✔
1874
            this.summaryService.clearSummaryCache();
924✔
1875
            this.notifyChanges();
924✔
1876
        }
1877
    }
1878

1879
    /**
1880
     * Gets/Sets the advanced filtering state.
1881
     *
1882
     * @example
1883
     * ```typescript
1884
     * let advancedFilteringExpressionsTree = this.grid.advancedFilteringExpressionsTree;
1885
     * this.grid.advancedFilteringExpressionsTree = logic;
1886
     * ```
1887
     */
1888
    @WatchChanges()
1889
    @Input()
1890
    public get advancedFilteringExpressionsTree() {
1891
        return this._advancedFilteringExpressionsTree;
68,680✔
1892
    }
1893

1894
    public set advancedFilteringExpressionsTree(value) {
1895
        const filteringEventArgs: IFilteringEventArgs = {
62✔
1896
            owner: this,
1897
            filteringExpressions: value,
1898
            cancel: false
1899
        };
1900

1901
        this.filtering.emit(filteringEventArgs);
62✔
1902

1903
        if (filteringEventArgs.cancel) {
62✔
1904
            return;
1✔
1905
        }
1906

1907
        if (value && isTree(value)) {
61✔
1908
            value.type = FilteringExpressionsTreeType.Advanced;
47✔
1909
            this._advancedFilteringExpressionsTree = recreateTreeFromFields(value, this.columns) as IFilteringExpressionsTree;
47✔
1910
            this.filteringPipeTrigger++;
47✔
1911
        } else {
1912
            this._advancedFilteringExpressionsTree = null;
14✔
1913
        }
1914
        this.advancedFilteringExpressionsTreeChange.emit(this._advancedFilteringExpressionsTree);
61✔
1915

1916
        if (this.filteringService.isFilteringExpressionsTreeEmpty(this._filteringExpressionsTree) &&
61✔
1917
            this.filteringService.isFilteringExpressionsTreeEmpty(this._advancedFilteringExpressionsTree)) {
1918
            this._filteredData = null;
19✔
1919
        }
1920

1921
        this.selectionService.clearHeaderCBState();
61✔
1922
        this.summaryService.clearSummaryCache();
61✔
1923
        this.notifyChanges();
61✔
1924

1925
        // Wait for the change detection to update filtered data through the pipes and then emit the event.
1926
        requestAnimationFrame(() => this.filteringDone.emit(this._advancedFilteringExpressionsTree));
61✔
1927
    }
1928

1929
    /**
1930
     * Gets/Sets the locale.
1931
     *
1932
     * @remarks
1933
     * If not set, returns browser's language.
1934
     */
1935
    @Input()
1936
    public get locale(): string {
1937
        return this._locale;
1,534,293✔
1938
    }
1939

1940
    public set locale(value: string) {
1941
        if (value !== this._locale) {
4,075✔
1942
            this._locale = value;
4,069✔
1943
            this._currencyPositionLeft = undefined;
4,069✔
1944
            this.summaryService.clearSummaryCache();
4,069✔
1945
            this.pipeTrigger++;
4,069✔
1946
            this.notifyChanges();
4,069✔
1947
            this.localeChange.emit();
4,069✔
1948
        }
1949
    }
1950

1951
    @Input()
1952
    public get pagingMode() {
1953
        return this._pagingMode;
8,419✔
1954
    }
1955

1956
    public set pagingMode(val: GridPagingMode) {
1957
        this._pagingMode = val;
2✔
1958
        this.pipeTrigger++;
2✔
1959
        this.notifyChanges(true);
2✔
1960
    }
1961

1962
    /** @hidden @internal */
1963
    public get page(): number {
1964
        return this.paginator?.page || 0;
2,218,361✔
1965
    }
1966

1967
    public set page(val: number) {
1968
        if (this.paginator) {
714✔
1969
            this.paginator.page = val;
330✔
1970
        }
1971
    }
1972

1973
    /** @hidden @internal */
1974
    public get perPage(): number {
1975
        return this.paginator?.perPage || DEFAULT_ITEMS_PER_PAGE;
2,218,274✔
1976
    }
1977

1978
    public set perPage(val: number) {
1979
        if (this.paginator) {
20✔
1980
            this.paginator.perPage = val;
20✔
1981
        }
1982
    }
1983

1984
    /**
1985
     * Gets/Sets if the row selectors are hidden.
1986
     *
1987
     * @remarks
1988
     *  By default row selectors are shown
1989
     */
1990
    @WatchChanges()
1991
    @Input({ transform: booleanAttribute })
1992
    public get hideRowSelectors() {
1993
        return this._hideRowSelectors;
31,763✔
1994
    }
1995

1996
    public set hideRowSelectors(value: boolean) {
1997
        this._hideRowSelectors = value;
21✔
1998
        this.notifyChanges(true);
21✔
1999
    }
2000

2001
    /**
2002
     * Gets/Sets whether rows can be moved.
2003
     *
2004
     * @example
2005
     * ```html
2006
     * <igx-grid #grid [rowDraggable]="true"></igx-grid>
2007
     * ```
2008
     */
2009
    @Input({ transform: booleanAttribute })
2010
    public get rowDraggable(): boolean {
2011
        return this._rowDrag && this.hasVisibleColumns;
324,354✔
2012
    }
2013

2014
    public set rowDraggable(val: boolean) {
2015
        this._rowDrag = val;
47✔
2016
        this.notifyChanges(true);
47✔
2017
    }
2018

2019
    /**
2020
     * Gets/Sets the trigger for validators used when editing the grid.
2021
     *
2022
     * @example
2023
     * ```html
2024
     * <igx-grid #grid validationTrigger='blur'></igx-grid>
2025
     * ```
2026
     */
2027
    @Input()
2028
    public validationTrigger: GridValidationTrigger = 'change';
4,058✔
2029

2030
    /**
2031
     * @hidden
2032
     * @internal
2033
     */
2034
    public rowDragging = false;
4,058✔
2035

2036
    /** @hidden @internal */
2037
    public dragRowID = null;
4,058✔
2038

2039
    /**
2040
     * Gets/Sets whether the rows are editable.
2041
     *
2042
     * @remarks
2043
     * By default it is set to false.
2044
     * @example
2045
     * ```html
2046
     * <igx-grid #grid [rowEditable]="true" [primaryKey]="'ProductID'" ></igx-grid>
2047
     * ```
2048
     */
2049
    @WatchChanges()
2050
    @Input({ transform: booleanAttribute })
2051
    public get rowEditable(): boolean {
2052
        return this._rowEditable;
13,291,306✔
2053
    }
2054

2055
    public set rowEditable(val: boolean) {
2056
        if (!this._init) {
534✔
2057
            this.refreshGridState();
35✔
2058
        }
2059
        this._rowEditable = val;
534✔
2060
        this.notifyChanges();
534✔
2061
    }
2062

2063
    /**
2064
     * Gets/Sets the height.
2065
     *
2066
     * @example
2067
     * ```html
2068
     * <igx-grid #grid [data]="Data" [height]="'305px'" [autoGenerate]="true"></igx-grid>
2069
     * ```
2070
     */
2071
    @WatchChanges()
2072
    @HostBinding('style.height')
2073
    @Input()
2074
    public get height(): string | null {
2075
        return this._height;
35,830✔
2076
    }
2077

2078
    public set height(value: string | null) {
2079
        if (this._height !== value) {
3,046✔
2080
            this._height = value;
3,044✔
2081
            this.nativeElement.style.height = value;
3,044✔
2082
            this.notifyChanges(true);
3,044✔
2083
        }
2084
    }
2085

2086
    /**
2087
     * @hidden @internal
2088
     */
2089
    @HostBinding('style.width')
2090
    public get hostWidth() {
2091
        return this._width || this._hostWidth;
32,292✔
2092
    }
2093

2094
    /**
2095
     * Gets/Sets the width of the grid.
2096
     *
2097
     * @example
2098
     * ```typescript
2099
     * let gridWidth = this.grid.width;
2100
     * ```
2101
     */
2102
    @WatchChanges()
2103
    @Input()
2104
    public get width(): string | null {
2105
        return this._width;
143,156✔
2106
    }
2107

2108
    public set width(value: string | null) {
2109
        if (this._width !== value) {
2,057✔
2110
            this._width = value;
2,057✔
2111
            this.nativeElement.style.width = value;
2,057✔
2112
            this.notifyChanges(true);
2,057✔
2113
        }
2114
    }
2115

2116
    /** @hidden @internal */
2117
    public get headerWidth() {
2118
        return parseInt(this.width, 10) - 17;
×
2119
    }
2120

2121
    /**
2122
     * Gets/Sets the row height.
2123
     *
2124
     * @example
2125
     * ```html
2126
     * <igx-grid #grid [data]="localData" [rowHeight]="100" [autoGenerate]="true"></igx-grid>
2127
     * ```
2128
     */
2129
    @WatchChanges()
2130
    @Input()
2131
    public get rowHeight(): number {
2132
        return this._rowHeight ? this._rowHeight : this.defaultRowHeight;
344,749✔
2133
    }
2134

2135
    public set rowHeight(value: number | string) {
2136
        if (typeof value !== 'number') {
2✔
2137
            value = parseInt(value, 10);
1✔
2138
        }
2139
        this._rowHeight = value;
2✔
2140
    }
2141

2142
    /**
2143
     * Gets/Sets the default width of the columns.
2144
     *
2145
     * @example
2146
     * ```html
2147
     * <igx-grid #grid [data]="localData" [columnWidth]="100" [autoGenerate]="true"></igx-grid>
2148
     * ```
2149
     */
2150
    @WatchChanges()
2151
    @Input()
2152
    public get columnWidth(): string {
2153
        return this._columnWidth;
610✔
2154
    }
2155
    public set columnWidth(value: string) {
2156
        this._columnWidth = value;
230✔
2157
        this.columnWidthSetByUser = true;
230✔
2158
        this.notifyChanges(true);
230✔
2159
    }
2160

2161
    /**
2162
     * Get/Sets the message displayed when there are no records.
2163
     *
2164
     * @example
2165
     * ```html
2166
     * <igx-grid #grid [data]="Data" [emptyGridMessage]="'The grid is empty'" [autoGenerate]="true"></igx-grid>
2167
     * ```
2168
     */
2169
    @Input()
2170
    public set emptyGridMessage(value: string) {
2171
        this._emptyGridMessage = value;
×
2172
    }
2173
    public get emptyGridMessage(): string {
2174
        return this._emptyGridMessage || this.resourceStrings.igx_grid_emptyGrid_message;
731✔
2175
    }
2176

2177
    /**
2178
     * Gets/Sets whether the grid is going to show a loading indicator.
2179
     *
2180
     * @example
2181
     * ```html
2182
     * <igx-grid #grid [data]="Data" [isLoading]="true" [autoGenerate]="true"></igx-grid>
2183
     * ```
2184
     */
2185
    @WatchChanges()
2186
    @Input({ transform: booleanAttribute })
2187
    public set isLoading(value: boolean) {
2188
        if (this._isLoading !== value) {
13✔
2189
            this._isLoading = value;
13✔
2190
            if (this.data) {
13✔
2191
                this.evaluateLoadingState();
7✔
2192
            }
2193
        }
2194
        Promise.resolve().then(() => {
13✔
2195
            // wait for the current detection cycle to end before triggering a new one.
2196
            this.notifyChanges();
13✔
2197
        });
2198
    }
2199

2200
    public get isLoading(): boolean {
2201
        return this._isLoading;
93,544✔
2202
    }
2203

2204
    /**
2205
     * Gets/Sets whether the columns should be auto-generated once again after the initialization of the grid
2206
     *
2207
     * @remarks
2208
     * This will allow to bind the grid to remote data and having auto-generated columns at the same time.
2209
     * Note that after generating the columns, this property would be disabled to avoid re-creating
2210
     * columns each time a new data is assigned.
2211
     * @example
2212
     * ```typescript
2213
     *  this.grid.shouldGenerate = true;
2214
     * ```
2215
     * @deprecated in version 18.2.0. Column re-creation now relies on `autoGenerate` instead.
2216
     */
2217
    public get shouldGenerate(): boolean {
2218
        return this.autoGenerate;
×
2219
    }
2220

2221
    public set shouldGenerate(value: boolean) {
2222
        this.autoGenerate = value;
×
2223
    }
2224

2225
    /**
2226
     * Gets/Sets the message displayed when there are no records and the grid is filtered.
2227
     *
2228
     * @example
2229
     * ```html
2230
     * <igx-grid #grid [data]="Data" [emptyGridMessage]="'The grid is empty'" [autoGenerate]="true"></igx-grid>
2231
     * ```
2232
     */
2233
    @Input()
2234
    public set emptyFilteredGridMessage(value: string) {
2235
        this._emptyFilteredGridMessage = value;
×
2236
    }
2237

2238
    public get emptyFilteredGridMessage(): string {
2239
        return this._emptyFilteredGridMessage || this.resourceStrings.igx_grid_emptyFilteredGrid_message;
204✔
2240
    }
2241

2242
    /* mustSetInCodePlatforms: WebComponents;Blazor;React */
2243
    /**
2244
     * Gets/Sets the initial pinning configuration.
2245
     *
2246
     * @remarks
2247
     * Allows to apply pinning the columns to the start or the end.
2248
     * Note that pinning to both sides at a time is not allowed.
2249
     * @example
2250
     * ```html
2251
     * <igx-grid [pinning]="pinningConfig"></igx-grid>
2252
     * ```
2253
     */
2254
    @Input()
2255
    public get pinning() {
2256
        return this._pinning;
2,931,562✔
2257
    }
2258
    public set pinning(value) {
2259
        if (value !== this._pinning) {
126✔
2260
            this.resetCaches();
126✔
2261
        }
2262
        this._pinning = value;
126✔
2263
    }
2264

2265
    /**
2266
     * Gets/Sets if the filtering is enabled.
2267
     *
2268
     * @example
2269
     * ```html
2270
     * <igx-grid #grid [data]="localData" [allowFiltering]="true" [height]="'305px'" [autoGenerate]="true"></igx-grid>
2271
     * ```
2272
     */
2273
    @Input({ transform: booleanAttribute })
2274
    public get allowFiltering() {
2275
        return this._allowFiltering;
406,008✔
2276
    }
2277

2278
    public set allowFiltering(value) {
2279
        if (this._allowFiltering !== value) {
726✔
2280
            this._allowFiltering = value;
715✔
2281
            this.filteringService.registerSVGIcons();
715✔
2282

2283

2284
            this.filteringService.isFilterRowVisible = false;
715✔
2285
            this.filteringService.filteredColumn = null;
715✔
2286

2287
            this.notifyChanges(true);
715✔
2288
        }
2289
    }
2290

2291
    /**
2292
     * Gets/Sets a value indicating whether the advanced filtering is enabled.
2293
     *
2294
     * @example
2295
     * ```html
2296
     * <igx-grid #grid [data]="localData" [allowAdvancedFiltering]="true" [autoGenerate]="true"></igx-grid>
2297
     * ```
2298
     */
2299
    @Input({ transform: booleanAttribute })
2300
    public get allowAdvancedFiltering() {
2301
        return this._allowAdvancedFiltering;
575✔
2302
    }
2303

2304
    public set allowAdvancedFiltering(value) {
2305
        if (this._allowAdvancedFiltering !== value) {
40✔
2306
            this._allowAdvancedFiltering = value;
40✔
2307
            this.filteringService.registerSVGIcons();
40✔
2308

2309
            if (!this._init) {
40✔
2310
                this.notifyChanges(true);
5✔
2311
            }
2312
        }
2313
    }
2314

2315
    /**
2316
     * Gets/Sets the filter mode.
2317
     *
2318
     * @example
2319
     * ```html
2320
     * <igx-grid #grid [data]="localData" [filterMode]="'quickFilter'" [height]="'305px'" [autoGenerate]="true"></igx-grid>
2321
     * ```
2322
     * @remarks
2323
     * By default it's set to FilterMode.quickFilter.
2324
     */
2325
    @Input()
2326
    public get filterMode() {
2327
        return this._filterMode;
94,394✔
2328
    }
2329

2330
    public set filterMode(value: FilterMode) {
2331
        switch (value) {
184!
2332
            case FilterMode.excelStyleFilter:
2333
            case FilterMode.quickFilter:
2334
                this._filterMode = value;
184✔
2335
                break;
184✔
2336
            default:
2337
                break;
×
2338
        }
2339

2340
        if (this.filteringService.isFilterRowVisible) {
184✔
2341
            this.filteringRow.close();
1✔
2342
        }
2343
        this.notifyChanges(true);
184✔
2344
    }
2345

2346
    /**
2347
     * Gets/Sets the summary position.
2348
     *
2349
     * @example
2350
     * ```html
2351
     * <igx-grid #grid [data]="localData" summaryPosition="top" [autoGenerate]="true"></igx-grid>
2352
     * ```
2353
     * @remarks
2354
     * By default it is bottom.
2355
     */
2356
    @Input()
2357
    public get summaryPosition() {
2358
        return this._summaryPosition;
32,102✔
2359
    }
2360

2361
    public set summaryPosition(value: GridSummaryPosition) {
2362
        this._summaryPosition = value;
18✔
2363
        this.notifyChanges();
18✔
2364
    }
2365

2366
    /**
2367
     * Gets/Sets the summary calculation mode.
2368
     *
2369
     * @example
2370
     * ```html
2371
     * <igx-grid #grid [data]="localData" summaryCalculationMode="rootLevelOnly" [autoGenerate]="true"></igx-grid>
2372
     * ```
2373
     * @remarks
2374
     * By default it is rootAndChildLevels which means the summaries are calculated for the root level and each child level.
2375
     */
2376
    @Input()
2377
    public get summaryCalculationMode() {
2378
        return this._summaryCalculationMode;
277,620✔
2379
    }
2380

2381
    public set summaryCalculationMode(value: GridSummaryCalculationMode) {
2382
        this._summaryCalculationMode = value;
63✔
2383
        if (!this._init) {
63✔
2384
            this.crudService.endEdit(false);
25✔
2385
            this.summaryService.resetSummaryHeight();
25✔
2386
            this.notifyChanges(true);
25✔
2387
        }
2388
    }
2389

2390
    /**
2391
     * Controls whether the summary row is visible when groupBy/parent row is collapsed.
2392
     *
2393
     * @example
2394
     * ```html
2395
     * <igx-grid #grid [data]="localData" [showSummaryOnCollapse]="true" [autoGenerate]="true"></igx-grid>
2396
     * ```
2397
     * @remarks
2398
     * By default showSummaryOnCollapse is set to 'false' which means that the summary row is not visible
2399
     * when the groupBy/parent row is collapsed.
2400
     */
2401
    @Input({ transform: booleanAttribute })
2402
    public get showSummaryOnCollapse() {
2403
        return this._showSummaryOnCollapse;
32,095✔
2404
    }
2405

2406
    public set showSummaryOnCollapse(value: boolean) {
2407
        this._showSummaryOnCollapse = value;
9✔
2408
        this.notifyChanges();
9✔
2409
    }
2410

2411
    /**
2412
     * Gets/Sets the filtering strategy of the grid.
2413
     *
2414
     * @example
2415
     * ```html
2416
     *  <igx-grid #grid [data]="localData" [filterStrategy]="filterStrategy"></igx-grid>
2417
     * ```
2418
     */
2419
    @Input()
2420
    public get filterStrategy(): IFilteringStrategy {
2421
        return this._filterStrategy;
46,092✔
2422
    }
2423

2424
    public set filterStrategy(classRef: IFilteringStrategy) {
2425
        this._filterStrategy = classRef;
37✔
2426
    }
2427

2428
    /**
2429
     * Gets/Sets the sorting strategy of the grid.
2430
     *
2431
     * @example
2432
     * ```html
2433
     *  <igx-grid #grid [data]="localData" [sortStrategy]="sortStrategy"></igx-grid>
2434
     * ```
2435
     */
2436
    @Input()
2437
    public get sortStrategy(): IGridSortingStrategy {
2438
        return this._sortingStrategy;
48,193✔
2439
    }
2440

2441
    public set sortStrategy(value: IGridSortingStrategy) {
2442
        this._sortingStrategy = value;
14✔
2443
    }
2444

2445
    /**
2446
     * Gets/Sets the sorting options - single or multiple sorting.
2447
     * Accepts an `ISortingOptions` object with any of the `mode` properties.
2448
     *
2449
     * @example
2450
     * ```typescript
2451
     * const _sortingOptions: ISortingOptions = {
2452
     *      mode: 'single'
2453
     * }
2454
     * ```html
2455
     * <igx-grid [sortingOptions]="sortingOptions"><igx-grid>
2456
     * ```
2457
     */
2458
    @Input()
2459
    public set sortingOptions(value: ISortingOptions) {
2460
        if (!this._init) {
3✔
2461
            // clear sort only if option is changed runtime. No need to clear on initial load.
2462
            this.clearSort();
2✔
2463
        }
2464
        this._sortingOptions = Object.assign(this._sortingOptions, value);
3✔
2465
    }
2466

2467
    public get sortingOptions() {
2468
        return this._sortingOptions;
28,730✔
2469
    }
2470

2471
    /* blazorByValueArray */
2472
    /* blazorAlwaysWriteback */
2473
    /* @tsTwoWayProperty (true, "SelectedRowsChange", "Detail", false) */
2474
    /* blazorPrimitiveValue */
2475
    /**
2476
     * Gets/Sets the current selection state.
2477
     *
2478
     * @remarks
2479
     * Represents the selected rows' IDs (primary key or rowData)
2480
     * @example
2481
     * ```html
2482
     * <igx-grid [data]="localData" primaryKey="ID" rowSelection="multiple" [selectedRows]="[0, 1, 2]"><igx-grid>
2483
     * ```
2484
     */
2485
    @Input()
2486
    public set selectedRows(rowIDs: any[]) {
2487
        this.selectRows(rowIDs || [], true);
138!
2488
    }
2489

2490
    public get selectedRows(): any[] {
2491
        return this.selectionService.getSelectedRows();
731✔
2492
    }
2493

2494

2495
    /** @hidden @internal */
2496
    public get headerGroupsList(): IgxGridHeaderGroupComponent[] {
2497
        return this.theadRow.groups;
20,444✔
2498
    }
2499

2500
    /** @hidden @internal */
2501
    public get headerCellList(): IgxGridHeaderComponent[] {
2502
        return this.headerGroupsList.map(headerGroup => headerGroup.header).filter(header => header);
4,952✔
2503
    }
2504

2505
    /** @hidden @internal */
2506
    public get filterCellList(): IgxGridFilteringCellComponent[] {
2507
        return this.headerGroupsList.map(group => group.filter).filter(cell => cell);
14,643✔
2508
    }
2509

2510
    /**
2511
     * @hidden @internal
2512
     */
2513
    public get summariesRowList() {
2514
        const res = new QueryList<any>();
2,660✔
2515
        if (!this._summaryRowList) {
2,660!
2516
            return res;
×
2517
        }
2518
        const sumList = this._summaryRowList.filter((item) => item.element.nativeElement.parentElement !== null);
2,660✔
2519
        res.reset(sumList);
2,660✔
2520
        return res;
2,660✔
2521
    }
2522

2523
    /* csSuppress */
2524
    /**
2525
     * A list of `IgxGridRowComponent`.
2526
     *
2527
     * @example
2528
     * ```typescript
2529
     * const rowList = this.grid.rowList;
2530
     * ```
2531
     */
2532
    public get rowList() {
2533
        const res = new QueryList<IgxRowDirective>();
11,846✔
2534
        if (!this._rowList) {
11,846!
2535
            return res;
×
2536
        }
2537
        const rList = this._rowList
11,846✔
2538
            .filter((item) => item.element.nativeElement.parentElement !== null)
91,758✔
2539
            .sort((a, b) => a.index - b.index);
101,094✔
2540
        res.reset(rList);
11,846✔
2541
        return res;
11,846✔
2542
    }
2543

2544
    /* csSuppress */
2545
    /**
2546
     * A list of currently rendered `IgxGridRowComponent`'s.
2547
     *
2548
     * @example
2549
     * ```typescript
2550
     * const dataList = this.grid.dataRowList;
2551
     * ```
2552
     */
2553
    public get dataRowList(): QueryList<IgxRowDirective> {
2554
        const res = new QueryList<IgxRowDirective>();
56,378✔
2555
        if (!this._dataRowList) {
56,378✔
2556
            return res;
9,281✔
2557
        }
2558
        const rList = this._dataRowList.filter(item => item.element.nativeElement.parentElement !== null).sort((a, b) => a.index - b.index);
231,923✔
2559
        res.reset(rList);
47,097✔
2560
        return res;
47,097✔
2561
    }
2562

2563
    /**
2564
     * Gets the header row selector template.
2565
     */
2566
    @Input()
2567
    public get headSelectorTemplate(): TemplateRef<IgxHeadSelectorTemplateContext> {
2568
        return this._headSelectorTemplate || this.headSelectorsTemplates?.first;
3,335✔
2569
    }
2570

2571
    /**
2572
     * Sets the header row selector template.
2573
     * ```html
2574
     * <ng-template #template igxHeadSelector let-headContext>
2575
     * {{ headContext.selectedCount }} / {{ headContext.totalCount  }}
2576
     * </ng-template>
2577
     * ```
2578
     * ```typescript
2579
     * @ViewChild("'template'", {read: TemplateRef })
2580
     * public template: TemplateRef<any>;
2581
     * this.grid.headSelectorTemplate = this.template;
2582
     * ```
2583
     */
2584
    public set headSelectorTemplate(template: TemplateRef<IgxHeadSelectorTemplateContext>) {
2585
        this._headSelectorTemplate = template;
1✔
2586
    }
2587

2588
    /**
2589
     * @hidden
2590
     * @internal
2591
     */
2592
    public get isPinningToStart() {
2593
        return this.pinning.columns !== ColumnPinningPosition.End;
2,440,872✔
2594
    }
2595

2596
    /**
2597
     * @hidden
2598
     * @internal
2599
     */
2600
    public get isRowPinningToTop() {
2601
        return this.pinning.rows !== RowPinningPosition.Bottom;
671,395✔
2602
    }
2603

2604
    /**
2605
     * Gets the row selector template.
2606
     */
2607
    @Input()
2608
    public get rowSelectorTemplate(): TemplateRef<IgxRowSelectorTemplateContext> {
2609
        return this._rowSelectorTemplate || this.rowSelectorsTemplates?.first;
20,205✔
2610
    }
2611

2612
    /**
2613
         * Sets a custom template for the row selectors.
2614
         * ```html
2615
         * <ng-template #template igxRowSelector let-rowContext>
2616
         *    <igx-checkbox [checked]="rowContext.selected"></igx-checkbox>
2617
         * </ng-template>
2618
         * ```
2619
         * ```typescript
2620
         * @ViewChild("'template'", {read: TemplateRef })
2621
         * public template: TemplateRef<any>;
2622
         * this.grid.rowSelectorTemplate = this.template;
2623
         * ```
2624
         */
2625
    public set rowSelectorTemplate(template: TemplateRef<IgxRowSelectorTemplateContext>) {
2626
        this._rowSelectorTemplate = template;
1✔
2627
    }
2628

2629
    /**
2630
     * @hidden @internal
2631
     */
2632
    public get rowOutletDirective() {
2633
        return this.rowEditingOutletDirective;
4,337✔
2634
    }
2635

2636
    /**
2637
     * @hidden @internal
2638
     */
2639
    public get parentRowOutletDirective() {
2640
        return this.outlet;
1✔
2641
    }
2642

2643
    /**
2644
     * @hidden @internal
2645
     */
2646
    public get rowEditCustom(): TemplateRef<IgxGridRowEditTemplateContext> {
2647
        if (this.rowEditCustomDirectives && this.rowEditCustomDirectives.first) {
6,437✔
2648
            return this.rowEditCustomDirectives.first;
62✔
2649
        }
2650
        return null;
6,375✔
2651
    }
2652

2653
    /**
2654

2655
    /**
2656
     * @hidden @internal
2657
     */
2658
    public get rowEditContainer(): TemplateRef<IgxGridRowEditTemplateContext> {
2659
        return this.rowEditCustom ? this.rowEditCustom : this.defaultRowEditTemplate;
5,872✔
2660
    }
2661

2662
    /**
2663
     * The custom template, if any, that should be used when rendering the row drag indicator icon
2664
     */
2665
    @Input()
2666
    public get dragIndicatorIconTemplate(): TemplateRef<IgxGridEmptyTemplateContext> {
2667
        return this._customDragIndicatorIconTemplate || this.dragIndicatorIconTemplates?.first;
3,337✔
2668
    }
2669

2670
    /**
2671
     * Sets a custom template that should be used when rendering the row drag indicator icon.
2672
     *```html
2673
     * <ng-template #template igxDragIndicatorIcon>
2674
     *    <igx-icon>expand_less</igx-icon>
2675
     * </ng-template>
2676
     * ```
2677
     * ```typescript
2678
     * @ViewChild("'template'", {read: TemplateRef })
2679
     * public template: TemplateRef<any>;
2680
     * this.grid.dragIndicatorIconTemplate = this.template;
2681
     * ```
2682
     */
2683
    public set dragIndicatorIconTemplate(val: TemplateRef<IgxGridEmptyTemplateContext>) {
2684
        this._customDragIndicatorIconTemplate = val;
891✔
2685
    }
2686

2687
    /**
2688
     * @hidden @internal
2689
     */
2690
    public get firstEditableColumnIndex(): number {
2691
        const index = this.visibleColumns.filter(col => col.editable)
26✔
2692
            .map(c => c.visibleIndex).sort((a, b) => a - b);
21✔
2693
        return index.length ? index[0] : null;
4!
2694
    }
2695

2696
    /**
2697
     * @hidden @internal
2698
     */
2699
    public get lastEditableColumnIndex(): number {
2700
        const index = this.visibleColumns.filter(col => col.editable)
47✔
2701
            .map(c => c.visibleIndex).sort((a, b) => a > b ? -1 : 1);
55✔
2702
        return index.length ? index[0] : null;
6!
2703
    }
2704

2705
    /**
2706
     * @hidden @internal
2707
     * TODO: Nav service logic doesn't handle 0 results from this querylist
2708
     */
2709
    public get rowEditTabs(): QueryList<IgxRowEditTabStopDirective> {
2710
        return this.rowEditTabsCUSTOM.length ? this.rowEditTabsCUSTOM : this.rowEditTabsDEFAULT;
141✔
2711
    }
2712

2713
    /** @hidden @internal */
2714
    public get activeDescendant() {
2715
        const activeElem = this.navigation.activeNode;
133,133✔
2716

2717
        if (!activeElem || !Object.keys(activeElem).length) {
133,133✔
2718
            return this.id;
117,676✔
2719
        }
2720

2721
        return activeElem.row < 0 ?
15,457✔
2722
            `${this.id}_${activeElem.row}_${activeElem.mchCache.level}_${activeElem.column}` :
2723
            `${this.id}_${activeElem.row}_${activeElem.column}`;
2724
    }
2725

2726
    /** @hidden @internal */
2727
    public get bannerClass(): string {
2728
        const position = this.rowEditPositioningStrategy.isTop ? 'igx-banner__border-top' : 'igx-banner__border-bottom';
5,872✔
2729
        return `igx-banner ${position}`;
5,872✔
2730
    }
2731

2732
    /* mustSetInCodePlatforms: WebComponents;Blazor;React */
2733
    /**
2734
     * Gets/Sets the sorting state.
2735
     *
2736
     * @remarks
2737
     * Supports two-way data binding.
2738
     * @example
2739
     * ```html
2740
     * <igx-grid #grid [data]="Data" [autoGenerate]="true" [(sortingExpressions)]="model.sortingExpressions"></igx-grid>
2741
     * ```
2742
     */
2743
    @WatchChanges()
2744
    @Input()
2745
    public get sortingExpressions(): ISortingExpression[] {
2746
        return this._sortingExpressions;
296,488✔
2747
    }
2748

2749
    public set sortingExpressions(value: ISortingExpression[]) {
2750
        this._sortingExpressions = cloneArray(value);
453✔
2751
        this.sortingExpressionsChange.emit(this._sortingExpressions);
453✔
2752
        this.notifyChanges();
453✔
2753
    }
2754

2755
    /**
2756
     * @hidden @internal
2757
     */
2758
    public get maxLevelHeaderDepth() {
2759
        if (this._maxLevelHeaderDepth === null) {
×
2760
            this._maxLevelHeaderDepth = this.hasColumnLayouts ?
×
2761
                this._columns.reduce((acc, col) => Math.max(acc, col.rowStart), 0) :
×
2762
                this._columns.reduce((acc, col) => Math.max(acc, col.level), 0);
×
2763
        }
2764
        return this._maxLevelHeaderDepth;
×
2765
    }
2766

2767
    /**
2768
     * Gets the number of hidden columns.
2769
     *
2770
     * @example
2771
     * ```typescript
2772
     * const hiddenCol = this.grid.hiddenColumnsCount;
2773
     * ``
2774
     */
2775
    public get hiddenColumnsCount() {
2776
        return this._columns.filter((col) => col.columnGroup === false && col.hidden === true).length;
28,234✔
2777
    }
2778

2779
    /**
2780
     * Gets the number of pinned columns.
2781
     */
2782
    public get pinnedColumnsCount() {
2783
        return this.pinnedColumns.filter(col => !col.columnLayout).length;
100✔
2784
    }
2785

2786
    /**
2787
     * Gets/Sets whether the grid has batch editing enabled.
2788
     * When batch editing is enabled, changes are not made directly to the underlying data.
2789
     * Instead, they are stored as transactions, which can later be committed w/ the `commit` method.
2790
     *
2791
     * @example
2792
     * ```html
2793
     * <igx-grid [batchEditing]="true" [data]="someData">
2794
     * </igx-grid>
2795
     * ```
2796
     */
2797
    @Input({ transform: booleanAttribute })
2798
    public get batchEditing(): boolean {
2799
        return this._batchEditing;
534,596✔
2800
    }
2801

2802
    public set batchEditing(val: boolean) {
2803
        if (val !== this._batchEditing) {
163✔
2804
            delete this._transactions;
152✔
2805
            this._batchEditing = val;
152✔
2806
            this.switchTransactionService(val);
152✔
2807
            this.subscribeToTransactions();
152✔
2808
        }
2809
    }
2810

2811
    /* blazorSuppress */
2812
    /**
2813
     * Get transactions service for the grid.
2814
     */
2815
    public get transactions(): TransactionService<Transaction, State> {
2816
        if (this._diTransactions && !this.batchEditing) {
7,214,254✔
2817
            return this._diTransactions;
3,804✔
2818
        }
2819
        return this._transactions;
7,210,450✔
2820
    }
2821

2822
    /**
2823
     * @hidden @internal
2824
     */
2825
    public get currentRowState(): any {
2826
        return this._currentRowState;
×
2827
    }
2828

2829
    /**
2830
     * @hidden @internal
2831
     */
2832
    public get currencyPositionLeft(): boolean {
2833
        if (this._currencyPositionLeft !== undefined) {
8✔
2834
            return this._currencyPositionLeft;
6✔
2835
        }
2836
        const format = getLocaleNumberFormat(this.locale, NumberFormatStyle.Currency);
2✔
2837
        const formatParts = format.split(',');
2✔
2838
        const i = formatParts.indexOf(formatParts.find(c => c.includes('¤')));
3✔
2839
        return this._currencyPositionLeft = i < 1;
2✔
2840
    }
2841

2842
    /**
2843
     * Gets/Sets cell selection mode.
2844
     *
2845
     * @remarks
2846
     * By default the cell selection mode is multiple
2847
     * @param selectionMode: GridSelectionMode
2848
     */
2849
    @WatchChanges()
2850
    @Input()
2851
    public get cellSelection() {
2852
        return this._cellSelectionMode;
1,206,022✔
2853
    }
2854

2855
    public set cellSelection(selectionMode: GridSelectionMode) {
2856
        this._cellSelectionMode = selectionMode;
33✔
2857
        // if (this.gridAPI.grid) {
2858
        this.selectionService.clear(true);
33✔
2859
        this.notifyChanges();
33✔
2860
        // }
2861
    }
2862

2863
    /**
2864
     * Gets/Sets row selection mode
2865
     *
2866
     * @remarks
2867
     * By default the row selection mode is 'none'
2868
     * Note that in IgxGrid and IgxHierarchicalGrid 'multipleCascade' behaves like 'multiple'
2869
     */
2870
    @WatchChanges()
2871
    @Input()
2872
    public get rowSelection() {
2873
        return this._rowSelectionMode;
297,509✔
2874
    }
2875

2876
    public set rowSelection(selectionMode: GridSelectionMode) {
2877
        this._rowSelectionMode = selectionMode;
523✔
2878
        if (!this._init) {
523✔
2879
            this.selectionService.clearAllSelectedRows();
72✔
2880
            this.notifyChanges(true);
72✔
2881
        }
2882
    }
2883

2884
    /**
2885
     * Gets/Sets column selection mode
2886
     *
2887
     * @remarks
2888
     * By default the row selection mode is none
2889
     * @param selectionMode: GridSelectionMode
2890
     */
2891
    @WatchChanges()
2892
    @Input()
2893
    public get columnSelection() {
2894
        return this._columnSelectionMode;
221,106✔
2895
    }
2896

2897
    public set columnSelection(selectionMode: GridSelectionMode) {
2898
        this._columnSelectionMode = selectionMode;
157✔
2899
        // if (this.gridAPI.grid) {
2900
        this.selectionService.clearAllSelectedColumns();
157✔
2901
        this.notifyChanges(true);
157✔
2902
        // }
2903
    }
2904

2905
    /**
2906
     * @hidden @internal
2907
     */
2908
    public set pagingState(value) {
2909
        this._pagingState = value;
580✔
2910
        if (this.paginator && !this._init) {
580✔
2911
            this.paginator.totalRecords = value.metadata.countRecords;
427✔
2912
        }
2913
    }
2914

2915
    public get pagingState() {
2916
        return this._pagingState;
174✔
2917
    }
2918

2919
    /**
2920
     * @hidden @internal
2921
     */
2922
    public rowEditMessage;
2923

2924
    /**
2925
     * @hidden @internal
2926
     */
2927
    public calcWidth: number;
2928
    /**
2929
     * @hidden @internal
2930
     */
2931
    public calcHeight = 0;
4,058✔
2932
    /**
2933
     * @hidden @internal
2934
     */
2935
    public tfootHeight: number;
2936

2937
    /**
2938
     * @hidden @internal
2939
     */
2940
    public disableTransitions = false;
4,058✔
2941

2942
    /**
2943
     * Represents the last search information.
2944
     */
2945
    public get lastSearchInfo(): ISearchInfo {
2946
        return this._lastSearchInfo;
1,474,475✔
2947
    }
2948

2949
    /**
2950
     * @hidden @internal
2951
     */
2952
    public columnWidthSetByUser = false;
4,058✔
2953

2954
    /**
2955
     * @hidden @internal
2956
     */
2957
    public pinnedRecords: any[];
2958

2959
    /**
2960
     * @hidden @internal
2961
     */
2962
    public unpinnedRecords: any[];
2963

2964
    /**
2965
     * @hidden @internal
2966
     */
2967
    public rendered$ = this.rendered.asObservable().pipe(shareReplay({ bufferSize: 1, refCount: true }));
4,058✔
2968

2969
    /** @hidden @internal */
2970
    public resizeNotify = new Subject<void>();
4,058✔
2971

2972
    /** @hidden @internal */
2973
    public rowAddedNotifier = new Subject<IRowDataEventArgs>();
4,058✔
2974

2975
    /** @hidden @internal */
2976
    public rowDeletedNotifier = new Subject<IRowDataEventArgs>();
4,058✔
2977

2978
    /** @hidden @internal */
2979
    public pipeTriggerNotifier = new Subject();
4,058✔
2980

2981
    /** @hidden @internal */
2982
    public _filteredSortedPinnedData: any[];
2983

2984
    /** @hidden @internal */
2985
    public _filteredSortedUnpinnedData: any[];
2986

2987
    /** @hidden @internal */
2988
    public _filteredPinnedData: any[];
2989

2990
    /**
2991
     * @hidden
2992
     */
2993
    public _filteredUnpinnedData;
2994
    /**
2995
     * @hidden @internal
2996
     */
2997
    public _destroyed = false;
4,058✔
2998
    /**
2999
     * @hidden @internal
3000
     */
3001
    public _totalRecords = -1;
4,058✔
3002
    /**
3003
     * @hidden @internal
3004
     */
3005
    public columnsWithNoSetWidths = null;
4,058✔
3006
    /**
3007
     * @hidden @internal
3008
     */
3009
    public pipeTrigger = 0;
4,058✔
3010
    /**
3011
     * @hidden @internal
3012
     */
3013
    public filteringPipeTrigger = 0;
4,058✔
3014

3015
    /**
3016
     * @hidden @internal
3017
     */
3018
    public isColumnWidthSum = false;
4,058✔
3019

3020
    /**
3021
     * @hidden @internal
3022
     */
3023
    public summaryPipeTrigger = 0;
4,058✔
3024
    /**
3025
     * @hidden @internal
3026
     */
3027
    public groupablePipeTrigger = 0;
4,058✔
3028

3029
    /**
3030
    * @hidden @internal
3031
    */
3032
    public EMPTY_DATA = [];
4,058✔
3033

3034
    /** @hidden @internal */
3035
    public get type(): GridType["type"] {
3036
        return 'flat';
24,465✔
3037
    }
3038

3039
    /** @hidden @internal */
3040
    public _baseFontSize: number;
3041

3042
    /**
3043
     * @hidden
3044
     */
3045
    public destroy$ = new Subject<any>();
4,058✔
3046
    /**
3047
     * @hidden
3048
     */
3049
    protected _pagingMode = GridPagingMode.Local;
4,058✔
3050
    /**
3051
     * @hidden
3052
     */
3053
    protected _pagingState;
3054
    /**
3055
     * @hidden
3056
     */
3057
    protected _hideRowSelectors = false;
4,058✔
3058
    /**
3059
     * @hidden
3060
     */
3061
    protected _rowDrag = false;
4,058✔
3062
    /**
3063
     * @hidden
3064
     */
3065
    protected _columns: IgxColumnComponent[] = [];
4,058✔
3066
    /**
3067
     * @hidden
3068
     */
3069
    protected _pinnedColumns: IgxColumnComponent[] = [];
4,058✔
3070
    /**
3071
     * @hidden
3072
     */
3073
    protected _unpinnedColumns: IgxColumnComponent[] = [];
4,058✔
3074
    /**
3075
     * @hidden
3076
     */
3077
    protected _filteringExpressionsTree: IFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And);
4,058✔
3078
    /**
3079
     * @hidden
3080
     */
3081
    protected _advancedFilteringExpressionsTree: IFilteringExpressionsTree;
3082
    /**
3083
     * @hidden
3084
     */
3085
    protected _sortingExpressions: Array<ISortingExpression> = [];
4,058✔
3086
    /**
3087
     * @hidden
3088
     */
3089
    protected _maxLevelHeaderDepth = null;
4,058✔
3090
    /**
3091
     * @hidden
3092
     */
3093
    protected _columnHiding = false;
4,058✔
3094
    /**
3095
     * @hidden
3096
     */
3097
    protected _columnPinning = false;
4,058✔
3098

3099
    protected _pinnedRecordIDs = [];
4,058✔
3100

3101
    /**
3102
     * @hidden
3103
     */
3104
    protected _hasVisibleColumns;
3105
    protected _allowFiltering = false;
4,058✔
3106
    protected _allowAdvancedFiltering = false;
4,058✔
3107
    protected _filterMode: FilterMode = FilterMode.quickFilter;
4,058✔
3108

3109

3110
    protected _defaultTargetRecordNumber = 10;
4,058✔
3111
    protected _expansionStates: Map<any, boolean> = new Map<any, boolean>();
4,058✔
3112
    protected _defaultExpandState = false;
4,058✔
3113
    protected _headerFeaturesWidth = NaN;
4,058✔
3114
    protected _init = true;
4,058✔
3115
    protected _firstAutoResize = true;
4,058✔
3116
    protected _autoSizeColumnsNotify = new Subject<void>();
4,058✔
3117
    protected _cdrRequestRepaint = false;
4,058✔
3118
    protected _userOutletDirective: IgxOverlayOutletDirective;
3119
    protected _transactions: TransactionService<Transaction, State>;
3120
    protected _batchEditing = false;
4,058✔
3121
    protected _sortingOptions: ISortingOptions = { mode: 'multiple' };
4,058✔
3122
    protected _filterStrategy: IFilteringStrategy = new FilteringStrategy();
4,058✔
3123
    protected _autoGeneratedCols = [];
4,058✔
3124
    protected _dataView = [];
4,058✔
3125
    protected _lastSearchInfo: ISearchInfo = {
4,058✔
3126
        searchText: '',
3127
        caseSensitive: false,
3128
        exactMatch: false,
3129
        activeMatchIndex: 0,
3130
        matchInfoCache: [],
3131
        matchCount: 0,
3132
        content: ''
3133
    };
3134
    protected gridComputedStyles;
3135

3136
    /** @hidden @internal */
3137
    public get paginator() {
3138
        return this.paginationComponents?.first;
3,725,357✔
3139
    }
3140

3141
    /**
3142
     * @hidden @internal
3143
     */
3144
    public get scrollSize() {
3145
        return this.verticalScrollContainer.getScrollNativeSize();
185,681✔
3146
    }
3147

3148
    private _primaryKey: string;
3149
    private _rowEditable = false;
4,058✔
3150
    private _currentRowState: any;
3151
    private _filteredSortedData = null;
4,058✔
3152
    private _filteredData = null;
4,058✔
3153

3154
    private _customDragIndicatorIconTemplate: TemplateRef<IgxGridEmptyTemplateContext>;
3155
    private _excelStyleHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext>;
3156
    private _rowSelectorTemplate: TemplateRef<IgxRowSelectorTemplateContext>;
3157
    private _headSelectorTemplate: TemplateRef<IgxHeadSelectorTemplateContext>;
3158
    private _rowEditTextTemplate: TemplateRef<IgxGridRowEditTextTemplateContext>;
3159
    private _rowAddTextTemplate: TemplateRef<IgxGridEmptyTemplateContext>;
3160
    private _rowEditActionsTemplate: TemplateRef<IgxGridRowEditActionsTemplateContext>;
3161
    private _dragGhostCustomTemplate: TemplateRef<IgxGridRowDragGhostContext>;
3162
    private _rowExpandedIndicatorTemplate: TemplateRef<IgxGridRowTemplateContext>;
3163
    private _rowCollapsedIndicatorTemplate: TemplateRef<IgxGridRowTemplateContext>;
3164
    private _headerExpandIndicatorTemplate: TemplateRef<IgxGridTemplateContext>;
3165
    private _headerCollapseIndicatorTemplate: TemplateRef<IgxGridTemplateContext>;
3166

3167
    private _cdrRequests = false;
4,058✔
3168
    private _resourceStrings = getCurrentResourceStrings(GridResourceStringsEN);
4,058✔
3169
    private _emptyGridMessage = null;
4,058✔
3170
    private _emptyFilteredGridMessage = null;
4,058✔
3171
    private _isLoading = false;
4,058✔
3172
    private _locale: string;
3173
    private overlayIDs = [];
4,058✔
3174
    private _sortingStrategy: IGridSortingStrategy;
3175
    private _pinning: IPinningConfig = { columns: ColumnPinningPosition.Start };
4,058✔
3176
    private _shouldRecalcRowHeight = false;
4,058✔
3177

3178
    private _hostWidth;
3179
    private _advancedFilteringOverlayId: string;
3180
    private _advancedFilteringPositionSettings: PositionSettings = {
4,058✔
3181
        verticalDirection: VerticalAlignment.Middle,
3182
        horizontalDirection: HorizontalAlignment.Center,
3183
        horizontalStartPoint: HorizontalAlignment.Center,
3184
        verticalStartPoint: VerticalAlignment.Middle
3185
    };
3186

3187
    private _advancedFilteringOverlaySettings: OverlaySettings = {
4,058✔
3188
        closeOnOutsideClick: false,
3189
        modal: false,
3190
        positionStrategy: new ConnectedPositioningStrategy(this._advancedFilteringPositionSettings),
3191
    };
3192

3193
    private columnListDiffer;
3194
    private rowListDiffer;
3195
    private _height: string | null = '100%';
4,058✔
3196
    private _width: string | null = '100%';
4,058✔
3197
    private _rowHeight: number | undefined;
3198
    private _horizontalForOfs: Array<IgxGridForOfDirective<any, any[]>> = [];
4,058✔
3199
    private _multiRowLayoutRowSize = 1;
4,058✔
3200
    // Caches
3201
    private _totalWidth = NaN;
4,058✔
3202
    private _pinnedVisible = [];
4,058✔
3203
    private _unpinnedVisible = [];
4,058✔
3204
    private _pinnedWidth = NaN;
4,058✔
3205
    private _unpinnedWidth = NaN;
4,058✔
3206
    private _visibleColumns = [];
4,058✔
3207
    private _columnGroups = false;
4,058✔
3208

3209
    private _columnWidth: string;
3210

3211
    private _summaryPosition: GridSummaryPosition = GridSummaryPosition.bottom;
4,058✔
3212
    private _summaryCalculationMode: GridSummaryCalculationMode = GridSummaryCalculationMode.rootAndChildLevels;
4,058✔
3213
    private _showSummaryOnCollapse = false;
4,058✔
3214
    private _summaryRowHeight = 0;
4,058✔
3215
    private _cellSelectionMode: GridSelectionMode = GridSelectionMode.multiple;
4,058✔
3216
    private _rowSelectionMode: GridSelectionMode = GridSelectionMode.none;
4,058✔
3217
    private _selectRowOnClick = true;
4,058✔
3218
    private _columnSelectionMode: GridSelectionMode = GridSelectionMode.none;
4,058✔
3219

3220
    private lastAddedRowIndex;
3221

3222
    private _currencyPositionLeft: boolean;
3223

3224
    private rowEditPositioningStrategy = new RowEditPositionStrategy({
4,058✔
3225
        horizontalDirection: HorizontalAlignment.Right,
3226
        verticalDirection: VerticalAlignment.Bottom,
3227
        horizontalStartPoint: HorizontalAlignment.Left,
3228
        verticalStartPoint: VerticalAlignment.Bottom,
3229
        closeAnimation: null
3230
    });
3231

3232
    private rowEditSettings: OverlaySettings = {
4,058✔
3233
        scrollStrategy: new AbsoluteScrollStrategy(),
3234
        modal: false,
3235
        closeOnOutsideClick: false,
3236
        outlet: this.rowOutletDirective,
3237
        positionStrategy: this.rowEditPositioningStrategy
3238
    };
3239

3240
    private transactionChange$ = new Subject<void>();
4,058✔
3241
    private _rendered = false;
4,058✔
3242
    private readonly DRAG_SCROLL_DELTA = 10;
4,058✔
3243
    private _dataCloneStrategy: IDataCloneStrategy = new DefaultDataCloneStrategy();
4,058✔
3244
    private _autoSize = false;
4,058✔
3245
    private _sortHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,058✔
3246
    private _sortAscendingHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,058✔
3247
    private _sortDescendingHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,058✔
3248
    private _gridSize: Size = Size.Large;
4,058✔
3249
    private _defaultRowHeight = 50;
4,058✔
3250

3251
    /**
3252
     * @hidden @internal
3253
     */
3254
    public get minColumnWidth() {
3255
        return MINIMUM_COLUMN_WIDTH;
65,514✔
3256
    }
3257

3258
    protected get isCustomSetRowHeight(): boolean {
3259
        return !isNaN(this._rowHeight);
1,196,870✔
3260
    }
3261

3262
    /**
3263
     * @hidden @internal
3264
     */
3265
    public abstract id: string;
3266
    /* blazorSuppress */
3267
    public abstract data: any[] | null;
3268

3269
    /**
3270
     * Returns an array of objects containing the filtered data.
3271
     *
3272
     * @example
3273
     * ```typescript
3274
     * let filteredData = this.grid.filteredData;
3275
     * ```
3276
     */
3277
    public get filteredData() {
3278
        return this._filteredData;
48,340✔
3279
    }
3280

3281
    /**
3282
     * Returns an array containing the filtered sorted data.
3283
     *
3284
     * @example
3285
     * ```typescript
3286
     * const filteredSortedData = this.grid1.filteredSortedData;
3287
     * ```
3288
     */
3289
    public get filteredSortedData(): any[] {
3290
        return this._filteredSortedData;
128,565✔
3291
    }
3292

3293
    /**
3294
     * @hidden @internal
3295
     */
3296
    public get rowChangesCount() {
3297
        if (!this.crudService.row) {
16,795✔
3298
            return 0;
14,061✔
3299
        }
3300
        const f = (obj: any) => {
2,734✔
3301
            let changes = 0;
470✔
3302
            Object.keys(obj).forEach(key => isObject(obj[key]) ? changes += f(obj[key]) : changes++);
492✔
3303
            return changes;
470✔
3304
        };
3305
        if (this.transactions.getState(this.crudService.row.id)?.type === TransactionType.ADD) {
2,734✔
3306
            return this._columns.filter(c => c.field).length;
77✔
3307
        }
3308
        const rowChanges = this.transactions.getAggregatedValue(this.crudService.row.id, false);
2,715✔
3309
        return rowChanges ? f(rowChanges) : 0;
2,715✔
3310
    }
3311

3312
    /**
3313
     * @hidden @internal
3314
     */
3315
    public get dataWithAddedInTransactionRows() {
3316
        const result = cloneArray(this.gridAPI.get_all_data());
11,406✔
3317
        if (this.transactions.enabled) {
11,406✔
3318
            result.push(...this.transactions.getAggregatedChanges(true)
7,025✔
3319
                .filter(t => t.type === TransactionType.ADD)
1,228✔
3320
                .map(t => t.newValue));
356✔
3321
        }
3322

3323
        if (this.crudService.row && this.crudService.row.getClassName() === IgxAddRow.name) {
11,406✔
3324
            result.splice(this.crudService.row.index, 0, this.crudService.row.data);
385✔
3325
        }
3326

3327
        return result;
11,406✔
3328
    }
3329

3330
    /**
3331
     * @hidden @internal
3332
     */
3333
    public get dataLength() {
3334
        return this.transactions.enabled ? this.dataWithAddedInTransactionRows.length : this.gridAPI.get_all_data().length;
45,525✔
3335
    }
3336

3337
    /**
3338
     * @hidden @internal
3339
     */
3340
    public get template(): TemplateRef<IgxGridTemplateContext> {
3341
        if (this.isLoading && (this.hasZeroResultFilter || this.hasNoData)) {
45,102✔
3342
            return this.loadingGridTemplate ? this.loadingGridTemplate : this.loadingGridDefaultTemplate;
81✔
3343
        }
3344

3345
        if (this.hasZeroResultFilter) {
45,021✔
3346
            return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyFilteredGridTemplate;
192!
3347
        }
3348

3349
        if (this.hasNoData) {
44,829✔
3350
            return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyGridDefaultTemplate;
736✔
3351
        }
3352
    }
3353

3354
    /**
3355
     * @hidden @internal
3356
     */
3357
    private get hasZeroResultFilter(): boolean {
3358
        return this.filteredData && this.filteredData.length === 0;
45,297✔
3359
    }
3360

3361
    /**
3362
     * @hidden @internal
3363
     */
3364
    private get hasNoData(): boolean {
3365
        return !this.data || this.dataLength === 0;
45,174✔
3366
    }
3367

3368
    /**
3369
     * @hidden @internal
3370
     */
3371
    public get shouldOverlayLoading(): boolean {
3372
        return this.isLoading && !this.hasNoData && !this.hasZeroResultFilter;
48,429✔
3373
    }
3374

3375
    /**
3376
     * @hidden @internal
3377
     */
3378
    public get isMultiRowSelectionEnabled(): boolean {
3379
        return this.rowSelection === GridSelectionMode.multiple
3,394✔
3380
            || this.rowSelection === GridSelectionMode.multipleCascade;
3381
    }
3382

3383
    /**
3384
     * @hidden @internal
3385
     */
3386
    public get isRowSelectable(): boolean {
3387
        return this.rowSelection !== GridSelectionMode.none;
288,344✔
3388
    }
3389

3390
    /**
3391
     * @hidden @internal
3392
     */
3393
    public get isCellSelectable() {
3394
        return this.cellSelection !== GridSelectionMode.none;
8,478✔
3395
    }
3396

3397
    /**
3398
     * @hidden @internal
3399
     */
3400
    public get columnInDrag() {
3401
        return this.gridAPI.cms.column;
269,307✔
3402
    }
3403

3404
    constructor(
3405
        public readonly validation: IgxGridValidationService,
4,058✔
3406
        /** @hidden @internal */
3407
        public readonly selectionService: IgxGridSelectionService,
4,058✔
3408
        protected colResizingService: IgxColumnResizingService,
4,058✔
3409
        @Inject(IGX_GRID_SERVICE_BASE) public readonly gridAPI: GridServiceType,
4,058✔
3410
        protected transactionFactory: IgxFlatTransactionFactory,
4,058✔
3411
        private elementRef: ElementRef<HTMLElement>,
4,058✔
3412
        protected zone: NgZone,
4,058✔
3413
        /** @hidden @internal */
3414
        @Inject(DOCUMENT) public document: any,
4,058✔
3415
        public readonly cdr: ChangeDetectorRef,
4,058✔
3416
        protected differs: IterableDiffers,
4,058✔
3417
        protected viewRef: ViewContainerRef,
4,058✔
3418
        protected injector: Injector,
4,058✔
3419
        protected envInjector: EnvironmentInjector,
4,058✔
3420
        public navigation: IgxGridNavigationService,
4,058✔
3421
        /** @hidden @internal */
3422
        public filteringService: IgxFilteringService,
4,058✔
3423
        protected textHighlightService: IgxTextHighlightService,
4,058✔
3424
        @Inject(IgxOverlayService) protected overlayService: IgxOverlayService,
4,058✔
3425
        /** @hidden @internal */
3426
        public summaryService: IgxGridSummaryService,
4,058✔
3427
        @Inject(LOCALE_ID) private localeId: string,
4,058✔
3428
        protected platform: PlatformUtil,
4,058✔
3429
        @Optional() @Inject(IgxGridTransaction) protected _diTransactions?: TransactionService<Transaction, State>,
4,058✔
3430
    ) {
3431
        this.locale = this.locale || this.localeId;
4,058✔
3432
        this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.None);
4,058✔
3433
        this._transactions.cloneStrategy = this.dataCloneStrategy;
4,058✔
3434
        this.cdr.detach();
4,058✔
3435
        this.selectionService.selectedRowsChange.pipe(takeUntil(this.destroy$)).subscribe((args: any[]) => {
4,058✔
3436
            this.selectedRowsChange.emit(args);
696✔
3437
        });
3438
        IgcTrialWatermark.register();
4,058✔
3439
    }
3440

3441
    /**
3442
     * @hidden
3443
     * @internal
3444
     */
3445
    @HostListener('mouseleave')
3446
    public hideActionStrip() {
3447
        this.actionStrip?.hide();
3✔
3448
    }
3449

3450
    /**
3451
     * @hidden
3452
     * @internal
3453
     */
3454
    public get headerFeaturesWidth() {
3455
        return this._headerFeaturesWidth;
1,320✔
3456
    }
3457

3458
    /**
3459
     * @hidden
3460
     * @internal
3461
     */
3462
    public isDetailRecord(_rec) {
3463
        return false;
1,237✔
3464
    }
3465

3466
    /**
3467
     * @hidden
3468
     * @internal
3469
     */
3470
    public isGroupByRecord(_rec) {
3471
        return false;
1,237✔
3472
    }
3473

3474
    /**
3475
     * @hidden @internal
3476
     */
3477
    public isGhostRecord(record: any): boolean {
3478
        return record.ghostRecord !== undefined;
536,664✔
3479
    }
3480
    /**
3481
     * @hidden @internal
3482
     */
3483
    public isAddRowRecord(record: any): boolean {
3484
        return record.addRow !== undefined;
×
3485
    }
3486

3487
    /**
3488
     * @hidden
3489
     * Returns the row index of a row that takes into account the full view data like pinning.
3490
     */
3491
    public getDataViewIndex(rowIndex, pinned) {
3492
        if (pinned && !this.isRowPinningToTop) {
252,413✔
3493
            rowIndex = rowIndex + this.unpinnedDataView.length;
198✔
3494
        } else if (!pinned && this.isRowPinningToTop) {
252,215✔
3495
            rowIndex = rowIndex + this.pinnedDataView.length;
250,464✔
3496
        }
3497
        return rowIndex;
252,413✔
3498
    }
3499

3500
    /**
3501
     * @hidden
3502
     * @internal
3503
     */
3504
    public get hasDetails() {
3505
        return false;
376✔
3506
    }
3507

3508
    /**
3509
     * Returns the state of the grid virtualization.
3510
     *
3511
     * @remarks
3512
     * Includes the start index and how many records are rendered.
3513
     * @example
3514
     * ```typescript
3515
     * const gridVirtState = this.grid1.virtualizationState;
3516
     * ```
3517
     */
3518
    public get virtualizationState() {
3519
        return this.verticalScrollContainer.state;
1,238✔
3520
    }
3521

3522
    /**
3523
     * @hidden
3524
     * @internal
3525
     */
3526
    public hideOverlays() {
3527
        this.overlayIDs.forEach(overlayID => {
532✔
3528
            const overlay = this.overlayService.getOverlayById(overlayID);
1✔
3529

3530
            if (overlay?.visible && !overlay.closeAnimationPlayer?.hasStarted()) {
1✔
3531
                this.overlayService.hide(overlayID);
1✔
3532

3533
                this.nativeElement.focus();
1✔
3534
            }
3535
        });
3536
    }
3537

3538
    /**
3539
     * Returns whether the record is pinned or not.
3540
     *
3541
     * @param rowIndex Index of the record in the `dataView` collection.
3542
     *
3543
     * @hidden
3544
     * @internal
3545
     */
3546
    public isRecordPinnedByViewIndex(rowIndex: number) {
3547
        return this.hasPinnedRecords && (this.isRowPinningToTop && rowIndex < this.pinnedDataView.length) ||
301,220✔
3548
            (!this.isRowPinningToTop && rowIndex >= this.unpinnedDataView.length);
3549
    }
3550

3551
    /**
3552
     * Returns whether the record is pinned or not.
3553
     *
3554
     * @param rowIndex Index of the record in the `filteredSortedData` collection.
3555
     */
3556
    public isRecordPinnedByIndex(rowIndex: number) {
3557
        return this.hasPinnedRecords && (this.isRowPinningToTop && rowIndex < this._filteredSortedPinnedData.length) ||
2,419!
3558
            (!this.isRowPinningToTop && rowIndex >= this._filteredSortedUnpinnedData.length);
3559
    }
3560

3561
    /**
3562
     * @hidden
3563
     * @internal
3564
     */
3565
    public isRecordPinned(rec) {
3566
        return this.getInitialPinnedIndex(rec) !== -1;
1,087,737✔
3567
    }
3568

3569
    /**
3570
     * @hidden
3571
     * @internal
3572
     * Returns the record index in order of pinning by the user. Does not consider sorting/filtering.
3573
     */
3574
    public getInitialPinnedIndex(rec) {
3575
        const id = this.gridAPI.get_row_id(rec);
1,087,845✔
3576
        return this._pinnedRecordIDs.indexOf(id);
1,087,845✔
3577
    }
3578

3579
    /**
3580
     * @hidden
3581
     * @internal
3582
     */
3583
    public get hasPinnedRecords() {
3584
        return this._pinnedRecordIDs.length > 0;
490,815✔
3585
    }
3586

3587
    /**
3588
     * @hidden
3589
     * @internal
3590
     */
3591
    public get pinnedRecordsCount() {
3592
        return this._pinnedRecordIDs.length;
7,323✔
3593
    }
3594

3595
    /**
3596
     * @hidden
3597
     * @internal
3598
     */
3599
    public get crudService() {
3600
        return this.gridAPI.crudService;
3,399,794✔
3601
    }
3602

3603
    /**
3604
     * @hidden
3605
     * @internal
3606
     */
3607
    public _setupServices() {
3608
        this.gridAPI.grid = this as any;
3,541✔
3609
        this.crudService.grid = this as any;
3,541✔
3610
        this.selectionService.grid = this as any;
3,541✔
3611
        this.validation.grid = this as any;
3,541✔
3612
        this.navigation.grid = this as any;
3,541✔
3613
        this.filteringService.grid = this as any;
3,541✔
3614
        this.summaryService.grid = this as any;
3,541✔
3615
    }
3616

3617
    /**
3618
     * @hidden
3619
     * @internal
3620
     */
3621
    public _setupListeners() {
3622
        const destructor = takeUntil<any>(this.destroy$);
3,541✔
3623
        fromEvent(this.nativeElement, 'focusout').pipe(filter(() => !!this.navigation.activeNode), destructor).subscribe((event) => {
3,541✔
3624
            const activeNode = this.navigation.activeNode;
651✔
3625
            if (!this.crudService.cell && !!activeNode &&
651!
3626
                ((event.target === this.tbody.nativeElement && activeNode.row >= 0 &&
3627
                    activeNode.row < this.dataView.length)
3628
                    || (event.target === this.theadRow.nativeElement && activeNode.row === -1)
3629
                    || (event.target === this.tfoot.nativeElement && activeNode.row === this.dataView.length)) &&
3630
                !(this.rowEditable && this.crudService.rowEditingBlocked && this.crudService.rowInEditMode)) {
79!
3631
                this.clearActiveNode();
72✔
3632
            }
3633
        });
3634
        this.rowAddedNotifier.pipe(destructor).subscribe(args => this.refreshGridState(args));
3,541✔
3635
        this.rowDeletedNotifier.pipe(destructor).subscribe(args => {
3,541✔
3636
            this.summaryService.deleteOperation = true;
185✔
3637
            this.summaryService.clearSummaryCache(args);
185✔
3638
        });
3639

3640
        this.subscribeToTransactions();
3,541✔
3641

3642
        this.resizeNotify.pipe(
3,541✔
3643
            filter(() => !this._init),
2,893✔
3644
            throttleTime(40, animationFrameScheduler, { leading: true, trailing: true }),
3645
            destructor
3646
        )
3647
        .subscribe(() => {
3648
            this.zone.run(() => {
2,121✔
3649
                // do not trigger reflow if element is detached.
3650
                if (this.nativeElement.isConnected) {
2,121✔
3651
                    if (this.shouldResize) {
1,750✔
3652
                        // resizing occurs due to the change of --ig-size css var
3653
                        this._gridSize = this.gridSize;
70✔
3654
                        this.updateDefaultRowHeight();
70✔
3655
                        this._autoSize = this.isPercentHeight && this.calcHeight !== this.getDataBasedBodyHeight();
70✔
3656
                        this.crudService.endEdit(false);
70✔
3657
                        if (this._summaryRowHeight === 0) {
70✔
3658
                            this.summaryService.summaryHeight = 0;
70✔
3659
                        }
3660
                    }
3661
                    this.notifyChanges(true);
1,750✔
3662
                }
3663
            });
3664
        });
3665

3666
        this.pipeTriggerNotifier.pipe(takeUntil(this.destroy$)).subscribe(() => this.pipeTrigger++);
3,541✔
3667
        this.columnMovingEnd.pipe(destructor).subscribe(() => this.crudService.endEdit(false));
3,541✔
3668

3669
        this.overlayService.opening.pipe(destructor).subscribe((event) => {
3,541✔
3670
            if (this._advancedFilteringOverlayId === event.id) {
794✔
3671
                const instance = event.componentRef.instance as IgxAdvancedFilteringDialogComponent;
41✔
3672
                if (instance) {
41✔
3673
                    instance.initialize(this as any, this.overlayService, event.id);
41✔
3674
                }
3675
            }
3676
        });
3677

3678
        this.overlayService.opened.pipe(destructor).subscribe((event) => {
3,541✔
3679
            const overlaySettings = this.overlayService.getOverlayById(event.id)?.settings;
529✔
3680

3681
            // do not hide the advanced filtering overlay on scroll
3682
            if (this._advancedFilteringOverlayId === event.id) {
529✔
3683
                const instance = event.componentRef.instance as IgxAdvancedFilteringDialogComponent;
39✔
3684
                if (instance) {
39✔
3685
                    instance.lastActiveNode = this.navigation.activeNode;
39✔
3686
                    instance.queryBuilder.setAddButtonFocus();
39✔
3687
                }
3688
                return;
39✔
3689
            }
3690

3691
            // do not hide the overlay if it's attached to a row
3692
            if (this.rowEditingOverlay?.overlayId === event.id) {
490✔
3693
                return;
21✔
3694
            }
3695

3696
            if (overlaySettings?.outlet === this.outlet && this.overlayIDs.indexOf(event.id) === -1) {
469✔
3697
                this.overlayIDs.push(event.id);
353✔
3698
            }
3699
        });
3700

3701
        this.overlayService.closed.pipe(filter(() => !this._init), destructor).subscribe((event) => {
3,541✔
3702
            if (this._advancedFilteringOverlayId === event.id) {
431✔
3703
                this.overlayService.detach(this._advancedFilteringOverlayId);
21✔
3704
                this._advancedFilteringOverlayId = null;
21✔
3705
                return;
21✔
3706
            }
3707

3708
            const ind = this.overlayIDs.indexOf(event.id);
410✔
3709
            if (ind !== -1) {
410✔
3710
                this.overlayIDs.splice(ind, 1);
197✔
3711
            }
3712
        });
3713

3714
        this.verticalScrollContainer.dataChanging.pipe(filter(() => !this._init), destructor).subscribe(($event) => {
6,722✔
3715
            const shouldRecalcSize = this.isPercentHeight &&
3,265✔
3716
                (!this.calcHeight || this.calcHeight === this.getDataBasedBodyHeight() ||
3717
                    this.calcHeight === this.renderedRowHeight * this._defaultTargetRecordNumber);
3718
            if (shouldRecalcSize) {
3,265✔
3719
                this.calculateGridHeight();
110✔
3720
                $event.containerSize = this.calcHeight;
110✔
3721
            }
3722
            this.evaluateLoadingState();
3,265✔
3723
        });
3724

3725
        this.verticalScrollContainer.scrollbarVisibilityChanged.pipe(filter(() => !this._init), destructor).subscribe(() => {
3,541✔
3726
            // called to recalc all widths that may have changes as a result of
3727
            // the vert. scrollbar showing/hiding
3728
            this.notifyChanges(true);
853✔
3729
            this.cdr.detectChanges();
853✔
3730
            Promise.resolve().then(() => this.headerContainer.updateScroll());
853✔
3731
        });
3732

3733

3734
        this.headerContainer?.scrollbarVisibilityChanged.pipe(filter(() => !this._init), destructor).subscribe(() => {
3,541✔
3735
            // the horizontal scrollbar showing/hiding
3736
            // update scrollbar visibility and recalc heights
3737
            this.notifyChanges(true);
1,342✔
3738
            this.cdr.detectChanges();
1,342✔
3739
        });
3740

3741
        this.verticalScrollContainer.contentSizeChange.pipe(filter(() => !this._init), throttleTime(30), destructor).subscribe(() => {
3,541✔
3742
            this.notifyChanges(true);
689✔
3743
        });
3744

3745
        // notifier for column autosize requests
3746
        this._autoSizeColumnsNotify.pipe(
3,541✔
3747
            throttleTime(0, animationFrameScheduler, { leading: false, trailing: true }),
3748
            destructor
3749
        )
3750
        .subscribe(() => {
3751
            this.autoSizeColumnsInView();
13✔
3752
            this._firstAutoResize = false;
13✔
3753
        });
3754
    }
3755

3756
    /**
3757
     * @hidden
3758
     */
3759
    public ngOnInit() {
3760
        this._setupServices();
3,541✔
3761
        this._setupListeners();
3,541✔
3762
        this.rowListDiffer = this.differs.find([]).create(null);
3,541✔
3763
        // compare based on field, not on object ref.
3764
        this.columnListDiffer = this.differs.find([]).create((index, col: ColumnType) => col.field);
17,284✔
3765
        this.calcWidth = this.width && this.width.indexOf('%') === -1 ? parseInt(this.width, 10) : 0;
3,541✔
3766
        this.gridComputedStyles = this.document.defaultView.getComputedStyle(this.nativeElement);
3,541✔
3767
    }
3768

3769
    /**
3770
     * @hidden
3771
     * @internal
3772
     */
3773
    public resetColumnsCaches() {
3774
        this._columns.forEach(column => column.resetCaches());
214,577✔
3775
    }
3776

3777
    /**
3778
     * @hidden @internal
3779
     */
3780
    public generateRowID(): string | number {
3781
        const primaryColumn = this._columns.find(col => col.field === this.primaryKey);
61✔
3782
        const idType = this.data.length ?
59✔
3783
            this.resolveDataTypes(this.data[0][this.primaryKey]) : primaryColumn ? primaryColumn.dataType : 'string';
1!
3784
        return idType === 'string' ? crypto.randomUUID() : FAKE_ROW_ID--;
59✔
3785
    }
3786

3787
    /**
3788
     * @hidden
3789
     * @internal
3790
     */
3791
    public resetForOfCache() {
3792
        const firstVirtRow = this.dataRowList.first;
33,969✔
3793
        if (firstVirtRow) {
33,969✔
3794
            if (this._cdrRequests) {
16,800✔
3795
                firstVirtRow.virtDirRow.cdr.detectChanges();
6,705✔
3796
            }
3797
            firstVirtRow.virtDirRow.assumeMaster();
16,800✔
3798
        }
3799
    }
3800

3801
    /**
3802
     * @hidden
3803
     * @internal
3804
     */
3805
    public setFilteredData(data, pinned: boolean) {
3806
        if (this.hasPinnedRecords && pinned) {
1,517✔
3807
            this._filteredPinnedData = data || [];
49✔
3808
            const filteredUnpinned = this._filteredUnpinnedData || [];
49✔
3809
            const filteredData = [... this._filteredPinnedData, ...filteredUnpinned];
49✔
3810
            this._filteredData = filteredData.length > 0 ? filteredData : this._filteredUnpinnedData;
49✔
3811
        } else if (this.hasPinnedRecords && !pinned) {
1,468✔
3812
            this._filteredUnpinnedData = data;
47✔
3813
        } else {
3814
            this._filteredData = data;
1,421✔
3815
        }
3816
    }
3817

3818
    /**
3819
     * @hidden
3820
     * @internal
3821
     */
3822
    public resetColumnCollections() {
3823
        if (this.hasColumnLayouts) {
34,110✔
3824
            this._columns.filter(x => x.columnLayout).forEach(x => x.populateVisibleIndexes());
11,902✔
3825
        }
3826
        this._visibleColumns.length = 0;
34,110✔
3827
        this._pinnedVisible.length = 0;
34,110✔
3828
        this._unpinnedVisible.length = 0;
34,110✔
3829
    }
3830

3831
    /**
3832
     * @hidden
3833
     * @internal
3834
     */
3835
    public resetCachedWidths() {
3836
        this._unpinnedWidth = NaN;
45,714✔
3837
        this._pinnedWidth = NaN;
45,714✔
3838
        this._totalWidth = NaN;
45,714✔
3839
    }
3840

3841
    /**
3842
     * @hidden
3843
     * @internal
3844
     */
3845
    public resetCaches(recalcFeatureWidth = true) {
10,482✔
3846
        if (recalcFeatureWidth) {
33,969✔
3847
            this._headerFeaturesWidth = NaN;
33,960✔
3848
            this.summaryService.summaryHeight = 0;
33,960✔
3849
        }
3850
        this.resetColumnsCaches();
33,969✔
3851
        this.resetColumnCollections();
33,969✔
3852
        this.resetForOfCache();
33,969✔
3853
        this.resetCachedWidths();
33,969✔
3854
        this.hasVisibleColumns = undefined;
33,969✔
3855
        this._columnGroups = this._columns.some(col => col.columnGroup);
160,640✔
3856
    }
3857

3858
    /**
3859
     * @hidden
3860
     */
3861
    public ngAfterContentInit() {
3862
        if (this.sortHeaderIconDirectiveTemplate) {
3,412✔
3863
            this.sortHeaderIconTemplate = this.sortHeaderIconDirectiveTemplate;
5✔
3864
        }
3865

3866
        if (this.sortAscendingHeaderIconDirectiveTemplate) {
3,412✔
3867
            this.sortAscendingHeaderIconTemplate = this.sortAscendingHeaderIconDirectiveTemplate;
5✔
3868
        }
3869

3870
        if (this.sortDescendingHeaderIconDirectiveTemplate) {
3,412✔
3871
            this.sortDescendingHeaderIconTemplate = this.sortDescendingHeaderIconDirectiveTemplate;
5✔
3872
        }
3873

3874
        this.setupColumns();
3,412✔
3875
        this.toolbar.changes.pipe(filter(() => !this._init), takeUntil(this.destroy$)).subscribe(() => this.notifyChanges(true));
3,412✔
3876
        this.setUpPaginator();
3,412✔
3877
        this.paginationComponents.changes.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,412✔
3878
            this.setUpPaginator();
64✔
3879
        });
3880
        if (this.actionStrip) {
3,412✔
3881
            this.actionStrip.menuOverlaySettings.outlet = this.outlet;
115✔
3882
        }
3883
    }
3884

3885
    /**
3886
     * @hidden @internal
3887
     */
3888
    public dataRebinding(event: IForOfDataChangingEventArgs) {
3889
        if (event.state.chunkSize == 0) {
6,722✔
3890
            this._shouldRecalcRowHeight = true;
3,571✔
3891
        }
3892
        this.dataChanging.emit(event);
6,722✔
3893
    }
3894

3895
    /**
3896
     * @hidden @internal
3897
     */
3898
    public dataRebound(event) {
3899
        this.selectionService.clearHeaderCBState();
6,722✔
3900
        if (this._shouldRecalcRowHeight) {
6,722✔
3901
            this._shouldRecalcRowHeight = false;
3,571✔
3902
            this.updateDefaultRowHeight();
3,571✔
3903
        }
3904
        this.dataChanged.emit(event);
6,722✔
3905
    }
3906

3907
    /** @hidden @internal */
3908
    public createFilterDropdown(column: ColumnType, options: OverlaySettings) {
3909
        options.outlet = this.outlet;
204✔
3910
        if (this.excelStyleFilteringComponent) {
204✔
3911
            this.excelStyleFilteringComponent.initialize(column, this.overlayService);
21✔
3912
            const id = this.overlayService.attach(this.excelStyleFilteringComponent.element, options);
21✔
3913
            this.excelStyleFilteringComponent.overlayComponentId = id;
21✔
3914
            return id;
21✔
3915
        }
3916
        const id = this.overlayService.attach(IgxGridExcelStyleFilteringComponent, this.viewRef, options);
183✔
3917
        return id;
183✔
3918
    }
3919

3920
    /** @hidden @internal */
3921
    public setUpPaginator() {
3922
        if (this.paginator) {
3,479✔
3923
            this.paginator.pageChange.pipe(takeWhile(() => !!this.paginator), filter(() => !this._init))
220✔
3924
                .subscribe(() => {
3925
                    this.selectionService.clear(true);
114✔
3926
                    this.crudService.endEdit(false);
114✔
3927
                    this.pipeTrigger++;
114✔
3928
                    this.navigateTo(0);
114✔
3929
                    this.notifyChanges();
114✔
3930
                });
3931
            this.paginator.perPageChange.pipe(takeWhile(() => !!this.paginator), filter(() => !this._init))
220✔
3932
                .subscribe(() => {
3933
                    this.selectionService.clear(true);
70✔
3934
                    this.page = 0;
70✔
3935
                    this.crudService.endEdit(false);
70✔
3936
                    this.notifyChanges();
70✔
3937
                });
3938
        } else {
3939
            this.markForCheck();
3,259✔
3940
        }
3941
    }
3942

3943
    /**
3944
     * @hidden
3945
     * @internal
3946
     */
3947
    public setFilteredSortedData(data, pinned: boolean) {
3948
        data = data || [];
6,466✔
3949
        if (this.pinnedRecordsCount > 0) {
6,466✔
3950
            if (pinned) {
341✔
3951
                this._filteredSortedPinnedData = data;
174✔
3952
                this.pinnedRecords = data;
174✔
3953
                this._filteredSortedData = this.isRowPinningToTop ? [... this._filteredSortedPinnedData, ... this._filteredSortedUnpinnedData] :
174✔
3954
                    [... this._filteredSortedUnpinnedData, ... this._filteredSortedPinnedData];
3955
                this.refreshSearch(true, false);
174✔
3956
            } else {
3957
                this._filteredSortedUnpinnedData = data;
167✔
3958
            }
3959
        } else {
3960
            this._filteredSortedData = data;
6,125✔
3961
            this.refreshSearch(true, false);
6,125✔
3962
        }
3963
        this.buildDataView(data);
6,466✔
3964
    }
3965

3966
    /**
3967
     * @hidden @internal
3968
     */
3969
    public resetHorizontalVirtualization() {
3970
        const elementFilter = (item: IgxRowDirective | IgxSummaryRowComponent) => this.isDefined(item.nativeElement.parentElement);
49,160✔
3971
        this._horizontalForOfs = [
6,686✔
3972
            ...this._dataRowList.filter(elementFilter).map(item => item.virtDirRow),
46,035✔
3973
            ...this._summaryRowList.filter(elementFilter).map(item => item.virtDirRow)
3,125✔
3974
        ];
3975
    }
3976

3977
    /**
3978
     * @hidden @internal
3979
     */
3980
    public _setupRowObservers() {
3981
        const elementFilter = (item: IgxRowDirective | IgxSummaryRowComponent) => this.isDefined(item.nativeElement.parentElement);
17,546✔
3982
        const extractForOfs = pipe(map((collection: any[]) => collection.filter(elementFilter).map(item => item.virtDirRow)));
17,546✔
3983
        const rowListObserver = extractForOfs(this._dataRowList.changes);
3,554✔
3984
        const summaryRowObserver = extractForOfs(this._summaryRowList.changes);
3,554✔
3985
        rowListObserver.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,554✔
3986
            this.resetHorizontalVirtualization();
2,717✔
3987
        });
3988
        summaryRowObserver.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,554✔
3989
            this.resetHorizontalVirtualization();
317✔
3990
        });
3991
        this.resetHorizontalVirtualization();
3,554✔
3992
    }
3993

3994
    /**
3995
     * @hidden @internal
3996
     */
3997
    public _zoneBegoneListeners() {
3998
        this.zone.runOutsideAngular(() => {
3,554✔
3999
            this.verticalScrollContainer.getScroll().addEventListener('scroll', this.verticalScrollHandler.bind(this));
3,554✔
4000
            this.headerContainer?.getScroll().addEventListener('scroll', this.horizontalScrollHandler.bind(this));
3,554✔
4001
            if (this.hasColumnsToAutosize) {
3,554✔
4002
                this.headerContainer?.dataChanged.pipe(takeUntil(this.destroy$)).subscribe(() => {
13✔
4003
                    this.cdr.detectChanges();
21✔
4004
                    this.zone.onStable.pipe(first()).subscribe(() => {
21✔
4005
                        this.autoSizeColumnsInView();
21✔
4006
                    });
4007
                });
4008
            }
4009
            // Window resize observer not needed because when you resize the window element the tbody container always resize so
4010
            // it would always notify resizing, thus a change detection and recalculation of sizes will occur
4011
            resizeObservable(this.nativeElement).pipe(first(), takeUntil(this.destroy$)).subscribe(() => this.resizeNotify.next());
3,554✔
4012
            resizeObservable(this.tbodyContainer.nativeElement).pipe(takeUntil(this.destroy$)).subscribe(() => this.resizeNotify.next());
3,554✔
4013
        });
4014
    }
4015

4016
    /**
4017
     * @hidden
4018
     */
4019
    public ngAfterViewInit() {
4020
        this.initPinning();
3,554✔
4021
        this.calculateGridSizes();
3,554✔
4022
        this._init = false;
3,554✔
4023
        this.cdr.reattach();
3,554✔
4024
        this._setupRowObservers();
3,554✔
4025
        this._zoneBegoneListeners();
3,554✔
4026

4027
        const vertScrDC = this.verticalScrollContainer.displayContainer;
3,554✔
4028
        vertScrDC.addEventListener('scroll', this.preventContainerScroll.bind(this));
3,554✔
4029

4030
        this._pinnedRowList.changes
3,554✔
4031
            .pipe(takeUntil(this.destroy$))
4032
            .subscribe((change: QueryList<IgxGridRowComponent>) => {
4033
                this.onPinnedRowsChanged(change);
182✔
4034
            });
4035

4036
        this.addRowSnackbar?.clicked.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,554✔
4037
            const rec = this.filteredSortedData[this.lastAddedRowIndex];
4✔
4038
            this.scrollTo(rec, 0);
4✔
4039
            this.addRowSnackbar.close();
4✔
4040
        });
4041

4042
        // Keep the stream open for future subscribers
4043
        this.rendered$.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,554✔
4044
            if (this.paginator) {
1,977✔
4045
                this.paginator.totalRecords = this.totalRecords ? this.totalRecords : this.paginator.totalRecords;
73✔
4046
                this.paginator.overlaySettings = { outlet: this.outlet };
73✔
4047
            }
4048
            if (this.hasColumnsToAutosize) {
1,977✔
4049
                this.autoSizeColumnsInView();
12✔
4050
            }
4051
            this._rendered = true;
1,977✔
4052
        });
4053
        Promise.resolve().then(() => this.rendered.next(true));
3,554✔
4054

4055
    }
4056

4057
    /**
4058
     * @hidden @internal
4059
     */
4060
    public notifyChanges(repaint = false) {
14,871✔
4061
        this._cdrRequests = true;
490,680✔
4062
        this._cdrRequestRepaint = repaint;
490,680✔
4063
        this.cdr.markForCheck();
490,680✔
4064
    }
4065

4066
    /**
4067
     * @hidden @internal
4068
     */
4069
    public ngDoCheck() {
4070
        if (this._init) {
17,508✔
4071
            return;
4,098✔
4072
        }
4073

4074
        if (this._cdrRequestRepaint) {
13,410✔
4075
            this.resetNotifyChanges();
3,934✔
4076
            this.calculateGridSizes();
3,934✔
4077
            this.refreshSearch(true);
3,934✔
4078
            return;
3,934✔
4079
        }
4080

4081
        if (this._cdrRequests) {
9,476✔
4082
            this.resetNotifyChanges();
4,181✔
4083
            this.cdr.detectChanges();
4,181✔
4084
        }
4085
    }
4086

4087
    /**
4088
     * @hidden
4089
     * @internal
4090
     */
4091
    public getDragGhostCustomTemplate() {
4092

4093
        return this.dragGhostCustomTemplate;
1,979✔
4094
    }
4095

4096
    /**
4097
     * @hidden @internal
4098
     */
4099
    public ngOnDestroy() {
4100
        this.tmpOutlets.forEach((tmplOutlet) => {
3,381✔
4101
            tmplOutlet.cleanCache();
24,893✔
4102
        });
4103

4104
        this.destroy$.next(true);
3,381✔
4105
        this.destroy$.complete();
3,381✔
4106
        this.transactionChange$.next();
3,381✔
4107
        this.transactionChange$.complete();
3,381✔
4108
        this._destroyed = true;
3,381✔
4109

4110
        this.textHighlightService.destroyGroup(this.id);
3,381✔
4111

4112
        if (this._advancedFilteringOverlayId) {
3,381!
4113
            this.overlayService.detach(this._advancedFilteringOverlayId);
×
4114
            delete this._advancedFilteringOverlayId;
×
4115
        }
4116

4117
        this.overlayIDs.forEach(overlayID => {
3,381✔
4118
            const overlay = this.overlayService.getOverlayById(overlayID);
23✔
4119

4120
            if (overlay && !overlay.detached) {
23✔
4121
                this.overlayService.detach(overlayID);
13✔
4122
            }
4123
        });
4124

4125

4126
        this.zone.runOutsideAngular(() => {
3,381✔
4127
            this.verticalScrollContainer?.getScroll()?.removeEventListener('scroll', this.verticalScrollHandler);
3,381✔
4128
            this.headerContainer?.getScroll()?.removeEventListener('scroll', this.horizontalScrollHandler);
3,381✔
4129
            const vertScrDC = this.verticalScrollContainer?.displayContainer;
3,381✔
4130
            vertScrDC?.removeEventListener('scroll', this.preventContainerScroll);
3,381✔
4131
        });
4132
    }
4133

4134
    /**
4135
     * Toggles the specified column's visibility.
4136
     *
4137
     * @example
4138
     * ```typescript
4139
     * this.grid1.toggleColumnVisibility({
4140
     *       column: this.grid1.columns[0],
4141
     *       newValue: true
4142
     * });
4143
     * ```
4144
     */
4145
    public toggleColumnVisibility(args: IColumnVisibilityChangedEventArgs) {
4146
        const col = args.column ? this._columns.find((c) => c === args.column) : undefined;
×
4147

4148
        if (!col) {
×
4149
            return;
×
4150
        }
4151
        col.toggleVisibility(args.newValue);
×
4152
    }
4153

4154
    /* blazorSuppress */
4155
    /**
4156
     * Gets/Sets a list of key-value pairs [row ID, expansion state].
4157
     *
4158
     * @remarks
4159
     * Includes only states that differ from the default one.
4160
     * Supports two-way binding.
4161
     * @example
4162
     * ```html
4163
     * <igx-grid #grid [data]="data" [(expansionStates)]="model.expansionStates">
4164
     * </igx-grid>
4165
     * ```
4166
     */
4167
    @Input()
4168
    public get expansionStates() {
4169
        return this._expansionStates;
225,708✔
4170
    }
4171

4172
    /* blazorSuppress */
4173
    public set expansionStates(value) {
4174
        this._expansionStates = new Map<any, boolean>(value);
688✔
4175
        this.expansionStatesChange.emit(this._expansionStates);
688✔
4176
        this.notifyChanges(true);
688✔
4177
        if (this.gridAPI.grid) {
688✔
4178
            this.cdr.detectChanges();
595✔
4179
        }
4180
    }
4181

4182
    /**
4183
     * Expands all rows.
4184
     *
4185
     * @example
4186
     * ```typescript
4187
     * this.grid.expandAll();
4188
     * ```
4189
     */
4190
    public expandAll() {
4191
        this._defaultExpandState = true;
8✔
4192
        this.expansionStates = new Map<any, boolean>();
8✔
4193
    }
4194

4195
    /**
4196
     * Collapses all rows.
4197
     *
4198
     * @example
4199
     * ```typescript
4200
     * this.grid.collapseAll();
4201
     * ```
4202
     */
4203
    public collapseAll() {
4204
        this._defaultExpandState = false;
2✔
4205
        this.expansionStates = new Map<any, boolean>();
2✔
4206
    }
4207

4208
    /**
4209
     * Expands the row by its id.
4210
     *
4211
     * @remarks
4212
     * ID is either the primaryKey value or the data record instance.
4213
     * @example
4214
     * ```typescript
4215
     * this.grid.expandRow(rowID);
4216
     * ```
4217
     * @param rowID The row id - primaryKey value or the data record instance.
4218
     */
4219
    public expandRow(rowID: any) {
4220
        this.gridAPI.set_row_expansion_state(rowID, true);
56✔
4221
    }
4222

4223
    /**
4224
     * Collapses the row by its id.
4225
     *
4226
     * @remarks
4227
     * ID is either the primaryKey value or the data record instance.
4228
     * @example
4229
     * ```typescript
4230
     * this.grid.collapseRow(rowID);
4231
     * ```
4232
     * @param rowID The row id - primaryKey value or the data record instance.
4233
     */
4234
    public collapseRow(rowID: any) {
4235
        this.gridAPI.set_row_expansion_state(rowID, false);
9✔
4236
    }
4237

4238

4239
    /**
4240
     * Toggles the row by its id.
4241
     *
4242
     * @remarks
4243
     * ID is either the primaryKey value or the data record instance.
4244
     * @example
4245
     * ```typescript
4246
     * this.grid.toggleRow(rowID);
4247
     * ```
4248
     * @param rowID The row id - primaryKey value or the data record instance.
4249
     */
4250
    public toggleRow(rowID: any) {
4251
        const rec = this.gridAPI.get_rec_by_id(rowID);
86✔
4252
        const state = this.gridAPI.get_row_expansion_state(rec);
86✔
4253
        this.gridAPI.set_row_expansion_state(rowID, !state);
86✔
4254
    }
4255

4256
    /**
4257
     * @hidden
4258
     * @internal
4259
     */
4260
    public getDefaultExpandState(_rec: any) {
4261
        return this._defaultExpandState;
9,606✔
4262
    }
4263

4264
    /**
4265
     * Gets the native element.
4266
     *
4267
     * @example
4268
     * ```typescript
4269
     * const nativeEl = this.grid.nativeElement.
4270
     * ```
4271
     */
4272
    public get nativeElement() {
4273
        return this.elementRef.nativeElement;
41,758✔
4274
    }
4275

4276
    /**
4277
     * Gets/Sets the outlet used to attach the grid's overlays to.
4278
     *
4279
     * @remarks
4280
     * If set, returns the outlet defined outside the grid. Otherwise returns the grid's internal outlet directive.
4281
     */
4282
    @Input()
4283
    public get outlet() {
4284
        return this.resolveOutlet();
198,807✔
4285
    }
4286

4287
    public set outlet(val: IgxOverlayOutletDirective) {
4288
        this._userOutletDirective = val;
×
4289
    }
4290

4291

4292
    /**
4293
     * Gets the default row height.
4294
     *
4295
     * @example
4296
     * ```typescript
4297
     * const rowHeigh = this.grid.defaultRowHeight;
4298
     * ```
4299
     */
4300
    public get defaultRowHeight(): number {
4301
        return this._defaultRowHeight;
344,249✔
4302
    }
4303

4304
    /**
4305
     * @hidden @internal
4306
     */
4307
    public get defaultSummaryHeight(): number {
4308
        switch (this.gridSize) {
15,055✔
4309
            case Size.Medium:
4310
                return 30;
29✔
4311
            case Size.Small:
4312
                return 24;
123✔
4313
            default:
4314
                return 36;
14,903✔
4315
        }
4316
    }
4317

4318
    /**
4319
     * Returns the `IgxGridHeaderGroupComponent`'s minimum allowed width.
4320
     *
4321
     * @remarks
4322
     * Used internally for restricting header group component width.
4323
     * The values below depend on the header cell default right/left padding values.
4324
     */
4325
    public get defaultHeaderGroupMinWidth(): number {
4326
        switch (this.gridSize) {
470,581✔
4327
            case Size.Medium:
4328
                return 32;
37,974✔
4329
            case Size.Small:
4330
                return 24;
3,385✔
4331
            default:
4332
                return 48;
429,222✔
4333
        }
4334
    }
4335

4336
    /** @hidden @internal */
4337
    public get pinnedWidth() {
4338
        if (!isNaN(this._pinnedWidth)) {
222,135✔
4339
            return this._pinnedWidth;
201,185✔
4340
        }
4341
        this._pinnedWidth = this.getPinnedWidth();
20,950✔
4342
        return this._pinnedWidth;
20,950✔
4343
    }
4344

4345
    /** @hidden @internal */
4346
    public get unpinnedWidth() {
4347
        if (!isNaN(this._unpinnedWidth)) {
366,951✔
4348
            return this._unpinnedWidth;
343,660✔
4349
        }
4350
        this._unpinnedWidth = this.getUnpinnedWidth();
23,291✔
4351
        return this._unpinnedWidth;
23,291✔
4352
    }
4353

4354
    /**
4355
     * @hidden @internal
4356
     */
4357
    public isHorizontalScrollHidden = false;
4,058✔
4358

4359
    /**
4360
     * @hidden @internal
4361
     * Gets the header cell inner width for auto-sizing.
4362
     */
4363
    public getHeaderCellWidth(element: HTMLElement): ISizeInfo {
4364
        const range = this.document.createRange();
35✔
4365
        const headerWidth = this.platform.getNodeSizeViaRange(range,
35✔
4366
            element,
4367
            element.parentElement);
4368

4369
        const headerStyle = this.document.defaultView.getComputedStyle(element);
35✔
4370
        const headerPadding = parseFloat(headerStyle.paddingLeft) + parseFloat(headerStyle.paddingRight) +
35✔
4371
            parseFloat(headerStyle.borderRightWidth);
4372

4373
        // Take into consideration the header group element, since column pinning applies borders to it if its not a columnGroup.
4374
        const headerGroupStyle = this.document.defaultView.getComputedStyle(element.parentElement);
35✔
4375
        const borderSize = parseFloat(headerGroupStyle.borderRightWidth) + parseFloat(headerGroupStyle.borderLeftWidth);
35✔
4376
        return { width: Math.ceil(headerWidth), padding: Math.ceil(headerPadding + borderSize) };
35✔
4377
    }
4378

4379
    /**
4380
     * @hidden @internal
4381
     * Gets the combined width of the columns that are specific to the enabled grid features. They are fixed.
4382
     */
4383
    public featureColumnsWidth(expander?: ElementRef) {
4384
        if (Number.isNaN(this._headerFeaturesWidth)) {
129,104✔
4385
            // TODO: platformUtil.isBrowser check
4386
            const rowSelectArea = this.headerSelectorContainer?.nativeElement?.getBoundingClientRect ?
28,507✔
4387
                this.headerSelectorContainer.nativeElement.getBoundingClientRect().width : 0;
4388
            const rowDragArea = this.rowDraggable && this.headerDragContainer?.nativeElement?.getBoundingClientRect ?
28,507✔
4389
                this.headerDragContainer.nativeElement.getBoundingClientRect().width : 0;
4390
            const groupableArea = this.headerGroupContainer?.nativeElement?.getBoundingClientRect ?
28,507✔
4391
                this.headerGroupContainer.nativeElement.getBoundingClientRect().width : 0;
4392
            const expanderWidth = expander?.nativeElement?.getBoundingClientRect ? expander.nativeElement.getBoundingClientRect().width : 0;
28,507✔
4393
            this._headerFeaturesWidth = rowSelectArea + rowDragArea + groupableArea + expanderWidth;
28,507✔
4394
        }
4395
        return this._headerFeaturesWidth;
129,104✔
4396
    }
4397

4398
    /**
4399
     * @hidden @internal
4400
     */
4401
    public get summariesMargin() {
4402
        return this.featureColumnsWidth();
21,803✔
4403
    }
4404

4405
    /**
4406
     * Gets an array of `IgxColumnComponent`s.
4407
     *
4408
     * @example
4409
     * ```typescript
4410
     * const colums = this.grid.columns.
4411
     * ```
4412
     */
4413
    public get columns(): IgxColumnComponent[] {
4414
        return this._columns || [];
40,311!
4415
    }
4416

4417
    /**
4418
     * Gets an array of the pinned `IgxColumnComponent`s.
4419
     *
4420
     * @example
4421
     * ```typescript
4422
     * const pinnedColumns = this.grid.pinnedColumns.
4423
     * ```
4424
     */
4425
    public get pinnedColumns(): IgxColumnComponent[] {
4426
        if (this._pinnedVisible.length) {
4,094,794✔
4427
            return this._pinnedVisible;
332,587✔
4428
        }
4429
        this._pinnedVisible = this._pinnedColumns.filter(col => !col.hidden);
3,762,207✔
4430
        return this._pinnedVisible;
3,762,207✔
4431
    }
4432

4433
    /* csSuppress */
4434
    /**
4435
     * Gets an array of the pinned `IgxRowComponent`s.
4436
     *
4437
     * @example
4438
     * ```typescript
4439
     * const pinnedRow = this.grid.pinnedRows;
4440
     * ```
4441
     */
4442
    public get pinnedRows(): IgxGridRowComponent[] {
4443
        return this._pinnedRowList.toArray().sort((a, b) => a.index - b.index);
147✔
4444
    }
4445

4446
    /**
4447
     * Gets an array of unpinned `IgxColumnComponent`s.
4448
     *
4449
     * @example
4450
     * ```typescript
4451
     * const unpinnedColumns = this.grid.unpinnedColumns.
4452
     * ```
4453
     */
4454
    public get unpinnedColumns(): IgxColumnComponent[] {
4455
        if (this._unpinnedVisible.length) {
759,092✔
4456
            return this._unpinnedVisible;
732,308✔
4457
        }
4458
        this._unpinnedVisible = this._unpinnedColumns.filter((col) => !col.hidden);
155,434✔
4459
        return this._unpinnedVisible;
26,784✔
4460
    }
4461

4462
    /**
4463
     * Gets the `width` to be set on `IgxGridHeaderGroupComponent`.
4464
     */
4465
    public getHeaderGroupWidth(column: IgxColumnComponent): string {
4466
        return this.hasColumnLayouts
×
4467
            ? ''
4468
            : `${Math.max(parseFloat(column.calcWidth), this.defaultHeaderGroupMinWidth)}px`;
4469
    }
4470

4471
    /**
4472
     * Returns the `IgxColumnComponent` by field name.
4473
     *
4474
     * @example
4475
     * ```typescript
4476
     * const myCol = this.grid1.getColumnByName("ID");
4477
     * ```
4478
     * @param name
4479
     */
4480
    public getColumnByName(name: string): IgxColumnComponent {
4481
        return this._columns.find((col) => col.field === name);
132,928✔
4482
    }
4483

4484
    public getColumnByVisibleIndex(index: number): IgxColumnComponent {
4485
        return this.visibleColumns.find((col) =>
1,964✔
4486
            !col.columnGroup && !col.columnLayout &&
11,574✔
4487
            col.visibleIndex === index
4488
        );
4489
    }
4490

4491
    /**
4492
     * Recalculates all widths of columns that have size set to `auto`.
4493
     *
4494
     * @example
4495
     * ```typescript
4496
     * this.grid1.recalculateAutoSizes();
4497
     * ```
4498
     */
4499
    public recalculateAutoSizes() {
4500
        // reset auto-size and calculate it again.
4501
        this._columns.forEach(x => x.autoSize = undefined);
12✔
4502
        this.resetCaches();
2✔
4503
        this.zone.onStable.pipe(first()).subscribe(() => {
2✔
4504
            this.cdr.detectChanges();
2✔
4505
            this.autoSizeColumnsInView();
2✔
4506
        });
4507
    }
4508

4509
    /**
4510
     * Returns an array of visible `IgxColumnComponent`s.
4511
     *
4512
     * @example
4513
     * ```typescript
4514
     * const visibleColumns = this.grid.visibleColumns.
4515
     * ```
4516
     */
4517
    public get visibleColumns(): IgxColumnComponent[] {
4518
        if (this._visibleColumns.length) {
161,335✔
4519
            return this._visibleColumns;
134,860✔
4520
        }
4521
        this._visibleColumns = this._columns.filter(c => !c.hidden);
151,294✔
4522
        return this._visibleColumns;
26,475✔
4523
    }
4524

4525
    /**
4526
     * Returns the total number of records.
4527
     *
4528
     * @remarks
4529
     * Only functions when paging is enabled.
4530
     * @example
4531
     * ```typescript
4532
     * const totalRecords = this.grid.totalRecords;
4533
     * ```
4534
     */
4535
    @Input()
4536
    public get totalRecords(): number {
4537
        return this._totalRecords >= 0 ? this._totalRecords : this.pagingState?.metadata.countRecords;
145✔
4538
    }
4539

4540
    public set totalRecords(total: number) {
4541
        if (total >= 0) {
1✔
4542
            if (this.paginator) {
1✔
4543
                this.paginator.totalRecords = total;
1✔
4544
            }
4545
            this._totalRecords = total;
1✔
4546
            this.pipeTrigger++;
1✔
4547
            this.notifyChanges();
1✔
4548
        }
4549
    }
4550

4551
    /** @hidden @internal */
4552
    public get totalWidth(): number {
4553
        if (!isNaN(this._totalWidth)) {
11,221✔
4554
            return this._totalWidth;
3,178✔
4555
        }
4556
        // Take only top level columns
4557
        const cols = this.visibleColumns.filter(col => col.level === 0 && !col.pinned);
49,844✔
4558
        let totalWidth = 0;
8,043✔
4559
        let i = 0;
8,043✔
4560
        for (i; i < cols.length; i++) {
8,043✔
4561
            totalWidth += parseFloat(cols[i].calcWidth) || 0;
39,883!
4562
        }
4563
        this._totalWidth = totalWidth;
8,043✔
4564
        return totalWidth;
8,043✔
4565
    }
4566

4567
    /**
4568
     * @hidden
4569
     * @internal
4570
     */
4571
    public get showRowSelectors(): boolean {
4572
        return this.isRowSelectable && this.hasVisibleColumns && !this.hideRowSelectors;
276,960✔
4573
    }
4574

4575
    /**
4576
     * @hidden
4577
     * @internal
4578
     */
4579
    public get showAddButton() {
4580
        return this.rowEditable && this.dataView.length === 0 && this._columns.length > 0;
910✔
4581
    }
4582

4583
    /**
4584
     * @hidden
4585
     * @internal
4586
     */
4587
    public get showDragIcons(): boolean {
4588
        return this.rowDraggable && this._columns.length > this.hiddenColumnsCount;
×
4589
    }
4590

4591
    /**
4592
     * @hidden
4593
     * @internal
4594
     */
4595
    protected _getDataViewIndex(index: number): number {
4596
        let newIndex = index;
56,288✔
4597
        if ((index < 0 || index >= this.dataView.length) && this.pagingMode === 1 && this.page !== 0) {
56,288!
4598
            newIndex = index - this.perPage * this.page;
×
4599
        } else if (this.gridAPI.grid.verticalScrollContainer.isRemote) {
56,288!
4600
            newIndex = index - this.gridAPI.grid.virtualizationState.startIndex;
×
4601
        }
4602
        return newIndex;
56,288✔
4603
    }
4604

4605
    /**
4606
     * @hidden
4607
     * @internal
4608
     */
4609
    protected getDataIndex(dataViewIndex: number): number {
4610
        let newIndex = dataViewIndex;
23✔
4611
        if (this.gridAPI.grid.verticalScrollContainer.isRemote) {
23!
4612
            newIndex = dataViewIndex + this.gridAPI.grid.virtualizationState.startIndex;
×
4613
        }
4614
        return newIndex;
23✔
4615
    }
4616

4617
    /**
4618
     * Places a column before or after the specified target column.
4619
     *
4620
     * @example
4621
     * ```typescript
4622
     * grid.moveColumn(column, target);
4623
     * ```
4624
     */
4625
    public moveColumn(column: IgxColumnComponent, target: IgxColumnComponent, pos: DropPosition = DropPosition.AfterDropTarget) {
51✔
4626
        // M.A. May 11th, 2021 #9508 Make the event cancelable
4627
        const eventArgs: IColumnMovingEndEventArgs = { source: column, target, cancel: false };
151✔
4628

4629
        this.columnMovingEnd.emit(eventArgs);
151✔
4630

4631
        if (eventArgs.cancel) {
151✔
4632
            return;
1✔
4633
        }
4634

4635
        if (column === target || (column.level !== target.level) ||
150✔
4636
            (column.topLevelParent !== target.topLevelParent)) {
4637
            return;
22✔
4638
        }
4639

4640
        if (column.level) {
128✔
4641
            this._moveChildColumns(column.parent, column, target, pos);
16✔
4642
        }
4643

4644
        // let columnPinStateChanged;
4645
        // pinning and unpinning will work correctly even without passing index
4646
        // but is easier to calclulate the index here, and later use it in the pinning event args
4647
        if (target.pinned && !column.pinned) {
128✔
4648
            const pinnedIndex = this._pinnedColumns.indexOf(target);
8✔
4649
            const index = pos === DropPosition.AfterDropTarget ? pinnedIndex + 1 : pinnedIndex;
8✔
4650
            column.pin(index);
8✔
4651
        }
4652

4653
        if (!target.pinned && column.pinned) {
128✔
4654
            const unpinnedIndex = this._unpinnedColumns.indexOf(target);
3✔
4655
            const index = pos === DropPosition.AfterDropTarget ? unpinnedIndex + 1 : unpinnedIndex;
3✔
4656
            column.unpin(index);
3✔
4657
        }
4658

4659
        // if (target.pinned && column.pinned && !columnPinStateChanged) {
4660
        //     this._reorderColumns(column, target, pos, this._pinnedColumns);
4661
        // }
4662

4663
        // if (!target.pinned && !column.pinned && !columnPinStateChanged) {
4664
        //     this._reorderColumns(column, target, pos, this._unpinnedColumns);
4665
        // }
4666

4667
        this._moveColumns(column, target, pos);
128✔
4668
        this._columnsReordered(column);
128✔
4669
    }
4670

4671
    /**
4672
     * Triggers change detection for the `IgxGridComponent`.
4673
     * Calling markForCheck also triggers the grid pipes explicitly, resulting in all updates being processed.
4674
     * May degrade performance if used when not needed, or if misused:
4675
     * ```typescript
4676
     * // DON'Ts:
4677
     * // don't call markForCheck from inside a loop
4678
     * // don't call markForCheck when a primitive has changed
4679
     * grid.data.forEach(rec => {
4680
     *  rec = newValue;
4681
     *  grid.markForCheck();
4682
     * });
4683
     *
4684
     * // DOs
4685
     * // call markForCheck after updating a nested property
4686
     * grid.data.forEach(rec => {
4687
     *  rec.nestedProp1.nestedProp2 = newValue;
4688
     * });
4689
     * grid.markForCheck();
4690
     * ```
4691
     *
4692
     * @example
4693
     * ```typescript
4694
     * grid.markForCheck();
4695
     * ```
4696
     */
4697
    public markForCheck() {
4698
        this.pipeTrigger++;
3,262✔
4699
        this.cdr.detectChanges();
3,262✔
4700
    }
4701

4702
    /* csSuppress */
4703
    /**
4704
     * Creates a new `IgxGridRowComponent` and adds the data record to the end of the data source.
4705
     *
4706
     * @example
4707
     * ```typescript
4708
     * this.grid1.addRow(record);
4709
     * ```
4710
     * @param data
4711
     */
4712
    public addRow(data: any): void {
4713
        // commit pending states prior to adding a row
4714
        this.crudService.endEdit(true);
171✔
4715
        this.gridAPI.addRowToData(data);
171✔
4716

4717
        this.pipeTrigger++;
171✔
4718
        this.rowAddedNotifier.next({ data: data, rowData: data, owner: this, primaryKey: data[this.primaryKey], rowKey: data[this.primaryKey] });
171✔
4719
        this.notifyChanges();
171✔
4720
    }
4721

4722
    /* blazorCSSuppress */
4723
    /**
4724
     * Removes the `IgxGridRowComponent` and the corresponding data record by primary key.
4725
     *
4726
     * @remarks
4727
     * Requires that the `primaryKey` property is set.
4728
     * The method accept rowSelector as a parameter, which is the rowID.
4729
     * @example
4730
     * ```typescript
4731
     * this.grid1.deleteRow(0);
4732
     * ```
4733
     * @param rowSelector
4734
     */
4735
    public deleteRow(rowSelector: any): any {
4736
        if (this.primaryKey !== undefined && this.primaryKey !== null) {
82✔
4737
            return this.deleteRowById(rowSelector);
82✔
4738
        }
4739
    }
4740

4741
    /** @hidden */
4742
    public deleteRowById(rowId: any): any {
4743
        const args: IRowDataCancelableEventArgs = {
79✔
4744
            rowID: rowId,
4745
            primaryKey: rowId,
4746
            rowKey: rowId,
4747
            rowData: this.getRowData(rowId),
4748
            data: this.getRowData(rowId),
4749
            oldValue: this.getRowData(rowId),
4750
            owner: this,
4751
            isAddRow: false,
4752
            cancel: false
4753
        };
4754
        this.rowDelete.emit(args);
79✔
4755
        if (args.cancel) {
79!
4756
            return;
×
4757
        }
4758

4759
        const record = this.gridAPI.deleteRowById(rowId);
79✔
4760
        if (record !== null && record !== undefined) {
79✔
4761
            const rowDeletedEventArgs: IRowDataEventArgs = {
78✔
4762
                data: record,
4763
                rowData: record,
4764
                owner: this,
4765
                primaryKey: record[this.primaryKey],
4766
                rowKey: record[this.primaryKey]
4767
            };
4768
            this.rowDeleted.emit(rowDeletedEventArgs);
78✔
4769
        }
4770
        return record;
79✔
4771
    }
4772

4773
    /* blazorCSSuppress */
4774
    /**
4775
     * Updates the `IgxGridRowComponent` and the corresponding data record by primary key.
4776
     *
4777
     * @remarks
4778
     * Requires that the `primaryKey` property is set.
4779
     * @example
4780
     * ```typescript
4781
     * this.gridWithPK.updateCell('Updated', 1, 'ProductName');
4782
     * ```
4783
     * @param value the new value which is to be set.
4784
     * @param rowSelector corresponds to rowID.
4785
     * @param column corresponds to column field.
4786
     */
4787
    public updateCell(value: any, rowSelector: any, column: string): void {
4788
        if (this.isDefined(this.primaryKey)) {
19✔
4789
            const col = this._columns.find(c => c.field === column);
47✔
4790
            if (col) {
19✔
4791
                // Simplify
4792
                const rowData = this.gridAPI.getRowData(rowSelector);
19✔
4793
                const index = this.gridAPI.get_row_index_in_data(rowSelector);
19✔
4794
                // If row passed is invalid
4795
                if (index < 0) {
19✔
4796
                    return;
1✔
4797
                }
4798

4799
                const id = {
18✔
4800
                    rowID: rowSelector,
4801
                    columnID: col.index,
4802
                    rowIndex: index
4803
                };
4804

4805
                const cell = new IgxCell(id, index, col, rowData[col.field], value, rowData, this as any);
18✔
4806
                const formControl = this.validation.getFormControl(cell.id.rowID, cell.column.field);
18✔
4807
                formControl.setValue(value);
18✔
4808
                this.gridAPI.update_cell(cell);
18✔
4809
                this.cdr.detectChanges();
18✔
4810
            }
4811
        }
4812
    }
4813

4814
    /* blazorCSSuppress */
4815
    /**
4816
     * Updates the `IgxGridRowComponent`
4817
     *
4818
     * @remarks
4819
     * The row is specified by
4820
     * rowSelector parameter and the data source record with the passed value.
4821
     * This method will apply requested update only if primary key is specified in the grid.
4822
     * @example
4823
     * ```typescript
4824
     * grid.updateRow({
4825
     *       ProductID: 1, ProductName: 'Spearmint', InStock: true, UnitsInStock: 1, OrderDate: new Date('2005-03-21')
4826
     *   }, 1);
4827
     * ```
4828
     * @param value–
4829
     * @param rowSelector correspond to rowID
4830
     */
4831
    // TODO: prevent event invocation
4832
    public updateRow(value: any, rowSelector: any): void {
4833
        if (this.isDefined(this.primaryKey)) {
32✔
4834
            const editableCell = this.crudService.cell;
32✔
4835
            if (editableCell && editableCell.id.rowID === rowSelector) {
32!
4836
                this.crudService.endCellEdit();
×
4837
            }
4838
            const row = new IgxEditRow(rowSelector, -1, this.gridAPI.getRowData(rowSelector), this as any);
32✔
4839
            this.gridAPI.update_row(row, value);
32✔
4840

4841
            // TODO: fix for #5934 and probably break for #5763
4842
            // consider adding of third optional boolean parameter in updateRow.
4843
            // If developer set this parameter to true we should call notifyChanges(true), and
4844
            // vise-versa if developer set it to false we should call notifyChanges(false).
4845
            // The parameter should default to false
4846
            this.notifyChanges();
32✔
4847
        }
4848
    }
4849

4850
    /**
4851
     * Returns the data that is contained in the row component.
4852
     *
4853
     * @remarks
4854
     * If the primary key is not specified the row selector match the row data.
4855
     * @example
4856
     * ```typescript
4857
     * const data = grid.getRowData(94741);
4858
     * ```
4859
     * @param rowSelector correspond to rowID
4860
     */
4861
    public getRowData(rowSelector: any): any {
4862
        if (!this.primaryKey) {
427✔
4863
            return rowSelector;
19✔
4864
        }
4865
        const data = this.gridAPI.get_all_data(this.transactions.enabled);
408✔
4866
        const index = this.gridAPI.get_row_index_in_data(rowSelector);
408✔
4867
        return index < 0 ? {} : data[index];
408✔
4868
    }
4869

4870
    /**
4871
     * Sort a single `IgxColumnComponent`.
4872
     *
4873
     * @remarks
4874
     * Sort the `IgxGridComponent`'s `IgxColumnComponent` based on the provided array of sorting expressions.
4875
     * @example
4876
     * ```typescript
4877
     * this.grid.sort({ fieldName: name, dir: SortingDirection.Asc, ignoreCase: false });
4878
     * ```
4879
     */
4880
    public sort(expression: ISortingExpression | Array<ISortingExpression>): void {
4881
        const sortingState = cloneArray(this.sortingExpressions);
159✔
4882

4883
        if (expression instanceof Array) {
159✔
4884
            for (const each of expression) {
3✔
4885
                this.gridAPI.prepare_sorting_expression([sortingState], each);
6✔
4886
            }
4887
        } else {
4888
            if (this._sortingOptions.mode === 'single') {
156✔
4889
                this._columns.forEach((col) => {
4✔
4890
                    if (!(col.field === expression.fieldName)) {
12✔
4891
                        this.clearSort(col.field);
8✔
4892
                    }
4893
                });
4894
            }
4895
            this.gridAPI.prepare_sorting_expression([sortingState], expression);
156✔
4896
        }
4897

4898
        const eventArgs: ISortingEventArgs = { owner: this, sortingExpressions: sortingState, cancel: false };
159✔
4899
        this.sorting.emit(eventArgs);
159✔
4900

4901
        if (eventArgs.cancel) {
159!
4902
            return;
×
4903
        }
4904

4905
        this.crudService.endEdit(false);
159✔
4906
        if (expression instanceof Array) {
159✔
4907
            this.gridAPI.sort_multiple(expression);
3✔
4908
        } else {
4909
            this.gridAPI.sort(expression);
156✔
4910
        }
4911
        requestAnimationFrame(() => this.sortingDone.emit(expression));
159✔
4912
    }
4913

4914
    /**
4915
     * Filters a single `IgxColumnComponent`.
4916
     *
4917
     * @example
4918
     * ```typescript
4919
     * public filter(term) {
4920
     *      this.grid.filter("ProductName", term, IgxStringFilteringOperand.instance().condition("contains"));
4921
     * }
4922
     * ```
4923
     * @param name
4924
     * @param value
4925
     * @param conditionOrExpressionTree
4926
     * @param ignoreCase
4927
     */
4928
    public filter(name: string, value: any, conditionOrExpressionTree?: IFilteringOperation | IFilteringExpressionsTree,
4929
        ignoreCase?: boolean) {
4930
        this.filteringService.filter(name, value, conditionOrExpressionTree, ignoreCase);
276✔
4931
    }
4932

4933
    /**
4934
     * Filters all the `IgxColumnComponent` in the `IgxGridComponent` with the same condition.
4935
     *
4936
     * @example
4937
     * ```typescript
4938
     * grid.filterGlobal('some', IgxStringFilteringOperand.instance().condition('contains'));
4939
     * ```
4940
     * @param value
4941
     * @param condition
4942
     * @param ignoreCase
4943
     * @deprecated in version 19.0.0.
4944
     */
4945
    public filterGlobal(value: any, condition, ignoreCase?) {
4946
        this.filteringService.filterGlobal(value, condition, ignoreCase);
3✔
4947
    }
4948

4949
    /**
4950
     * Enables summaries for the specified column and applies your customSummary.
4951
     *
4952
     * @remarks
4953
     * If you do not provide the customSummary, then the default summary for the column data type will be applied.
4954
     * @example
4955
     * ```typescript
4956
     * grid.enableSummaries([{ fieldName: 'ProductName' }, { fieldName: 'ID' }]);
4957
     * ```
4958
     * Enable summaries for the listed columns.
4959
     * @example
4960
     * ```typescript
4961
     * grid.enableSummaries('ProductName');
4962
     * ```
4963
     * @param rest
4964
     */
4965
    public enableSummaries(...rest) {
4966
        if (rest.length === 1 && Array.isArray(rest[0])) {
10✔
4967
            this._multipleSummaries(rest[0], true);
7✔
4968
        } else {
4969
            this._summaries(rest[0], true, rest[1]);
3✔
4970
        }
4971
    }
4972

4973
    /**
4974
     * Disable summaries for the specified column.
4975
     *
4976
     * @example
4977
     * ```typescript
4978
     * grid.disableSummaries('ProductName');
4979
     * ```
4980
     * @remarks
4981
     * Disable summaries for the listed columns.
4982
     * @example
4983
     * ```typescript
4984
     * grid.disableSummaries([{ fieldName: 'ProductName' }]);
4985
     * ```
4986
     */
4987
    public disableSummaries(...rest) {
4988
        if (rest.length === 1 && Array.isArray(rest[0])) {
7✔
4989
            this._disableMultipleSummaries(rest[0]);
5✔
4990
        } else {
4991
            this._summaries(rest[0], false);
2✔
4992
        }
4993
    }
4994

4995
    /**
4996
     * If name is provided, clears the filtering state of the corresponding `IgxColumnComponent`.
4997
     *
4998
     * @remarks
4999
     * Otherwise clears the filtering state of all `IgxColumnComponent`s.
5000
     * @example
5001
     * ```typescript
5002
     * this.grid.clearFilter();
5003
     * ```
5004
     * @param name
5005
     */
5006
    public clearFilter(name?: string) {
5007
        this.filteringService.clearFilter(name);
147✔
5008
    }
5009

5010
    /**
5011
     * If name is provided, clears the sorting state of the corresponding `IgxColumnComponent`.
5012
     *
5013
     * @remarks
5014
     * otherwise clears the sorting state of all `IgxColumnComponent`.
5015
     * @example
5016
     * ```typescript
5017
     * this.grid.clearSort();
5018
     * ```
5019
     * @param name
5020
     */
5021
    public clearSort(name?: string) {
5022
        if (!name) {
35✔
5023
            this.sortingExpressions = [];
22✔
5024
            return;
22✔
5025
        }
5026
        if (!this.gridAPI.get_column_by_name(name)) {
13!
5027
            return;
×
5028
        }
5029
        this.gridAPI.clear_sort(name);
13✔
5030
    }
5031

5032
    /**
5033
     * @hidden @internal
5034
     */
5035
    public refreshGridState(_args?) {
5036
        this.crudService.endEdit(true);
287✔
5037
        this.selectionService.clearHeaderCBState();
287✔
5038
        this.summaryService.clearSummaryCache();
287✔
5039
        this.summaryPipeTrigger++;
287✔
5040
        this.cdr.detectChanges();
287✔
5041
    }
5042

5043
    // TODO: We have return values here. Move them to event args ??
5044

5045
    /**
5046
     * Pins a column by field name.
5047
     *
5048
     * @remarks
5049
     * Returns whether the operation is successful.
5050
     * @example
5051
     * ```typescript
5052
     * this.grid.pinColumn("ID");
5053
     * ```
5054
     * @param columnName
5055
     * @param index
5056
     */
5057
    public pinColumn(columnName: string | IgxColumnComponent, index?: number): boolean {
5058
        const col = columnName instanceof IgxColumnComponent ? columnName : this.getColumnByName(columnName);
37✔
5059
        return col.pin(index);
37✔
5060
    }
5061

5062
    /**
5063
     * Unpins a column by field name. Returns whether the operation is successful.
5064
     *
5065
     * @example
5066
     * ```typescript
5067
     * this.grid.pinColumn("ID");
5068
     * ```
5069
     * @param columnName
5070
     * @param index
5071
     */
5072
    public unpinColumn(columnName: string | IgxColumnComponent, index?: number): boolean {
5073
        const col = columnName instanceof IgxColumnComponent ? columnName : this.getColumnByName(columnName);
19✔
5074
        return col.unpin(index);
19✔
5075
    }
5076

5077
    /* csSuppress */
5078
    /**
5079
     * Pin the row by its id.
5080
     *
5081
     * @remarks
5082
     * ID is either the primaryKey value or the data record instance.
5083
     * @example
5084
     * ```typescript
5085
     * this.grid.pinRow(rowID);
5086
     * ```
5087
     * @param rowID The row id - primaryKey value or the data record instance.
5088
     * @param index The index at which to insert the row in the pinned collection.
5089
     */
5090
    public pinRow(rowID: any, index?: number, row?: RowType): boolean {
5091
        if (this._pinnedRecordIDs.indexOf(rowID) !== -1) {
134✔
5092
            return false;
1✔
5093
        }
5094
        const eventArgs = this.gridAPI.get_pin_row_event_args(rowID, index, row, true);
133✔
5095
        this.rowPinning.emit(eventArgs);
133✔
5096

5097
        if (eventArgs.cancel) {
133✔
5098
            return;
1✔
5099
        }
5100
        this.crudService.endEdit(false);
132✔
5101

5102
        const insertIndex = typeof eventArgs.insertAtIndex === 'number' ? eventArgs.insertAtIndex : this._pinnedRecordIDs.length;
132✔
5103
        this._pinnedRecordIDs.splice(insertIndex, 0, rowID);
132✔
5104
        this.pipeTrigger++;
132✔
5105
        if (this.gridAPI.grid) {
132✔
5106
            this.cdr.detectChanges();
130✔
5107
            this.rowPinned.emit(eventArgs);
130✔
5108
        }
5109

5110
        return true;
132✔
5111
    }
5112

5113
    /* csSuppress */
5114
    /**
5115
     * Unpin the row by its id.
5116
     *
5117
     * @remarks
5118
     * ID is either the primaryKey value or the data record instance.
5119
     * @example
5120
     * ```typescript
5121
     * this.grid.unpinRow(rowID);
5122
     * ```
5123
     * @param rowID The row id - primaryKey value or the data record instance.
5124
     */
5125
    public unpinRow(rowID: any, row?: RowType): boolean {
5126
        const index = this._pinnedRecordIDs.indexOf(rowID);
24✔
5127
        if (index === -1) {
24!
5128
            return false;
×
5129
        }
5130

5131
        const eventArgs = this.gridAPI.get_pin_row_event_args(rowID, null, row, false);
24✔
5132
        this.rowPinning.emit(eventArgs);
24✔
5133

5134
        if (eventArgs.cancel) {
24✔
5135
            return;
1✔
5136
        }
5137

5138
        this.crudService.endEdit(false);
23✔
5139
        this._pinnedRecordIDs.splice(index, 1);
23✔
5140
        this.pipeTrigger++;
23✔
5141
        if (this.gridAPI.grid) {
23✔
5142
            this.cdr.detectChanges();
23✔
5143
            this.rowPinned.emit(eventArgs);
23✔
5144
        }
5145

5146
        return true;
23✔
5147
    }
5148

5149
    /** @hidden @internal */
5150
    public get pinnedRowHeight() {
5151
        const containerHeight = this.pinContainer ? this.pinContainer.nativeElement.offsetHeight : 0;
81,669✔
5152
        return this.hasPinnedRecords ? containerHeight : 0;
81,669✔
5153
    }
5154

5155
    /** @hidden @internal */
5156
    public get totalHeight() {
5157
        return this.calcHeight ? this.calcHeight + this.pinnedRowHeight : this.calcHeight;
43,017✔
5158
    }
5159

5160
    /**
5161
     * Recalculates grid width/height dimensions.
5162
     *
5163
     * @remarks
5164
     * Should be run when changing DOM elements dimentions manually that affect the grid's size.
5165
     * @example
5166
     * ```typescript
5167
     * this.grid.reflow();
5168
     * ```
5169
     */
5170
    public reflow() {
5171
        this.calculateGridSizes();
320✔
5172
    }
5173

5174
    /**
5175
     * Finds the next occurrence of a given string in the grid and scrolls to the cell if it isn't visible.
5176
     *
5177
     * @remarks
5178
     * Returns how many times the grid contains the string.
5179
     * @example
5180
     * ```typescript
5181
     * this.grid.findNext("financial");
5182
     * ```
5183
     * @param text the string to search.
5184
     * @param caseSensitive optionally, if the search should be case sensitive (defaults to false).
5185
     * @param exactMatch optionally, if the text should match the entire value  (defaults to false).
5186
     */
5187
    public findNext(text: string, caseSensitive?: boolean, exactMatch?: boolean): number {
5188
        return this.find(text, 1, caseSensitive, exactMatch);
155✔
5189
    }
5190

5191
    /**
5192
     * Finds the previous occurrence of a given string in the grid and scrolls to the cell if it isn't visible.
5193
     *
5194
     * @remarks
5195
     * Returns how many times the grid contains the string.
5196
     * @example
5197
     * ```typescript
5198
     * this.grid.findPrev("financial");
5199
     * ```
5200
     * @param text the string to search.
5201
     * @param caseSensitive optionally, if the search should be case sensitive (defaults to false).
5202
     * @param exactMatch optionally, if the text should match the entire value (defaults to false).
5203
     */
5204
    public findPrev(text: string, caseSensitive?: boolean, exactMatch?: boolean): number {
5205
        return this.find(text, -1, caseSensitive, exactMatch);
40✔
5206
    }
5207

5208
    /**
5209
     * Reapplies the existing search.
5210
     *
5211
     * @remarks
5212
     * Returns how many times the grid contains the last search.
5213
     * @example
5214
     * ```typescript
5215
     * this.grid.refreshSearch();
5216
     * ```
5217
     * @param updateActiveInfo
5218
     */
5219
    public refreshSearch(updateActiveInfo?: boolean, endEdit = true): number {
4,019✔
5220
        if (this._lastSearchInfo.searchText) {
9,932✔
5221
            this.rebuildMatchCache();
127✔
5222

5223
            if (updateActiveInfo) {
127✔
5224
                const activeInfo = this.textHighlightService.highlightGroupsMap.get(this.id);
126✔
5225
                this._lastSearchInfo.matchInfoCache.forEach((match, i) => {
126✔
5226
                    if (match.column === activeInfo.column &&
1,552✔
5227
                        match.row === activeInfo.row &&
5228
                        match.index === activeInfo.index &&
5229
                        compareMaps(match.metadata, activeInfo.metadata)) {
5230
                        this._lastSearchInfo.activeMatchIndex = i;
111✔
5231
                    }
5232
                });
5233
            }
5234

5235
            return this.find(this._lastSearchInfo.searchText,
127✔
5236
                0,
5237
                this._lastSearchInfo.caseSensitive,
5238
                this._lastSearchInfo.exactMatch,
5239
                false,
5240
                endEdit);
5241
        } else {
5242
            return 0;
9,805✔
5243
        }
5244
    }
5245

5246
    /**
5247
     * Removes all the highlights in the cell.
5248
     *
5249
     * @example
5250
     * ```typescript
5251
     * this.grid.clearSearch();
5252
     * ```
5253
     */
5254
    public clearSearch() {
5255
        this._lastSearchInfo = {
1✔
5256
            searchText: '',
5257
            caseSensitive: false,
5258
            exactMatch: false,
5259
            activeMatchIndex: 0,
5260
            matchInfoCache: [],
5261
            matchCount: 0,
5262
            content: ''
5263
        };
5264

5265
        this.rowList.forEach((row) => {
1✔
5266
            if (row.cells) {
10✔
5267
                row.cells.forEach((c: IgxGridCellComponent) => {
10✔
5268
                    c.clearHighlight();
40✔
5269
                });
5270
            }
5271
        });
5272
    }
5273

5274
    /** @hidden @internal */
5275
    public get hasEditableColumns(): boolean {
5276
        return this._columns.some((col) => col.editable);
6✔
5277
    }
5278

5279
    /** @hidden @internal */
5280
    public get hasSummarizedColumns(): boolean {
5281
        const summarizedColumns = this._columns.filter(col => col.hasSummary && !col.hidden);
2,099,547✔
5282
        return summarizedColumns.length > 0;
331,541✔
5283
    }
5284

5285
    /**
5286
     * @hidden @internal
5287
     */
5288
    public get rootSummariesEnabled(): boolean {
5289
        return this.summaryCalculationMode !== GridSummaryCalculationMode.childLevelsOnly;
245,489✔
5290
    }
5291

5292
    /**
5293
     * @hidden @internal
5294
     */
5295
    public get hasVisibleColumns(): boolean {
5296
        if (this._hasVisibleColumns === undefined) {
77,485✔
5297
            return this._columns ? this._columns.some(c => !c.hidden) : false;
78,642!
5298
        }
5299
        return this._hasVisibleColumns;
×
5300
    }
5301

5302
    public set hasVisibleColumns(value) {
5303
        this._hasVisibleColumns = value;
33,969✔
5304
    }
5305

5306
    /** @hidden @internal */
5307
    public get hasMovableColumns(): boolean {
5308
        return this.moving;
×
5309
    }
5310

5311
    /** @hidden @internal */
5312
    public get hasColumnGroups(): boolean {
5313
        return this._columnGroups;
848✔
5314
    }
5315

5316
    /** @hidden @internal */
5317
    public get hasColumnLayouts() {
5318
        return !!this._columns.some(col => col.columnLayout);
21,490,308✔
5319
    }
5320

5321

5322
    /**
5323
     * @hidden @internal
5324
     */
5325
    public get multiRowLayoutRowSize() {
5326
        return this._multiRowLayoutRowSize;
20,048✔
5327
    }
5328

5329
    /**
5330
     * @hidden
5331
     */
5332
    protected get rowBasedHeight() {
5333
        return this.dataLength * this.rowHeight;
×
5334
    }
5335

5336
    /**
5337
     * @hidden
5338
     */
5339
    protected get isPercentWidth() {
5340
        return this.width && this.width.indexOf('%') !== -1;
41,716✔
5341
    }
5342

5343
    protected get shouldResize(): boolean {
5344
        return this._gridSize !== this.gridSize;
1,750✔
5345
    }
5346

5347
    /**
5348
     * @hidden @internal
5349
     */
5350
    public get isPercentHeight() {
5351
        return this._height && this._height.indexOf('%') !== -1;
15,701✔
5352
    }
5353

5354
    /**
5355
     * @hidden
5356
     */
5357
    protected get defaultTargetBodyHeight(): number {
5358
        const allItems = this.dataLength;
324✔
5359
        return this.renderedActualRowHeight * Math.min(this._defaultTargetRecordNumber,
324✔
5360
            this.paginator ? Math.min(allItems, this.paginator.perPage) : allItems);
324✔
5361
    }
5362

5363
    /**
5364
     * @hidden @internal
5365
     * The rowHeight input is bound to min-height css prop of rows that adds a 1px border in all cases
5366
     */
5367
    public get renderedRowHeight(): number {
5368
        return this.rowHeight + 1;
84,659✔
5369
    }
5370

5371
    /**
5372
     * @hidden @internal
5373
     */
5374
    public get outerWidth() {
5375
        return this.hasVerticalScroll() ? this.calcWidth + this.scrollSize : this.calcWidth;
3,858✔
5376
    }
5377

5378
    /**
5379
     * @hidden @internal
5380
     * Gets the size of the grid
5381
     */
5382
    public get gridSize(): Size {
5383
        return this.gridComputedStyles?.getPropertyValue('--component-size') || Size.Large;
797,532✔
5384
    }
5385

5386
    /**
5387
     * @hidden @internal
5388
     * Gets the visible content height that includes header + tbody + footer.
5389
     */
5390
    public getVisibleContentHeight() {
5391
        let height = this.theadRow.nativeElement.clientHeight + this.tbody.nativeElement.clientHeight;
51✔
5392
        if (this.hasSummarizedColumns) {
51✔
5393
            height += this.tfoot.nativeElement.clientHeight;
5✔
5394
        }
5395
        return height;
51✔
5396
    }
5397

5398
    /**
5399
     * @hidden @internal
5400
     */
5401
    public getPossibleColumnWidth(baseWidth: number = null) {
72,541✔
5402
        let computedWidth;
5403
        if (baseWidth !== null) {
72,541!
5404
            computedWidth = baseWidth;
×
5405
        } else {
5406
            computedWidth = this.calcWidth ||
72,541✔
5407
                parseFloat(this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('width'));
5408
        }
5409

5410
        const visibleChildColumns = this.visibleColumns.filter(c => !c.columnGroup);
681,683✔
5411

5412

5413
        // Column layouts related
5414
        let visibleCols = [];
72,541✔
5415
        const columnBlocks = this.visibleColumns.filter(c => c.columnGroup);
681,683✔
5416
        const colsPerBlock = columnBlocks.map(block => block.getInitialChildColumnSizes(block.children));
122,346✔
5417
        const combinedBlocksSize = colsPerBlock.reduce((acc, item) => acc + item.length, 0);
122,346✔
5418
        colsPerBlock.forEach(blockCols => visibleCols = visibleCols.concat(blockCols));
122,346✔
5419
        //
5420

5421
        const columnsWithSetWidths = this.hasColumnLayouts ?
72,541✔
5422
            visibleCols.filter(c => c.widthSetByUser) :
165,199✔
5423
            visibleChildColumns.filter(c => c.widthSetByUser && c.width !== 'fit-content');
296,048✔
5424

5425
        const columnsToSize = this.hasColumnLayouts ?
72,541✔
5426
            combinedBlocksSize - columnsWithSetWidths.length :
5427
            visibleChildColumns.length - columnsWithSetWidths.length;
5428
        const sumExistingWidths = columnsWithSetWidths
72,541✔
5429
            .reduce((prev, curr) => {
5430
                const colWidth = curr.width;
37,576✔
5431
                let widthValue = parseFloat(colWidth);
37,576✔
5432
                if (isNaN(widthValue)) {
37,576!
5433
                    widthValue = MINIMUM_COLUMN_WIDTH;
×
5434
                }
5435
                const currWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1 ?
37,576✔
5436
                    widthValue / 100 * computedWidth :
5437
                    widthValue;
5438
                return prev + currWidth;
37,576✔
5439
            }, 0);
5440

5441
        // When all columns are hidden, return 0px width
5442
        if (!sumExistingWidths && !columnsToSize) {
72,541✔
5443
            return '0px';
1,463✔
5444
        }
5445
        computedWidth -= this.featureColumnsWidth();
71,078✔
5446

5447
        const columnWidth = !Number.isFinite(sumExistingWidths) ?
71,078!
5448
            Math.max(computedWidth / columnsToSize, this.minColumnWidth) :
5449
            Math.max((computedWidth - sumExistingWidths) / columnsToSize, this.minColumnWidth);
5450

5451
        return columnWidth + 'px';
71,078✔
5452
    }
5453

5454
    /**
5455
     * @hidden @internal
5456
     */
5457
    public hasVerticalScroll() {
5458
        if (this._init) {
186,813✔
5459
            return false;
82,449✔
5460
        }
5461
        const isScrollable = this.verticalScrollContainer ? this.verticalScrollContainer.isScrollable() : false;
104,364✔
5462
        return !!(this.calcWidth && this.dataView && this.dataView.length > 0 && isScrollable);
104,364✔
5463
    }
5464

5465
    /**
5466
     * Gets calculated width of the pinned area.
5467
     *
5468
     * @example
5469
     * ```typescript
5470
     * const pinnedWidth = this.grid.getPinnedWidth();
5471
     * ```
5472
     * @param takeHidden If we should take into account the hidden columns in the pinned area.
5473
     */
5474
    public getPinnedWidth(takeHidden = false) {
20,355✔
5475
        const fc = takeHidden ? this._pinnedColumns : this.pinnedColumns;
44,241!
5476
        let sum = 0;
44,241✔
5477
        for (const col of fc) {
44,241✔
5478
            if (col.level === 0) {
6,590✔
5479
                sum += parseInt(col.calcWidth, 10);
3,870✔
5480
            }
5481
        }
5482
        if (this.isPinningToStart) {
44,241✔
5483
            sum += this.featureColumnsWidth();
44,117✔
5484
        }
5485

5486
        return sum;
44,241✔
5487
    }
5488

5489
    /**
5490
     * @hidden @internal
5491
     */
5492
    public isColumnGrouped(_fieldName: string): boolean {
5493
        return false;
×
5494
    }
5495

5496
    /**
5497
     * @hidden @internal
5498
     * TODO: REMOVE
5499
     */
5500
    public onHeaderSelectorClick(event) {
5501
        if (!this.isMultiRowSelectionEnabled) {
7!
5502
            return;
×
5503
        }
5504
        if (this.selectionService.areAllRowSelected()) {
7✔
5505
            this.selectionService.clearRowSelection(event);
1✔
5506
        } else {
5507
            this.selectionService.selectAllRows(event);
6✔
5508
        }
5509
    }
5510

5511
    /**
5512
     * @hidden @internal
5513
     */
5514
    public get headSelectorBaseAriaLabel() {
5515
        if (this._filteringExpressionsTree.filteringOperands.length > 0) {
3,170✔
5516
            return this.selectionService.areAllRowSelected() ? 'Deselect all filtered' : 'Select all filtered';
112✔
5517
        }
5518

5519
        return this.selectionService.areAllRowSelected() ? 'Deselect all' : 'Select all';
3,058✔
5520
    }
5521

5522
    /**
5523
     * @hidden
5524
     * @internal
5525
     */
5526
    public get totalRowsCountAfterFilter() {
5527
        if (this.data) {
3,335✔
5528
            return this.selectionService.allData.length;
3,335✔
5529
        }
5530

5531
        return 0;
×
5532
    }
5533

5534
    /** @hidden @internal */
5535
    public get pinnedDataView(): any[] {
5536
        return this.pinnedRecords ? this.pinnedRecords : [];
261,606✔
5537
    }
5538

5539
    /** @hidden @internal */
5540
    public get unpinnedDataView(): any[] {
5541
        return this.unpinnedRecords ? this.unpinnedRecords : this.verticalScrollContainer?.igxForOf || [];
15,585✔
5542
    }
5543

5544
    /**
5545
     * Returns the currently transformed paged/filtered/sorted/grouped/pinned/unpinned row data, displayed in the grid.
5546
     *
5547
     * @example
5548
     * ```typescript
5549
     *      const dataView = this.grid.dataView;
5550
     * ```
5551
     */
5552
    public get dataView() {
5553
        return this._dataView;
399,683✔
5554
    }
5555

5556
    /**
5557
     * Gets/Sets whether clicking over a row should select/deselect it
5558
     *
5559
     * @remarks
5560
     * By default it is set to true
5561
     * @param enabled: boolean
5562
     */
5563
    @WatchChanges()
5564
    @Input({ transform: booleanAttribute })
5565
    public get selectRowOnClick() {
5566
        return this._selectRowOnClick;
110✔
5567
    }
5568

5569
    public set selectRowOnClick(enabled: boolean) {
5570
        this._selectRowOnClick = enabled;
13✔
5571
    }
5572

5573
    /**
5574
     * Select specified rows by ID.
5575
     *
5576
     * @example
5577
     * ```typescript
5578
     * this.grid.selectRows([1,2,5], true);
5579
     * ```
5580
     * @param rowIDs
5581
     * @param clearCurrentSelection if true clears the current selection
5582
     */
5583
    public selectRows(rowIDs: any[], clearCurrentSelection?: boolean) {
5584
        this.selectionService.selectRowsWithNoEvent(rowIDs, clearCurrentSelection);
264✔
5585
        this.notifyChanges();
264✔
5586
    }
5587

5588
    /**
5589
     * Deselect specified rows by ID.
5590
     *
5591
     * @example
5592
     * ```typescript
5593
     * this.grid.deselectRows([1,2,5]);
5594
     * ```
5595
     * @param rowIDs
5596
     */
5597
    public deselectRows(rowIDs: any[]) {
5598
        this.selectionService.deselectRowsWithNoEvent(rowIDs);
19✔
5599
        this.notifyChanges();
19✔
5600
    }
5601

5602
    /**
5603
     * Selects all rows
5604
     *
5605
     * @remarks
5606
     * By default if filtering is in place, selectAllRows() and deselectAllRows() select/deselect all filtered rows.
5607
     * If you set the parameter onlyFilterData to false that will select all rows in the grid exept deleted rows.
5608
     * @example
5609
     * ```typescript
5610
     * this.grid.selectAllRows();
5611
     * this.grid.selectAllRows(false);
5612
     * ```
5613
     * @param onlyFilterData
5614
     */
5615
    public selectAllRows(onlyFilterData = true) {
29✔
5616
        const data = onlyFilterData && this.filteredData ? this.filteredData : this.gridAPI.get_all_data(true);
30✔
5617
        const rowIDs = this.selectionService.getRowIDs(data).filter(rID => !this.gridAPI.row_deleted_transaction(rID));
429✔
5618
        this.selectRows(rowIDs);
30✔
5619
    }
5620

5621
    /**
5622
     * Deselects all rows
5623
     *
5624
     * @remarks
5625
     * By default if filtering is in place, selectAllRows() and deselectAllRows() select/deselect all filtered rows.
5626
     * If you set the parameter onlyFilterData to false that will deselect all rows in the grid exept deleted rows.
5627
     * @example
5628
     * ```typescript
5629
     * this.grid.deselectAllRows();
5630
     * ```
5631
     * @param onlyFilterData
5632
     */
5633
    public deselectAllRows(onlyFilterData = true) {
5✔
5634
        if (onlyFilterData && this.filteredData && this.filteredData.length > 0) {
6✔
5635
            this.deselectRows(this.selectionService.getRowIDs(this.filteredData));
1✔
5636
        } else {
5637
            this.selectionService.clearAllSelectedRows();
5✔
5638
            this.notifyChanges();
5✔
5639
        }
5640
    }
5641

5642
    /**
5643
     * Deselect selected cells.
5644
     * @example
5645
     * ```typescript
5646
     * this.grid.clearCellSelection();
5647
     * ```
5648
     */
5649
    public clearCellSelection(): void {
5650
        this.selectionService.clear(true);
8✔
5651
        this.notifyChanges();
8✔
5652
    }
5653

5654
    /**
5655
     * @hidden @internal
5656
     */
5657
    public dragScroll(delta: { left: number; top: number }): void {
5658
        const horizontal = this.headerContainer.getScroll();
1✔
5659
        const vertical = this.verticalScrollContainer.getScroll();
1✔
5660
        const { left, top } = delta;
1✔
5661

5662
        horizontal.scrollLeft += left * this.DRAG_SCROLL_DELTA;
1✔
5663
        vertical.scrollTop += top * this.DRAG_SCROLL_DELTA;
1✔
5664
    }
5665

5666
    /**
5667
     * @hidden @internal
5668
     */
5669
    public isDefined(arg: any): boolean {
5670
        return arg !== undefined && arg !== null;
66,927✔
5671
    }
5672

5673
    /**
5674
     * Select range(s) of cells between certain rows and columns of the grid.
5675
     */
5676
    public selectRange(arg: GridSelectionRange | GridSelectionRange[] | null | undefined): void {
5677
        if (!this.isDefined(arg)) {
170✔
5678
            this.clearCellSelection();
6✔
5679
            return;
6✔
5680
        }
5681
        if (arg instanceof Array) {
164✔
5682
            arg.forEach(range => this.setSelection(range));
6✔
5683
        } else {
5684
            this.setSelection(arg);
161✔
5685
        }
5686
        this.notifyChanges();
161✔
5687
    }
5688

5689
    /**
5690
     * @hidden @internal
5691
     */
5692
    public columnToVisibleIndex(field: string | number): number {
5693
        const visibleColumns = this.visibleColumns;
341✔
5694
        if (typeof field === 'number') {
341✔
5695
            return field;
208✔
5696
        }
5697
        return visibleColumns.find(column => column.field === field).visibleIndex;
322✔
5698
    }
5699

5700
    /**
5701
     * @hidden @internal
5702
     */
5703
    public setSelection(range: GridSelectionRange): void {
5704
        const startNode = { row: range.rowStart, column: this.columnToVisibleIndex(range.columnStart) };
169✔
5705
        const endNode = { row: range.rowEnd, column: this.columnToVisibleIndex(range.columnEnd) };
167✔
5706

5707
        this.selectionService.pointerState.node = startNode;
166✔
5708
        this.selectionService.selectRange(endNode, this.selectionService.pointerState);
166✔
5709
        this.selectionService.addRangeMeta(endNode, this.selectionService.pointerState);
166✔
5710
        this.selectionService.initPointerState();
166✔
5711
    }
5712

5713
    /**
5714
     * Get the currently selected ranges in the grid.
5715
     */
5716
    public getSelectedRanges(): GridSelectionRange[] {
5717
        return this.selectionService.ranges;
348✔
5718
    }
5719

5720
    /**
5721
     *
5722
     * Returns an array of the current cell selection in the form of `[{ column.field: cell.value }, ...]`.
5723
     *
5724
     * @remarks
5725
     * If `formatters` is enabled, the cell value will be formatted by its respective column formatter (if any).
5726
     * If `headers` is enabled, it will use the column header (if any) instead of the column field.
5727
     */
5728
    public getSelectedData(formatters = false, headers = false) {
4✔
5729
        const source = this.filteredSortedData;
167✔
5730
        return this.extractDataFromSelection(source, formatters, headers);
167✔
5731
    }
5732

5733
    /**
5734
     * Get current selected columns.
5735
     *
5736
     * @example
5737
     * Returns an array with selected columns
5738
     * ```typescript
5739
     * const selectedColumns = this.grid.selectedColumns();
5740
     * ```
5741
     */
5742
    public selectedColumns(): ColumnType[] {
5743
        const fields = this.selectionService.getSelectedColumns();
67✔
5744
        return fields.map(field => this.getColumnByName(field)).filter(field => field);
67✔
5745
    }
5746

5747
    /**
5748
     * Select specified columns.
5749
     *
5750
     * @example
5751
     * ```typescript
5752
     * this.grid.selectColumns(['ID','Name'], true);
5753
     * ```
5754
     * @param columns
5755
     * @param clearCurrentSelection if true clears the current selection
5756
     */
5757
    public selectColumns(columns: string[] | ColumnType[], clearCurrentSelection?: boolean) {
5758
        let fieldToSelect: string[] = [];
12✔
5759
        if (columns.length === 0 || typeof columns[0] === 'string') {
12✔
5760
            fieldToSelect = columns as string[];
7✔
5761
        } else {
5762
            (columns as ColumnType[]).forEach(col => {
5✔
5763
                if (col.columnGroup) {
18!
5764
                    const children = col.allChildren.filter(c => !c.columnGroup).map(c => c.field);
×
5765
                    fieldToSelect = [...fieldToSelect, ...children];
×
5766
                } else {
5767
                    fieldToSelect.push(col.field);
18✔
5768
                }
5769
            });
5770
        }
5771

5772
        this.selectionService.selectColumnsWithNoEvent(fieldToSelect, clearCurrentSelection);
12✔
5773
        this.notifyChanges();
12✔
5774
    }
5775

5776
    /**
5777
     * Deselect specified columns by field.
5778
     *
5779
     * @example
5780
     * ```typescript
5781
     * this.grid.deselectColumns(['ID','Name']);
5782
     * ```
5783
     * @param columns
5784
     */
5785
    public deselectColumns(columns: string[] | ColumnType[]) {
5786
        let fieldToDeselect: string[] = [];
3✔
5787
        if (columns.length === 0 || typeof columns[0] === 'string') {
3✔
5788
            fieldToDeselect = columns as string[];
2✔
5789
        } else {
5790
            (columns as ColumnType[]).forEach(col => {
1✔
5791
                if (col.columnGroup) {
2!
5792
                    const children = col.allChildren.filter(c => !c.columnGroup).map(c => c.field);
×
5793
                    fieldToDeselect = [...fieldToDeselect, ...children];
×
5794
                } else {
5795
                    fieldToDeselect.push(col.field);
2✔
5796
                }
5797
            });
5798
        }
5799
        this.selectionService.deselectColumnsWithNoEvent(fieldToDeselect);
3✔
5800
        this.notifyChanges();
3✔
5801
    }
5802

5803
    /**
5804
     * Deselects all columns
5805
     *
5806
     * @example
5807
     * ```typescript
5808
     * this.grid.deselectAllColumns();
5809
     * ```
5810
     */
5811
    public deselectAllColumns() {
5812
        this.selectionService.clearAllSelectedColumns();
3✔
5813
        this.notifyChanges();
3✔
5814
    }
5815

5816
    /**
5817
     * Selects all columns
5818
     *
5819
     * @example
5820
     * ```typescript
5821
     * this.grid.deselectAllColumns();
5822
     * ```
5823
     */
5824
    public selectAllColumns() {
5825
        this.selectColumns(this._columns.filter(c => !c.columnGroup));
15✔
5826
    }
5827

5828
    /**
5829
     *
5830
     * Returns an array of the current columns selection in the form of `[{ column.field: cell.value }, ...]`.
5831
     *
5832
     * @remarks
5833
     * If `formatters` is enabled, the cell value will be formatted by its respective column formatter (if any).
5834
     * If `headers` is enabled, it will use the column header (if any) instead of the column field.
5835
     */
5836
    public getSelectedColumnsData(formatters = false, headers = false) {
16✔
5837
        const source = this.filteredSortedData ? this.filteredSortedData : this.data;
20!
5838
        return this.extractDataFromColumnsSelection(source, formatters, headers);
20✔
5839
    }
5840

5841

5842
    /** @hidden @internal **/
5843
    public combineSelectedCellAndColumnData(columnData: any[], formatters = false, headers = false) {
×
5844
        const source = this.filteredSortedData;
×
5845
        return this.extractDataFromSelection(source, formatters, headers, columnData);
×
5846
    }
5847

5848
    /**
5849
     * @hidden @internal
5850
     */
5851
    public preventContainerScroll = (evt) => {
4,058✔
5852
        if (evt.target.scrollTop !== 0) {
3!
5853
            this.verticalScrollContainer.addScroll(evt.target.scrollTop);
×
5854
            evt.target.scrollTop = 0;
×
5855
        }
5856
        if (evt.target.scrollLeft !== 0) {
3!
5857
            this.headerContainer.scrollPosition += evt.target.scrollLeft;
×
5858
            evt.target.scrollLeft = 0;
×
5859
        }
5860
    };
5861

5862
    /**
5863
     * @hidden
5864
     * @internal
5865
     */
5866
    public copyHandler(event) {
5867
        const eventPathElements = event.composedPath().map(el => el.tagName?.toLowerCase());
98✔
5868
        if (eventPathElements.includes('igx-grid-filtering-row') ||
13✔
5869
            eventPathElements.includes('igx-grid-filtering-cell')) {
5870
            return;
1✔
5871
        }
5872

5873
        const selectedColumns = this.gridAPI.grid.selectedColumns();
12✔
5874
        const columnData = this.getSelectedColumnsData(this.clipboardOptions.copyFormatters, this.clipboardOptions.copyHeaders);
12✔
5875
        let selectedData;
5876
        if (event.type === 'copy') {
12✔
5877
            selectedData = this.getSelectedData(this.clipboardOptions.copyFormatters, this.clipboardOptions.copyHeaders);
12✔
5878
        }
5879

5880
        let data = [];
12✔
5881
        let result;
5882

5883
        if (event.code === 'KeyC' && (event.ctrlKey || event.metaKey) && event.currentTarget.className === 'igx-grid-thead__wrapper') {
12!
5884
            if (selectedData.length) {
×
5885
                if (columnData.length === 0) {
×
5886
                    result = this.prepareCopyData(event, selectedData);
×
5887
                } else {
5888
                    data = this.combineSelectedCellAndColumnData(columnData, this.clipboardOptions.copyFormatters,
×
5889
                        this.clipboardOptions.copyHeaders);
5890
                    result = this.prepareCopyData(event, data[0], data[1]);
×
5891
                }
5892
            } else {
5893
                data = columnData;
×
5894
                result = this.prepareCopyData(event, data);
×
5895
            }
5896

5897
            navigator.clipboard.writeText(result).then().catch(e => console.error(e));
×
5898
        } else if (!this.clipboardOptions.enabled || this.crudService.cellInEditMode || event.type === 'keydown') {
12✔
5899
            return;
2✔
5900
        } else {
5901
            if (selectedColumns.length) {
10!
5902
                data = this.combineSelectedCellAndColumnData(columnData, this.clipboardOptions.copyFormatters,
×
5903
                    this.clipboardOptions.copyHeaders);
5904
                result = this.prepareCopyData(event, data[0], data[1]);
×
5905
            } else {
5906
                data = selectedData;
10✔
5907
                result = this.prepareCopyData(event, data);
10✔
5908
            }
5909
            event.clipboardData.setData('text/plain', result);
10✔
5910
        }
5911
    }
5912

5913
    /**
5914
     * @hidden @internal
5915
     */
5916
    public prepareCopyData(event, data, keys?) {
5917
        const ev = { data, cancel: false } as IGridClipboardEvent;
10✔
5918
        this.gridCopy.emit(ev);
10✔
5919

5920
        if (ev.cancel) {
10✔
5921
            return;
1✔
5922
        }
5923

5924
        const transformer = new CharSeparatedValueData(ev.data, this.clipboardOptions.separator);
9✔
5925
        let result = keys ? transformer.prepareData(keys) : transformer.prepareData();
9!
5926

5927
        if (!this.clipboardOptions.copyHeaders) {
9✔
5928
            result = result.substring(result.indexOf('\n') + 1);
2✔
5929
        }
5930

5931
        if (data && data.length > 0 && Object.values(data[0]).length === 1) {
9✔
5932
            result = result.slice(0, -2);
4✔
5933
        }
5934

5935
        event.preventDefault();
9✔
5936

5937
        /* Necessary for the hiearachical case but will probably have to
5938
           change how getSelectedData is propagated in the hiearachical grid
5939
        */
5940
        event.stopPropagation();
9✔
5941

5942
        return result;
9✔
5943
    }
5944

5945
    /**
5946
     * @hidden @internal
5947
     */
5948
    public showSnackbarFor(index: number) {
5949
        this.addRowSnackbar.actionText = index === -1 ? '' : this.resourceStrings.igx_grid_snackbar_addrow_actiontext;
29✔
5950
        this.lastAddedRowIndex = index;
29✔
5951
        this.addRowSnackbar.open();
29✔
5952
    }
5953

5954
    /* blazorCsSuppress */
5955
    /**
5956
     * Navigates to a position in the grid based on provided `rowindex` and `visibleColumnIndex`.
5957
     *
5958
     * @remarks
5959
     * Also can execute a custom logic over the target element,
5960
     * through a callback function that accepts { targetType: GridKeydownTargetType, target: Object }
5961
     * @example
5962
     * ```typescript
5963
     *  this.grid.navigateTo(10, 3, (args) => { args.target.nativeElement.focus(); });
5964
     * ```
5965
     */
5966
    public navigateTo(rowIndex: number, visibleColIndex = -1, cb: (args: any) => void = null) {
410✔
5967
        const totalItems = (this as any).totalItemCount ?? this.dataView.length - 1;
835✔
5968
        if (rowIndex < 0 || rowIndex > totalItems || (visibleColIndex !== -1
835!
5969
            && this._columns.map(col => col.visibleIndex).indexOf(visibleColIndex) === -1)) {
5,057✔
5970
            return;
×
5971
        }
5972
        if (this.dataView.slice(rowIndex, rowIndex + 1).find(rec => rec.expression || rec.childGridsData)) {
835✔
5973
            visibleColIndex = -1;
43✔
5974
        }
5975
        // If the target row is pinned no need to scroll as well.
5976
        const shouldScrollVertically = this.navigation.shouldPerformVerticalScroll(rowIndex, visibleColIndex);
835✔
5977
        const shouldScrollHorizontally = this.navigation.shouldPerformHorizontalScroll(visibleColIndex, rowIndex);
835✔
5978
        if (shouldScrollVertically) {
835✔
5979
            this.navigation.performVerticalScrollToCell(rowIndex, visibleColIndex, () => {
143✔
5980
                if (shouldScrollHorizontally) {
140✔
5981
                    this.navigation.performHorizontalScrollToCell(visibleColIndex, () =>
16✔
5982
                        this.executeCallback(rowIndex, visibleColIndex, cb));
16✔
5983
                } else {
5984
                    this.executeCallback(rowIndex, visibleColIndex, cb);
124✔
5985
                }
5986
            });
5987
        } else if (shouldScrollHorizontally) {
692✔
5988
            this.navigation.performHorizontalScrollToCell(visibleColIndex, () => {
93✔
5989
                if (shouldScrollVertically) {
93!
5990
                    this.navigation.performVerticalScrollToCell(rowIndex, visibleColIndex, () =>
×
5991
                        this.executeCallback(rowIndex, visibleColIndex, cb));
×
5992
                } else {
5993
                    this.executeCallback(rowIndex, visibleColIndex, cb);
93✔
5994
                }
5995
            });
5996
        } else {
5997
            this.executeCallback(rowIndex, visibleColIndex, cb);
599✔
5998
        }
5999
    }
6000

6001
    /* blazorCsSuppress */
6002
    /**
6003
     * Returns `ICellPosition` which defines the next cell,
6004
     * according to the current position, that match specific criteria.
6005
     *
6006
     * @remarks
6007
     * You can pass callback function as a third parameter of `getPreviousCell` method.
6008
     * The callback function accepts IgxColumnComponent as a param
6009
     * @example
6010
     * ```typescript
6011
     *  const nextEditableCellPosition = this.grid.getNextCell(0, 3, (column) => column.editable);
6012
     * ```
6013
     */
6014
    public getNextCell(currRowIndex: number, curVisibleColIndex: number,
6015
        callback: (IgxColumnComponent) => boolean = null): ICellPosition {
×
6016
        const columns = this._columns.filter(col => !col.columnGroup && col.visibleIndex >= 0);
477✔
6017
        const dataViewIndex = this._getDataViewIndex(currRowIndex);
56✔
6018
        if (!this.isValidPosition(dataViewIndex, curVisibleColIndex)) {
56✔
6019
            return { rowIndex: currRowIndex, visibleColumnIndex: curVisibleColIndex };
2✔
6020
        }
6021
        const colIndexes = callback ? columns.filter((col) => callback(col)).map(editCol => editCol.visibleIndex).sort((a, b) => a - b) :
460!
6022
            columns.map(editCol => editCol.visibleIndex).sort((a, b) => a - b);
×
6023
        const nextCellIndex = colIndexes.find(index => index > curVisibleColIndex);
147✔
6024
        if (this.dataView.slice(dataViewIndex, dataViewIndex + 1)
54✔
6025
            .find(rec => !rec.expression && !rec.summaries && !rec.childGridsData && !rec.detailsData) && nextCellIndex !== undefined) {
54✔
6026
            return { rowIndex: currRowIndex, visibleColumnIndex: nextCellIndex };
39✔
6027
        } else {
6028
            const nextIndex = this.getNextDataRowIndex(currRowIndex)
15✔
6029
            if (colIndexes.length === 0 || nextIndex === currRowIndex) {
15!
6030
                return { rowIndex: currRowIndex, visibleColumnIndex: curVisibleColIndex };
×
6031
            } else {
6032
                return { rowIndex: nextIndex, visibleColumnIndex: colIndexes[0] };
15✔
6033
            }
6034
        }
6035
    }
6036

6037
    /* blazorCsSuppress */
6038
    /**
6039
     * Returns `ICellPosition` which defines the previous cell,
6040
     * according to the current position, that match specific criteria.
6041
     *
6042
     * @remarks
6043
     * You can pass callback function as a third parameter of `getPreviousCell` method.
6044
     * The callback function accepts IgxColumnComponent as a param
6045
     * @example
6046
     * ```typescript
6047
     *  const previousEditableCellPosition = this.grid.getPreviousCell(0, 3, (column) => column.editable);
6048
     * ```
6049
     */
6050
    public getPreviousCell(currRowIndex: number, curVisibleColIndex: number,
6051
        callback: (IgxColumnComponent) => boolean = null): ICellPosition {
1✔
6052
        const columns = this._columns.filter(col => !col.columnGroup && col.visibleIndex >= 0);
300✔
6053
        const dataViewIndex = this._getDataViewIndex(currRowIndex);
41✔
6054
        if (!this.isValidPosition(dataViewIndex, curVisibleColIndex)) {
41✔
6055
            return { rowIndex: currRowIndex, visibleColumnIndex: curVisibleColIndex };
2✔
6056
        }
6057
        const colIndexes = callback ? columns.filter((col) => callback(col)).map(editCol => editCol.visibleIndex).sort((a, b) => b - a) :
238✔
6058
            columns.map(editCol => editCol.visibleIndex).sort((a, b) => b - a);
4✔
6059
        const prevCellIndex = colIndexes.find(index => index < curVisibleColIndex);
141✔
6060
        if (this.dataView.slice(dataViewIndex, dataViewIndex + 1)
39✔
6061
            .find(rec => !rec.expression && !rec.summaries && !rec.childGridsData && !rec.detailsData) && prevCellIndex !== undefined) {
39✔
6062
            return { rowIndex: currRowIndex, visibleColumnIndex: prevCellIndex };
24✔
6063
        } else {
6064
            const prevIndex = this.getNextDataRowIndex(currRowIndex, true);
15✔
6065
            if (colIndexes.length === 0 || prevIndex === currRowIndex) {
15✔
6066
                return { rowIndex: currRowIndex, visibleColumnIndex: curVisibleColIndex };
7✔
6067
            } else {
6068
                return { rowIndex: prevIndex, visibleColumnIndex: colIndexes[0] };
8✔
6069
            }
6070
        }
6071
    }
6072

6073
    /**
6074
     * @hidden
6075
     * @internal
6076
     */
6077
    public endRowEditTabStop(commit = true, event?: Event) {
×
6078
        const canceled = this.crudService.endEdit(commit, event);
15✔
6079

6080
        if (canceled) {
15✔
6081
            return true;
3✔
6082
        }
6083

6084
        this.navigation.restoreActiveNodeFocus();
12✔
6085
    }
6086

6087
    /**
6088
     * @hidden @internal
6089
     */
6090
    public trackColumnChanges(index, col) {
6091
        return col.field + col._calcWidth;
1,697,262✔
6092
    }
6093

6094
    /**
6095
     * @hidden
6096
     */
6097
    public isExpandedGroup(_group: IGroupByRecord): boolean {
6098
        return undefined;
×
6099
    }
6100

6101
    /**
6102
     * @hidden @internal
6103
     * TODO: MOVE to CRUD
6104
     */
6105
    public openRowOverlay(id) {
6106
        this.configureRowEditingOverlay(id, this.rowList.length <= MIN_ROW_EDITING_COUNT_THRESHOLD);
208✔
6107

6108
        this.rowEditingOverlay.open(this.rowEditSettings);
208✔
6109
        this.rowEditingOverlay.element.addEventListener('wheel', this.rowEditingWheelHandler.bind(this));
208✔
6110
    }
6111

6112
    /**
6113
     * @hidden @internal
6114
     */
6115
    public closeRowEditingOverlay() {
6116
        this.rowEditingOverlay.element.removeEventListener('wheel', this.rowEditingWheelHandler);
130✔
6117
        this.rowEditPositioningStrategy.isTopInitialPosition = null;
130✔
6118
        this.rowEditingOverlay.close();
130✔
6119
        this.rowEditingOverlay.element.parentElement.style.display = '';
130✔
6120
    }
6121

6122
    /**
6123
     * @hidden @internal
6124
     */
6125
    public toggleRowEditingOverlay(show) {
6126
        const rowStyle = this.rowEditingOverlay.element.style;
280✔
6127
        if (show) {
280!
6128
            rowStyle.display = 'block';
280✔
6129
        } else {
6130
            rowStyle.display = 'none';
×
6131
        }
6132
    }
6133

6134
    /**
6135
     * @hidden @internal
6136
     */
6137
    public repositionRowEditingOverlay(row: RowType) {
6138
        if (row && !this.rowEditingOverlay.collapsed) {
868✔
6139
            const rowStyle = this.rowEditingOverlay.element.parentElement.style;
72✔
6140
            if (row) {
72!
6141
                rowStyle.display = '';
72✔
6142
                this.configureRowEditingOverlay(row.key);
72✔
6143
                this.rowEditingOverlay.reposition();
72✔
6144
            } else {
6145
                rowStyle.display = 'none';
×
6146
            }
6147
        }
6148
    }
6149

6150
    /**
6151
     * @hidden @internal
6152
     */
6153
    public cachedViewLoaded(args: ICachedViewLoadedEventArgs) {
6154
        if (this.hasHorizontalScroll()) {
628✔
6155
            const tmplId = args.context.templateID.type;
443✔
6156
            const index = args.context.index;
443✔
6157
            args.view.detectChanges();
443✔
6158
            this.zone.onStable.pipe(first()).subscribe(() => {
443✔
6159
                const row = tmplId === 'dataRow' ? this.gridAPI.get_row_by_index(index) : null;
443✔
6160
                const summaryRow = tmplId === 'summaryRow' ? this.summariesRowList.find((sr) => sr.dataRowIndex === index) : null;
443✔
6161
                if (row && row instanceof IgxRowDirective) {
443✔
6162
                    this._restoreVirtState(row);
266✔
6163
                } else if (summaryRow) {
177✔
6164
                    this._restoreVirtState(summaryRow);
43✔
6165
                }
6166
            });
6167
        }
6168
    }
6169

6170
    /**
6171
     * Opens the advanced filtering dialog.
6172
     */
6173
    public openAdvancedFilteringDialog(overlaySettings?: OverlaySettings) {
6174
        const settings = overlaySettings ? overlaySettings : this._advancedFilteringOverlaySettings;
44!
6175
        if (!this._advancedFilteringOverlayId) {
44✔
6176
            this._advancedFilteringOverlaySettings.target =
41✔
6177
                (this as any).rootGrid ? (this as any).rootGrid.nativeElement : this.nativeElement;
41!
6178
            this._advancedFilteringOverlaySettings.outlet = this.outlet;
41✔
6179

6180
            this._advancedFilteringOverlayId = this.overlayService.attach(
41✔
6181
                IgxAdvancedFilteringDialogComponent,
6182
                this.viewRef,
6183
                settings);
6184
            this.overlayService.show(this._advancedFilteringOverlayId);
41✔
6185
        }
6186
    }
6187

6188
    /**
6189
     * Closes the advanced filtering dialog.
6190
     *
6191
     * @param applyChanges indicates whether the changes should be applied
6192
     */
6193
    public closeAdvancedFilteringDialog(applyChanges: boolean) {
6194
        if (this._advancedFilteringOverlayId) {
3✔
6195
            const advancedFilteringOverlay = this.overlayService.getOverlayById(this._advancedFilteringOverlayId);
3✔
6196
            const advancedFilteringDialog = advancedFilteringOverlay.componentRef.instance as IgxAdvancedFilteringDialogComponent;
3✔
6197

6198
            if (applyChanges) {
3✔
6199
                advancedFilteringDialog.applyChanges();
1✔
6200
            }
6201
            advancedFilteringDialog.closeDialog();
3✔
6202
        }
6203
    }
6204

6205
    /**
6206
     * @hidden @internal
6207
     */
6208
    public getEmptyRecordObjectFor(inRow: RowType) {
6209
        const row = { ...inRow?.data };
49✔
6210
        Object.keys(row).forEach(key => row[key] = undefined);
213✔
6211
        const id = this.generateRowID();
49✔
6212
        row[this.primaryKey] = id;
49✔
6213
        return { rowID: id, data: row, recordRef: row };
49✔
6214
    }
6215

6216
    /**
6217
     * @hidden @internal
6218
     */
6219
    public hasHorizontalScroll() {
6220
        return this.totalWidth - this.unpinnedWidth > 0 && this.width !== null;
11,221✔
6221
    }
6222

6223
    /**
6224
     * @hidden @internal
6225
     */
6226
    public isSummaryRow(rowData): boolean {
6227
        return rowData && rowData.summaries && (rowData.summaries instanceof Map);
434,902✔
6228
    }
6229

6230
    /**
6231
     * @hidden @internal
6232
     */
6233
    public triggerPipes() {
6234
        this.pipeTrigger++;
129✔
6235
        this.cdr.detectChanges();
129✔
6236
    }
6237

6238
    /**
6239
     * @hidden
6240
     */
6241
    public rowEditingWheelHandler(event: WheelEvent) {
6242
        if (event.deltaY > 0) {
×
6243
            this.verticalScrollContainer.scrollNext();
×
6244
        } else {
6245
            this.verticalScrollContainer.scrollPrev();
×
6246
        }
6247
    }
6248

6249
    /**
6250
     * @hidden
6251
     */
6252
    public getUnpinnedIndexById(id) {
6253
        return this.unpinnedRecords.findIndex(x => x[this.primaryKey] === id);
102✔
6254
    }
6255

6256
    /**
6257
     * Finishes the row transactions on the current row and returns whether the grid editing was canceled.
6258
     *
6259
     * @remarks
6260
     * If `commit === true`, passes them from the pending state to the data (or transaction service)
6261
     * @example
6262
     * ```html
6263
     * <button type="button" igxButton (click)="grid.endEdit(true)">Commit Row</button>
6264
     * ```
6265
     * @param commit
6266
     */
6267
    // TODO: Facade for crud service refactoring. To be removed
6268
    // TODO: do not remove this, as it is used in rowEditTemplate, but mark is as internal and hidden
6269
    /* blazorCSSuppress */
6270
    public endEdit(commit = true, event?: Event): boolean {
1✔
6271
        const document = this.nativeElement?.getRootNode() as Document | ShadowRoot;
6✔
6272
        const focusWithin = this.nativeElement?.contains(document.activeElement);
6✔
6273

6274
        const success = this.crudService.endEdit(commit, event);
6✔
6275

6276
        if (focusWithin) {
6✔
6277
            // restore focus for navigation
6278
            this.navigation.restoreActiveNodeFocus();
2✔
6279
        } else if (this.navigation.activeNode) {
4✔
6280
            // grid already lost focus, clear active node
6281
            this.clearActiveNode();
4✔
6282
        }
6283

6284
        return success;
6✔
6285
    }
6286

6287
    /**
6288
     * Enters add mode by spawning the UI under the specified row by rowID.
6289
     *
6290
     * @remarks
6291
     * If null is passed as rowID, the row adding UI is spawned as the first record in the data view
6292
     * @remarks
6293
     * Spawning the UI to add a child for a record only works if you provide a rowID
6294
     * @example
6295
     * ```typescript
6296
     * this.grid.beginAddRowById('ALFKI');
6297
     * this.grid.beginAddRowById('ALFKI', true);
6298
     * this.grid.beginAddRowById(null);
6299
     * ```
6300
     * @param rowID - The rowID to spawn the add row UI for, or null to spawn it as the first record in the data view
6301
     * @param asChild - Whether the record should be added as a child. Only applicable to igxTreeGrid.
6302
     */
6303
    public beginAddRowById(rowID: any, asChild?: boolean): void {
6304
        let index = rowID;
4✔
6305
        if (rowID == null) {
4✔
6306
            if (asChild) {
3!
6307
                console.warn('The record cannot be added as a child to an unspecified record.');
×
6308
                return;
×
6309
            }
6310
            index = null;
3✔
6311
        } else {
6312
            // find the index of the record with that PK
6313
            index = this.gridAPI.get_rec_index_by_id(rowID, this.dataView);
1✔
6314
            if (index === -1) {
1!
6315
                console.warn('No row with the specified ID was found.');
×
6316
                return;
×
6317
            }
6318
        }
6319

6320
        this._addRowForIndex(index, asChild);
4✔
6321
    }
6322

6323
    protected _addRowForIndex(index: number, asChild?: boolean) {
6324
        if (!this.dataView.length) {
4!
6325
            this.beginAddRowForIndex(index, asChild);
×
6326
            return;
×
6327
        }
6328
        // check if the index is valid - won't support anything outside the data view
6329
        if (index >= 0 && index < this.dataView.length) {
4!
6330
            // check if the index is in the view port
6331
            if ((index < this.virtualizationState.startIndex ||
4✔
6332
                index >= this.virtualizationState.startIndex + this.virtualizationState.chunkSize) &&
6333
                !this.isRecordPinnedByViewIndex(index)) {
6334
                this.verticalScrollContainer.chunkLoad
1✔
6335
                    .pipe(first(), takeUntil(this.destroy$))
6336
                    .subscribe(() => {
6337
                        this.beginAddRowForIndex(index, asChild);
1✔
6338
                    });
6339
                this.navigateTo(index);
1✔
6340
                this.notifyChanges(true);
1✔
6341
                return;
1✔
6342
            }
6343
            this.beginAddRowForIndex(index, asChild);
3✔
6344
        } else {
6345
            console.warn('The row with the specified PK or index is outside of the current data view.');
×
6346
        }
6347
    }
6348

6349
    /* csSuppress */
6350
    /**
6351
     * Enters add mode by spawning the UI at the specified index.
6352
     *
6353
     * @remarks
6354
     * Accepted values for index are integers from 0 to this.grid.dataView.length
6355
     * @example
6356
     * ```typescript
6357
     * this.grid.beginAddRowByIndex(0);
6358
     * ```
6359
     * @param index - The index to spawn the UI at. Accepts integers from 0 to this.grid.dataView.length
6360
     */
6361
    public beginAddRowByIndex(index: number): void {
6362
        if (index === 0) {
1✔
6363
            return this.beginAddRowById(null);
1✔
6364
        }
6365
        return this._addRowForIndex(index - 1);
×
6366
    }
6367

6368
    /**
6369
     * @hidden
6370
     */
6371
    public preventHeaderScroll(args) {
6372
        if (args.target.scrollLeft !== 0) {
×
6373
            (this.navigation as any).forOfDir().getScroll().scrollLeft = args.target.scrollLeft;
×
6374
            args.target.scrollLeft = 0;
×
6375
        }
6376
    }
6377

6378
    protected beginAddRowForIndex(index: number, asChild = false) {
3✔
6379
        // TODO is row from rowList suitable for enterAddRowMode
6380
        const row = index == null ?
4✔
6381
            null : this.rowList.find(r => r.index === index);
2✔
6382
        if (row !== undefined) {
4!
6383
            this.crudService.enterAddRowMode(row, asChild);
4✔
6384
        } else {
6385
            console.warn('No row with the specified PK or index was found.');
×
6386
        }
6387
    }
6388

6389
    protected switchTransactionService(val: boolean) {
6390
        if (val) {
244✔
6391
            this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.Base);
243✔
6392
        } else {
6393
            this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.None);
1✔
6394
        }
6395

6396
        if (this.dataCloneStrategy) {
244✔
6397
            this._transactions.cloneStrategy = this.dataCloneStrategy;
244✔
6398
        }
6399
    }
6400

6401
    protected subscribeToTransactions(): void {
6402
        this.transactionChange$.next();
3,785✔
6403
        this.transactions.onStateUpdate.pipe(takeUntil(merge(this.destroy$, this.transactionChange$)))
3,785✔
6404
            .subscribe(this.transactionStatusUpdate.bind(this));
6405
    }
6406

6407
    protected transactionStatusUpdate(event: StateUpdateEvent) {
6408
        let actions: Action<Transaction>[] = [];
324✔
6409
        if (event.origin === TransactionEventOrigin.REDO) {
324✔
6410
            actions = event.actions ? event.actions.filter(x => x.transaction.type === TransactionType.DELETE) : [];
44!
6411
        } else if (event.origin === TransactionEventOrigin.UNDO) {
285✔
6412
            actions = event.actions ? event.actions.filter(x => x.transaction.type === TransactionType.ADD) : [];
54!
6413
        }
6414
        if (actions.length > 0) {
324✔
6415
            for (const action of actions) {
22✔
6416
                if (this.selectionService.isRowSelected(action.transaction.id)) {
27!
6417
                    this.selectionService.deselectRow(action.transaction.id);
×
6418
                }
6419
            }
6420
        }
6421
        if (event.origin === TransactionEventOrigin.REDO || event.origin === TransactionEventOrigin.UNDO) {
324✔
6422
            event.actions.forEach(x => {
88✔
6423
                if (x.transaction.type === TransactionType.UPDATE) {
98✔
6424
                    const value = this.transactions.getAggregatedValue(x.transaction.id, true);
43✔
6425
                    this.validation.update(x.transaction.id, value ?? x.recordRef);
43✔
6426
                } else if (x.transaction.type === TransactionType.DELETE || x.transaction.type === TransactionType.ADD) {
55✔
6427
                    const value = this.transactions.getAggregatedValue(x.transaction.id, true);
55✔
6428
                    if (value) {
55✔
6429
                        this.validation.create(x.transaction.id, value ?? x.recordRef);
29!
6430
                        this.validation.update(x.transaction.id, value ?? x.recordRef);
29!
6431
                        this.validation.markAsTouched(x.transaction.id);
29✔
6432
                    } else {
6433
                        this.validation.clear(x.transaction.id);
26✔
6434
                    }
6435
                }
6436

6437
            });
6438
        }
6439

6440
        this.selectionService.clearHeaderCBState();
324✔
6441
        this.summaryService.clearSummaryCache();
324✔
6442
        this.pipeTrigger++;
324✔
6443
        this.notifyChanges();
324✔
6444
    }
6445

6446
    protected writeToData(rowIndex: number, value: any) {
6447
        mergeObjects(this.gridAPI.get_all_data()[rowIndex], value);
×
6448
    }
6449

6450
    protected _restoreVirtState(row) {
6451
        // check virtualization state of data record added from cache
6452
        // in case state is no longer valid - update it.
6453
        const rowForOf = row.virtDirRow;
309✔
6454
        const gridScrLeft = rowForOf.getScroll().scrollLeft;
309✔
6455
        rowForOf.onHScroll(gridScrLeft);
309✔
6456
        rowForOf.cdr.detectChanges();
309✔
6457
    }
6458

6459
    protected changeRowEditingOverlayStateOnScroll(row: RowType) {
6460
        if (!this.rowEditable || !this.rowEditingOverlay || this.rowEditingOverlay.collapsed) {
16✔
6461
            return;
14✔
6462
        }
6463
        if (!row) {
2!
6464
            this.toggleRowEditingOverlay(false);
×
6465
        } else {
6466
            this.repositionRowEditingOverlay(row);
2✔
6467
        }
6468
    }
6469

6470
    /**
6471
     * Should be called when data and/or isLoading input changes so that the overlay can be
6472
     * hidden/shown based on the current value of shouldOverlayLoading
6473
     */
6474
    protected evaluateLoadingState() {
6475
        if (this.shouldOverlayLoading) {
3,272✔
6476
            // a new overlay should be shown
6477
            const overlaySettings: OverlaySettings = {
15✔
6478
                outlet: this.loadingOutlet,
6479
                closeOnOutsideClick: false,
6480
                positionStrategy: new ContainerPositionStrategy()
6481
            };
6482
            this.loadingOverlay.open(overlaySettings);
15✔
6483
        } else {
6484
            this.loadingOverlay.close();
3,257✔
6485
        }
6486
    }
6487

6488
    /**
6489
     * @hidden
6490
     * Sets grid width i.e. this.calcWidth
6491
     */
6492
    protected calculateGridWidth() {
6493
        let width;
6494

6495
        if (this.isPercentWidth) {
11,727✔
6496
            /* width in %*/
6497
            const computed = this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('width');
5,392✔
6498
            width = computed.indexOf('%') === -1 ? parseFloat(computed) : null;
5,392✔
6499
        } else {
6500
            width = parseInt(this.width, 10);
6,335✔
6501
        }
6502

6503
        if (!width && this.nativeElement) {
11,727✔
6504
            width = this.nativeElement.offsetWidth;
41✔
6505
        }
6506

6507

6508
        if (this.width === null || !width) {
11,727✔
6509
            this.isColumnWidthSum = true;
41✔
6510
            width = this.getColumnWidthSum();
41✔
6511
        } else {
6512
            this.isColumnWidthSum = false;
11,686✔
6513
        }
6514

6515
        if (this.hasVerticalScroll() && this.width !== null) {
11,727✔
6516
            width -= this.scrollSize;
2,391✔
6517
        }
6518
        if ((Number.isFinite(width) || width === null) && width !== this.calcWidth) {
11,727!
6519
            this.calcWidth = width;
3,778✔
6520
        }
6521
        this._derivePossibleWidth();
11,727✔
6522
    }
6523

6524
    /**
6525
     * @hidden
6526
     * Sets columns defaultWidth property
6527
     */
6528
    protected _derivePossibleWidth() {
6529
        if (!this.columnWidthSetByUser) {
11,727✔
6530
            this._columnWidth = this.width !== null ? this.getPossibleColumnWidth() : this.minColumnWidth + 'px';
10,969✔
6531
        }
6532
        this._columns.forEach((column: IgxColumnComponent) => {
11,727✔
6533
            if (this.hasColumnLayouts && parseFloat(this._columnWidth)) {
74,238✔
6534
                const columnWidthCombined = parseFloat(this._columnWidth) * (column.colEnd ? column.colEnd - column.colStart : 1);
3,825✔
6535
                column.defaultWidth = columnWidthCombined + 'px';
3,825✔
6536
            } else {
6537
                // D.K. March 29th, 2021 #9145 Consider min/max width when setting defaultWidth property
6538
                column.defaultWidth = this.getExtremumBasedColWidth(column);
70,413✔
6539
                column.resetCaches();
70,413✔
6540
            }
6541
        });
6542
        this.resetCachedWidths();
11,727✔
6543
    }
6544

6545
    /**
6546
     * @hidden
6547
     * @internal
6548
     */
6549
    protected getExtremumBasedColWidth(column: IgxColumnComponent): string {
6550
        let width = this._columnWidth;
70,413✔
6551
        if (width && typeof width !== 'string') {
70,413!
6552
            width = String(width);
×
6553
        }
6554
        const minWidth = width.indexOf('%') === -1 ? column.minWidthPx : column.minWidthPercent;
70,413✔
6555
        const maxWidth = width.indexOf('%') === -1 ? column.maxWidthPx : column.maxWidthPercent;
70,413✔
6556
        if (column.hidden) {
70,413✔
6557
            return width;
1,326✔
6558
        }
6559

6560
        if (minWidth > parseFloat(width)) {
69,087!
6561
            width = String(column.minWidth);
×
6562
        } else if (maxWidth < parseFloat(width)) {
69,087✔
6563
            width = String(column.maxWidth);
1,114✔
6564
        }
6565

6566
        // if no px or % are defined in maxWidth/minWidth consider it px
6567
        if (width.indexOf('%') === -1 && width.indexOf('px') === -1) {
69,087✔
6568
            width += 'px';
276✔
6569
        }
6570
        return width;
69,087✔
6571
    }
6572

6573
    protected resetNotifyChanges() {
6574
        this._cdrRequestRepaint = false;
8,115✔
6575
        this._cdrRequests = false;
8,115✔
6576
    }
6577

6578
    /** @hidden @internal */
6579
    public resolveOutlet() {
6580
        return this._userOutletDirective ? this._userOutletDirective : this._outletDirective;
237,473!
6581
    }
6582

6583
    /**
6584
     * Reorder columns in the main columnList and _columns collections.
6585
     *
6586
     * @hidden
6587
     */
6588
    protected _moveColumns(from: IgxColumnComponent, to: IgxColumnComponent, pos: DropPosition) {
6589
        const orderedList = this._pinnedColumns.concat(this._unpinnedColumns);
138✔
6590
        const list = orderedList;
138✔
6591
        this._reorderColumns(from, to, pos, list);
138✔
6592
        const newList = this._resetColumnList(list);
138✔
6593
        this.updateColumns(newList);
138✔
6594
    }
6595

6596

6597
    /**
6598
     * Update internal column's collection.
6599
     * @hidden
6600
     */
6601
    public updateColumns(newColumns: IgxColumnComponent[]) {
6602
        // update internal collections to retain order.
6603
        this._pinnedColumns = newColumns
5,697✔
6604
            .filter((c) => c.pinned);
34,399✔
6605
        this._unpinnedColumns = newColumns.filter((c) => !c.pinned);
34,399✔
6606
        this._columns = newColumns;
5,697✔
6607
        if (this._columns && this._filteringExpressionsTree) {
5,697✔
6608
            this._filteringExpressionsTree = recreateTreeFromFields(this._filteringExpressionsTree, this.columns) as IFilteringExpressionsTree;
5,697✔
6609
        }
6610
        this.resetCaches();
5,697✔
6611
    }
6612

6613
    /**
6614
     * @hidden
6615
     */
6616
    protected _resetColumnList(list?) {
6617
        if (!list) {
138!
6618
            list = this._columns;
×
6619
        }
6620
        let newList = [];
138✔
6621
        list.filter(c => c.level === 0).forEach(p => {
1,189✔
6622
            newList.push(p);
814✔
6623
            if (p.columnGroup) {
814✔
6624
                newList = newList.concat(p.allChildren);
138✔
6625
            }
6626
        });
6627
        return newList;
138✔
6628
    }
6629

6630
    /**
6631
     * Reorders columns inside the passed column collection.
6632
     * When reordering column group collection, the collection is not flattened.
6633
     * In all other cases, the columns collection is flattened, this is why adittional calculations on the dropIndex are done.
6634
     *
6635
     * @hidden
6636
     */
6637
    protected _reorderColumns(from: IgxColumnComponent, to: IgxColumnComponent, position: DropPosition, columnCollection: any[],
6638
        inGroup = false) {
138✔
6639
        const fromIndex = columnCollection.indexOf(from);
154✔
6640
        const childColumnsCount = inGroup ? 1 : from.allChildren.length + 1;
154✔
6641
        columnCollection.splice(fromIndex, childColumnsCount);
154✔
6642
        let dropIndex = columnCollection.indexOf(to);
154✔
6643
        if (position === DropPosition.AfterDropTarget) {
154✔
6644
            dropIndex++;
87✔
6645
            if (!inGroup && to.columnGroup) {
87✔
6646
                dropIndex += to.allChildren.length;
21✔
6647
            }
6648
        }
6649
        columnCollection.splice(dropIndex, 0, from);
154✔
6650
    }
6651

6652
    /**
6653
     * Reorder column group collection.
6654
     *
6655
     * @hidden
6656
     */
6657
    protected _moveChildColumns(parent: IgxColumnComponent, from: IgxColumnComponent, to: IgxColumnComponent, pos: DropPosition) {
6658
        const buffer = parent.children.toArray();
16✔
6659
        this._reorderColumns(from, to, pos, buffer, true);
16✔
6660
        parent.children.reset(buffer);
16✔
6661
    }
6662

6663
    /**
6664
     * @hidden @internal
6665
     */
6666
    protected setupColumns() {
6667
        if (this.autoGenerate) {
3,347✔
6668
            this.autogenerateColumns();
907✔
6669
        } else {
6670
            this._columns = this.getColumnList();
2,440✔
6671
            if (this._columns && this._filteringExpressionsTree) {
2,440✔
6672
                this._filteringExpressionsTree = recreateTreeFromFields(this._filteringExpressionsTree, this._columns) as IFilteringExpressionsTree;
2,440✔
6673
            }
6674
        }
6675

6676
        this.initColumns(this._columns, (col: IgxColumnComponent) => this.columnInit.emit(col));
21,576✔
6677
        this.columnListDiffer.diff(this.columnList);
3,347✔
6678

6679
        this.columnList.changes
3,347✔
6680
            .pipe(takeUntil(this.destroy$))
6681
            .subscribe((change: QueryList<IgxColumnComponent>) => {
6682
                this.onColumnsChanged(change);
78✔
6683
            });
6684
    }
6685

6686
    protected getColumnList() {
6687
        return this.columnList.toArray();
2,253✔
6688
    }
6689

6690
    /**
6691
     * @hidden
6692
     */
6693
    protected deleteRowFromData(rowID: any, index: number) {
6694
        //  if there is a row (index !== 0) delete it
6695
        //  if there is a row in ADD or UPDATE state change it's state to DELETE
6696
        if (index !== -1) {
×
6697
            if (this.transactions.enabled) {
×
6698
                const transaction: Transaction = { id: rowID, type: TransactionType.DELETE, newValue: null };
×
6699
                this.transactions.add(transaction, this.data[index]);
×
6700
            } else {
6701
                this.data.splice(index, 1);
×
6702
            }
6703
        } else {
6704
            const state: State = this.transactions.getState(rowID);
×
6705
            this.transactions.add({ id: rowID, type: TransactionType.DELETE, newValue: null }, state && state.recordRef);
×
6706
        }
6707
    }
6708

6709

6710
    /**
6711
     * @hidden @internal
6712
     */
6713
    protected getDataBasedBodyHeight(): number {
6714
        return !this.data || (this.data.length < this._defaultTargetRecordNumber) ?
1,053✔
6715
            0 : this.defaultTargetBodyHeight;
6716
    }
6717

6718
    /**
6719
     * @hidden @internal
6720
     */
6721
    protected onPinnedRowsChanged(change: QueryList<IgxGridRowComponent>) {
6722
        const diff = this.rowListDiffer.diff(change);
182✔
6723
        if (diff) {
182✔
6724
            this.notifyChanges(true);
182✔
6725
        }
6726
    }
6727

6728
    /**
6729
     * @hidden
6730
     */
6731
    protected onColumnsChanged(change: QueryList<IgxColumnComponent>) {
6732
        const diff = this.columnListDiffer.diff(change);
67✔
6733

6734
        if (this.autoGenerate && this._columns.length === 0 && this._autoGeneratedCols.length > 0) {
67!
6735
            // In Ivy if there are nested conditional templates the content children are re-evaluated
6736
            // hence autogenerated columns are cleared and need to be reset.
6737
            this.updateColumns(this._autoGeneratedCols);
×
6738
            return;
×
6739
        }
6740
        if (diff) {
67✔
6741
            let added = false;
67✔
6742
            let removed = false;
67✔
6743
            let pinning = false;
67✔
6744
            diff.forEachAddedItem((record: IterableChangeRecord<IgxColumnComponent>) => {
67✔
6745
                added = true;
1,036✔
6746
                if (record.item.pinned) {
1,036✔
6747
                    this._pinnedColumns.push(record.item);
1✔
6748
                    pinning = true;
1✔
6749
                } else {
6750
                    this._unpinnedColumns.push(record.item);
1,035✔
6751
                }
6752
            });
6753

6754
            this.initColumns(this.columnList.toArray(), (col: IgxColumnComponent) => this.columnInit.emit(col));
1,297✔
6755
            if (pinning) {
67✔
6756
                this.initPinning();
1✔
6757
            }
6758

6759
            diff.forEachRemovedItem((record: IterableChangeRecord<IgxColumnComponent | IgxColumnGroupComponent>) => {
67✔
6760
                const isColumnGroup = record.item instanceof IgxColumnGroupComponent;
135✔
6761
                if (!isColumnGroup) {
135✔
6762
                    // Clear Grouping
6763
                    this.gridAPI.clear_groupby(record.item.field);
132✔
6764

6765
                    // Clear Filtering
6766
                    this.filteringService.clear_filter(record.item.field);
132✔
6767

6768
                    // Close filter row
6769
                    if (this.filteringService.isFilterRowVisible
132!
6770
                        && this.filteringService.filteredColumn
6771
                        && this.filteringService.filteredColumn.field === record.item.field) {
6772
                        this.filteringRow.close();
×
6773
                    }
6774

6775
                    // Clear Sorting
6776
                    this.gridAPI.clear_sort(record.item.field);
132✔
6777

6778
                    // Remove column selection
6779
                    this.selectionService.deselectColumnsWithNoEvent([record.item.field]);
132✔
6780
                }
6781
                removed = true;
135✔
6782
            });
6783

6784
            this.resetCaches();
67✔
6785

6786
            if (added || removed) {
67✔
6787
                this.onColumnsAddedOrRemoved();
63✔
6788
            }
6789
        }
6790
    }
6791

6792
    protected checkPrimaryKeyField() {
6793
        if (this.primaryKey && this.data?.length && !(this.primaryKey in this.data[0])) {
4,638✔
6794
            console.warn(`Field "${this.primaryKey}" is not defined in the data. Set \`primaryKey\` to a valid field.`);
9✔
6795
        }
6796
    }
6797

6798
    /**
6799
     * @hidden @internal
6800
     */
6801
    protected onColumnsAddedOrRemoved() {
6802
        this.summaryService.clearSummaryCache();
63✔
6803
        Promise.resolve().then(() => {
63✔
6804
            // `onColumnsChanged` can be executed midway a current detectChange cycle and markForCheck will be ignored then.
6805
            // This ensures that we will wait for the current cycle to end so we can trigger a new one and ngDoCheck to fire.
6806
            this.notifyChanges(true);
63✔
6807
        });
6808
    }
6809

6810
    /**
6811
     * @hidden
6812
     */
6813
    protected calculateGridSizes(recalcFeatureWidth = true) {
7,330✔
6814
        /*
6815
            TODO: (R.K.) This layered lasagne should be refactored
6816
            ASAP. The reason I have to reset the caches so many times is because
6817
            after teach `detectChanges` call they are filled with invalid
6818
            state. Of course all of this happens midway through the grid
6819
            sizing process which of course, uses values from the caches, thus resulting
6820
            in a broken layout.
6821
        */
6822
        this.cdr.detectChanges();
7,829✔
6823
        this.resetCaches(recalcFeatureWidth);
7,829✔
6824
        const hasScroll = this.hasVerticalScroll();
7,829✔
6825
        const hasHScroll = !this.isHorizontalScrollHidden;
7,829✔
6826
        this.calculateGridWidth();
7,829✔
6827
        this.resetCaches(recalcFeatureWidth);
7,829✔
6828
        this.cdr.detectChanges();
7,829✔
6829
        this.calculateGridHeight();
7,829✔
6830

6831
        if (this.rowEditable) {
7,829✔
6832
            this.repositionRowEditingOverlay(this.crudService.rowInEditMode);
859✔
6833
        }
6834

6835
        if (this.filteringService.isFilterRowVisible) {
7,829✔
6836
            this.filteringRow.resetChipsArea();
155✔
6837
        }
6838

6839
        this.cdr.detectChanges();
7,829✔
6840
        // in case scrollbar has appeared recalc to size correctly.
6841
        if (hasScroll !== this.hasVerticalScroll()) {
7,829✔
6842
            this.calculateGridWidth();
167✔
6843
            this.cdr.detectChanges();
167✔
6844
        }
6845

6846
        // in case horizontal scrollbar has appeared recalc to size correctly.
6847
        if (hasHScroll !== this.hasHorizontalScroll()) {
7,829✔
6848
            this.isHorizontalScrollHidden = !this.hasHorizontalScroll();
2,755✔
6849
            this.cdr.detectChanges();
2,755✔
6850
            this.calculateGridHeight();
2,755✔
6851
            this.cdr.detectChanges();
2,755✔
6852
        }
6853
        if (this.zone.isStable) {
7,829✔
6854
            this.zone.run(() => {
226✔
6855
                this._applyWidthHostBinding();
226✔
6856
                this.cdr.detectChanges();
226✔
6857
            });
6858
        } else {
6859
            this.zone.onStable.pipe(first()).subscribe(() => {
7,603✔
6860
                this.zone.run(() => {
7,603✔
6861
                    this._applyWidthHostBinding();
7,603✔
6862
                });
6863
            });
6864
        }
6865
        this.resetCaches(recalcFeatureWidth);
7,829✔
6866
        if (this.hasColumnsToAutosize) {
7,829✔
6867
            this.cdr.detectChanges();
18✔
6868
            this.zone.onStable.pipe(first()).subscribe(() => {
18✔
6869
                this._autoSizeColumnsNotify.next();
18✔
6870
            });
6871
        }
6872
    }
6873

6874
    /**
6875
     * @hidden
6876
     * Sets TBODY height i.e. this.calcHeight
6877
     */
6878
    protected calculateGridHeight() {
6879

6880
        this.calcHeight = this._calculateGridBodyHeight();
10,342✔
6881
        if (this.pinnedRowHeight && this.calcHeight) {
10,342✔
6882
            this.calcHeight -= this.pinnedRowHeight;
107✔
6883
        }
6884
    }
6885

6886
    /**
6887
     * @hidden
6888
     */
6889
    protected getGroupAreaHeight(): number {
6890
        return 0;
2,711✔
6891
    }
6892

6893
    /**
6894
     * @hidden
6895
     */
6896
    protected getComputedHeight(elem) {
6897
        return elem.offsetHeight ? parseFloat(this.document.defaultView.getComputedStyle(elem).getPropertyValue('height')) : 0;
36,740✔
6898
    }
6899
    /**
6900
     * @hidden
6901
     */
6902
    protected getFooterHeight(): number {
6903
        return this.summaryRowHeight || this.getComputedHeight(this.tfoot.nativeElement);
9,544✔
6904
    }
6905
    /**
6906
     * @hidden
6907
     */
6908
    protected getTheadRowHeight(): number {
6909
        // D.P.: Before CSS loads,theadRow computed height will be 'auto'->NaN, so use 0 fallback
6910
        const height = this.getComputedHeight(this.theadRow.nativeElement) || 0;
9,544✔
6911
        return (!this.allowFiltering || (this.allowFiltering && this.filterMode !== FilterMode.quickFilter)) ?
9,544✔
6912
            height - this.getFilterCellHeight() :
6913
            height;
6914
    }
6915

6916
    /**
6917
     * @hidden
6918
     */
6919
    protected getToolbarHeight(): number {
6920
        let toolbarHeight = 0;
9,544✔
6921
        if (this.toolbar.first) {
9,544✔
6922
            toolbarHeight = this.getComputedHeight(this.toolbar.first.nativeElement);
256✔
6923
        }
6924
        return toolbarHeight;
9,544✔
6925
    }
6926

6927
    /**
6928
     * @hidden
6929
     */
6930
    protected getPagingFooterHeight(): number {
6931
        let pagingHeight = 0;
9,544✔
6932
        if (this.footer) {
9,544✔
6933
            const height = this.getComputedHeight(this.footer.nativeElement);
9,544✔
6934
            pagingHeight = this.footer.nativeElement.firstElementChild ?
9,544✔
6935
                height : 0;
6936
        }
6937
        return pagingHeight;
9,544✔
6938
    }
6939

6940
    /**
6941
     * @hidden
6942
     */
6943
    protected getFilterCellHeight(): number {
6944
        const headerGroupNativeEl = (this.headerGroupsList.length !== 0) ?
7,879✔
6945
            this.headerGroupsList[0].nativeElement : null;
6946
        const filterCellNativeEl = (headerGroupNativeEl) ?
7,879✔
6947
            headerGroupNativeEl.querySelector('igx-grid-filtering-cell') as HTMLElement : null;
6948
        return (filterCellNativeEl) ? filterCellNativeEl.offsetHeight : 0;
7,879✔
6949
    }
6950

6951
    /**
6952
     * @hidden
6953
     */
6954
    protected _calculateGridBodyHeight(): number {
6955
        if (!this._height) {
10,342✔
6956
            return null;
798✔
6957
        }
6958
        const actualTheadRow = this.getTheadRowHeight();
9,544✔
6959
        const footerHeight = this.getFooterHeight();
9,544✔
6960
        const toolbarHeight = this.getToolbarHeight();
9,544✔
6961
        const pagingHeight = this.getPagingFooterHeight();
9,544✔
6962
        const groupAreaHeight = this.getGroupAreaHeight();
9,544✔
6963
        const scrHeight = this.getComputedHeight(this.scr.nativeElement);
9,544✔
6964
        const renderedHeight = toolbarHeight + actualTheadRow +
9,544✔
6965
            footerHeight + pagingHeight + groupAreaHeight +
6966
            scrHeight;
6967

6968
        let gridHeight = 0;
9,544✔
6969

6970
        if (this.isPercentHeight) {
9,544✔
6971
            const computed = this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('height');
2,038✔
6972
            const autoSize = this._shouldAutoSize(renderedHeight);
2,038✔
6973
            if (autoSize || computed.indexOf('%') !== -1) {
2,038✔
6974
                const bodyHeight = this.getDataBasedBodyHeight();
853✔
6975
                return bodyHeight > 0 ? bodyHeight : null;
853✔
6976
            }
6977
            gridHeight = parseFloat(computed);
1,185✔
6978
        } else {
6979
            gridHeight = parseInt(this._height, 10);
7,506✔
6980
        }
6981
        const height = Math.abs(gridHeight - renderedHeight);
8,691✔
6982

6983
        if (Math.round(height) === 0 || isNaN(gridHeight)) {
8,691✔
6984
            const bodyHeight = this.defaultTargetBodyHeight;
18✔
6985
            return bodyHeight > 0 ? bodyHeight : null;
18✔
6986
        }
6987
        return height;
8,673✔
6988
    }
6989

6990
    protected checkContainerSizeChange() {
6991
        const parentElement = this.nativeElement.parentElement || (this.nativeElement.getRootNode() as any).host;
98!
6992
        const origHeight = parentElement.offsetHeight;
98✔
6993
        this.nativeElement.style.display = 'none';
98✔
6994
        const height = parentElement.offsetHeight;
98✔
6995
        this.nativeElement.style.display = '';
98✔
6996
        return origHeight !== height;
98✔
6997
    }
6998

6999
    protected _shouldAutoSize(renderedHeight) {
7000
        this.tbody.nativeElement.style.display = 'none';
1,300✔
7001
        const parentElement = this.nativeElement.parentElement || (this.nativeElement.getRootNode() as any).host;
1,300✔
7002
        let res = !parentElement ||
1,300✔
7003
            parentElement.clientHeight === 0 ||
7004
            parentElement.clientHeight === renderedHeight;
7005
        if (parentElement && (res || this._autoSize)) {
1,300✔
7006
            // If grid causes the parent container to extend (for example when container is flex)
7007
            // we should always auto-size since the actual size of the container will continuously change as the grid renders elements.
7008
            this._autoSize = false;
98✔
7009
            res = this.checkContainerSizeChange();
98✔
7010
        }
7011
        this.tbody.nativeElement.style.display = '';
1,300✔
7012
        return res;
1,300✔
7013
    }
7014

7015
    /**
7016
     * @hidden
7017
     * Gets calculated width of the unpinned area
7018
     * @param takeHidden If we should take into account the hidden columns in the pinned area.
7019
     */
7020
    protected getUnpinnedWidth(takeHidden = false) {
23,291✔
7021
        let width = this.isPercentWidth ?
23,291✔
7022
            this.calcWidth :
7023
            parseInt(this.width, 10) || parseInt(this.hostWidth, 10) || this.calcWidth;
13,082✔
7024
        if (this.hasVerticalScroll() && !this.isPercentWidth) {
23,291✔
7025
            width -= this.scrollSize;
4,291✔
7026
        }
7027
        if (!this.isPinningToStart) {
23,291✔
7028
            width -= this.featureColumnsWidth();
62✔
7029
        }
7030

7031
        return width - this.getPinnedWidth(takeHidden);
23,291✔
7032
    }
7033

7034
    /**
7035
     * @hidden
7036
     */
7037
    protected _summaries(fieldName: string, hasSummary: boolean, summaryOperand?: any) {
7038
        const column = this.gridAPI.get_column_by_name(fieldName);
27✔
7039
        if (column) {
27✔
7040
            column.hasSummary = hasSummary;
27✔
7041
            if (summaryOperand) {
27✔
7042
                if (this.rootSummariesEnabled) {
2✔
7043
                    this.summaryService.retriggerRootPipe++;
2✔
7044
                }
7045
                column.summaries = summaryOperand;
2✔
7046
            }
7047
        }
7048
    }
7049

7050
    /**
7051
     * @hidden
7052
     */
7053
    protected _multipleSummaries(expressions: ISummaryExpression[], hasSummary: boolean) {
7054
        expressions.forEach((element) => {
7✔
7055
            this._summaries(element.fieldName, hasSummary, element.customSummary);
9✔
7056
        });
7057
    }
7058
    /**
7059
     * @hidden
7060
     */
7061
    protected _disableMultipleSummaries(expressions) {
7062
        expressions.forEach((column) => {
5✔
7063
            const columnName = column && column.fieldName ? column.fieldName : column;
13✔
7064
            this._summaries(columnName, false);
13✔
7065
        });
7066
    }
7067

7068
    /**
7069
     * @hidden
7070
     */
7071
    public resolveDataTypes(rec) {
7072
        if (typeof rec === 'number') {
6,445✔
7073
            return GridColumnDataType.Number;
3,802✔
7074
        } else if (typeof rec === 'boolean') {
2,643✔
7075
            return GridColumnDataType.Boolean;
152✔
7076
        } else if (typeof rec === 'object' && rec instanceof Date) {
2,491✔
7077
            return GridColumnDataType.Date;
143✔
7078
        } else if (typeof rec === 'string' && (/\.(gif|jpe?g|tiff?|png|webp|bmp)$/i).test(rec)) {
2,348✔
7079
            return GridColumnDataType.Image;
1✔
7080
        }
7081
        return GridColumnDataType.String;
2,347✔
7082
    }
7083

7084
    /**
7085
     * @hidden
7086
     */
7087
    protected autogenerateColumns() {
7088
        const data = this.gridAPI.get_data();
663✔
7089
        const fields = this.generateDataFields(data);
663✔
7090
        const columns = [];
663✔
7091

7092
        fields.forEach((field) => {
663✔
7093
            const ref = createComponent(IgxColumnComponent, { environmentInjector: this.envInjector, elementInjector: this.injector });
4,280✔
7094
            ref.instance.field = field;
4,280✔
7095
            ref.instance.dataType = this.resolveDataTypes(data[0][field]);
4,280✔
7096
            ref.changeDetectorRef.detectChanges();
4,280✔
7097
            columns.push(ref.instance);
4,280✔
7098
        });
7099
        this._autoGeneratedCols = columns;
663✔
7100

7101
        this.updateColumns(columns);
663✔
7102
        this.columnsAutogenerated.emit({ columns: this._autoGeneratedCols });
663✔
7103
    }
7104

7105
    protected generateDataFields(data: any[]): string[] {
7106
        return Object.keys(data && data.length !== 0 ? data[0] : [])
669✔
7107
            .filter(key => !this.autoGenerateExclude.includes(key));
4,557✔
7108
    }
7109

7110
    /**
7111
     * @hidden
7112
     */
7113
    protected initColumns(collection: IgxColumnComponent[], cb: (args: any) => void = null) {
×
7114
        this._columnGroups = collection.some(col => col.columnGroup);
17,512✔
7115
        if (this.hasColumnLayouts) {
3,611✔
7116
            // Set overall row layout size
7117
            collection.forEach((col) => {
146✔
7118
                if (col.columnLayout) {
1,391✔
7119
                    const layoutSize = col.children ?
289!
7120
                        col.children.reduce((acc, val) => Math.max(val.rowStart + val.gridRowSpan - 1, acc), 1) :
1,092✔
7121
                        1;
7122
                    this._multiRowLayoutRowSize = Math.max(layoutSize, this._multiRowLayoutRowSize);
289✔
7123
                }
7124
            });
7125
        }
7126
        if (this.hasColumnLayouts && this.hasColumnGroups) {
3,611✔
7127
            // invalid configuration - multi-row and column groups
7128
            // remove column groups
7129
            const columnLayoutColumns = collection.filter((col) => col.columnLayout || col.columnLayoutChild);
1,391✔
7130
            collection = columnLayoutColumns;
146✔
7131
        }
7132
        this._maxLevelHeaderDepth = null;
3,611✔
7133
        collection.forEach((column: IgxColumnComponent) => {
3,611✔
7134
            column.defaultWidth = this.columnWidthSetByUser ? this._columnWidth : column.defaultWidth ? column.defaultWidth : '';
23,521✔
7135

7136
            if (cb) {
23,521✔
7137
                cb(column);
23,521✔
7138
            }
7139
        });
7140

7141
        this.updateColumns(collection);
3,611✔
7142

7143
        if (this.hasColumnLayouts) {
3,611✔
7144
            collection.forEach((column: IgxColumnComponent) => {
146✔
7145
                column.populateVisibleIndexes();
1,381✔
7146
            });
7147
        }
7148
    }
7149

7150
    /**
7151
     * @hidden
7152
     */
7153
    protected reinitPinStates() {
7154
        this._pinnedColumns = this._columns
191✔
7155
            .filter((c) => c.pinned).sort((a, b) => this._pinnedColumns.indexOf(a) - this._pinnedColumns.indexOf(b));
2,409✔
7156
        this._unpinnedColumns = this.hasColumnGroups ? this._columns.filter((c) => !c.pinned) :
2,109✔
7157
            this._columns.filter((c) => !c.pinned)
300✔
7158
                .sort((a, b) => this._unpinnedColumns.indexOf(a) - this._unpinnedColumns.indexOf(b));
214✔
7159
    }
7160

7161
    protected extractDataFromSelection(source: any[], formatters = false, headers = false, columnData?: any[]): any[] {
×
7162
        let columnsArray: IgxColumnComponent[];
7163
        let record = {};
246✔
7164
        let selectedData = [];
246✔
7165
        let keys = [];
246✔
7166
        const selectionCollection = new Map();
246✔
7167
        const keysAndData = [];
246✔
7168
        const activeEl = this.selectionService.activeElement;
246✔
7169

7170
        if (this.type === 'hierarchical') {
246✔
7171
            const expansionRowIndexes = [];
2✔
7172
            for (const [key, value] of this.expansionStates.entries()) {
2✔
7173
                if (value) {
×
7174
                    const rowIndex = this.gridAPI.get_rec_index_by_id(key, this.dataView);
×
7175
                    expansionRowIndexes.push(rowIndex);
×
7176
                }
7177
            }
7178
            if (this.selectionService.selection.size > 0) {
2!
7179
                if (expansionRowIndexes.length > 0) {
2!
7180
                    for (const [key, value] of this.selectionService.selection.entries()) {
×
7181
                        const updatedKey = key;
×
7182
                        let subtract = 0;
×
7183
                        expansionRowIndexes.forEach((row) => {
×
7184
                            if (updatedKey > Number(row)) {
×
7185
                                subtract++;
×
7186
                            }
7187
                        });
7188
                        selectionCollection.set(updatedKey - subtract, value);
×
7189
                    }
7190
                }
7191
            } else if (activeEl) {
×
7192
                let subtract = 0;
×
7193
                if (expansionRowIndexes.length > 0) {
×
7194
                    expansionRowIndexes.forEach(row => {
×
7195
                        if (activeEl.row > Number(row)) {
×
7196
                            subtract++;
×
7197
                        }
7198
                    });
7199
                    activeEl.row -= subtract;
×
7200
                }
7201
            }
7202
        }
7203

7204
        const totalItems = (this as any).totalItemCount ?? 0;
246✔
7205
        const isRemote = totalItems && totalItems > this.dataView.length;
246!
7206
        let selectionMap;
7207
        if (this.type === 'hierarchical' && selectionCollection.size > 0) {
246!
7208
            selectionMap = isRemote ? Array.from(selectionCollection) :
×
7209
                Array.from(selectionCollection).filter((tuple) => tuple[0] < source.length);
×
7210
        } else {
7211
            selectionMap = isRemote ? Array.from(this.selectionService.selection) :
246!
7212
                Array.from(this.selectionService.selection).filter((tuple) => tuple[0] < source.length);
883✔
7213
        }
7214

7215
        if (this.cellSelection === GridSelectionMode.single && activeEl) {
246✔
7216
            selectionMap.push([activeEl.row, new Set<number>().add(activeEl.column)]);
10✔
7217
        }
7218

7219
        if (this.cellSelection === GridSelectionMode.none && activeEl) {
246✔
7220
            selectionMap.push([activeEl.row, new Set<number>().add(activeEl.column)]);
5✔
7221
        }
7222

7223
        if (columnData) {
246!
7224
            selectedData = columnData;
×
7225
        }
7226

7227
        // eslint-disable-next-line prefer-const
7228
        for (let [row, set] of selectionMap) {
246✔
7229
            row = this.paginator && (this.pagingMode === GridPagingMode.Local && source === this.filteredSortedData) ? row + (this.perPage * this.page) : row;
788✔
7230
            row = isRemote ? row - this.virtualizationState.startIndex : row;
788!
7231
            if (!source[row] || source[row].detailsData !== undefined) {
788✔
7232
                continue;
40✔
7233
            }
7234
            const temp = Array.from(set);
748✔
7235
            for (const each of temp) {
748✔
7236
                columnsArray = this.getSelectableColumnsAt(each);
2,512✔
7237
                columnsArray.forEach((col) => {
2,512✔
7238
                    if (col) {
2,518✔
7239
                        const key = this.type !== 'pivot' && headers ? col.header || col.field : col.field;
2,316!
7240
                        const rowData = source[row].ghostRecord ? source[row].recordRef : source[row];
2,316!
7241
                        const value = this.type === 'pivot' ? rowData.aggregationValues.get(col.field)
2,316!
7242
                            : resolveNestedPath(rowData, col.field);
7243
                        record[key] = formatters && col.formatter ? col.formatter(value, rowData) : value;
2,316✔
7244
                        if (columnData) {
2,316!
7245
                            if (!record[key]) {
×
7246
                                record[key] = '';
×
7247
                            }
7248
                            record[key] = record[key].toString().concat('recordRow-' + row);
×
7249
                        }
7250
                    }
7251
                });
7252
            }
7253
            if (Object.keys(record).length) {
748✔
7254
                if (columnData) {
746!
7255
                    if (!keys.length) {
×
7256
                        keys = Object.keys(columnData[0]);
×
7257
                    }
7258
                    for (const [key, value] of Object.entries(record)) {
×
7259
                        if (!keys.includes(key)) {
×
7260
                            keys.push(key);
×
7261
                        }
7262
                        let c: any = value;
×
7263
                        const rowNumber = +c.split('recordRow-')[1];
×
7264
                        c = c.split('recordRow-')[0];
×
7265
                        record[key] = c;
×
7266
                        const mergedObj = Object.assign(selectedData[rowNumber], record);
×
7267
                        selectedData[rowNumber] = mergedObj;
×
7268
                    }
7269
                } else {
7270
                    selectedData.push(record);
746✔
7271
                }
7272
            }
7273
            record = {};
748✔
7274
        }
7275

7276
        if (keys.length) {
246!
7277
            keysAndData.push(selectedData);
×
7278
            keysAndData.push(keys);
×
7279
            return keysAndData;
×
7280
        } else {
7281
            return selectedData;
246✔
7282
        }
7283
    }
7284

7285
    protected getSelectableColumnsAt(index) {
7286
        if (this.hasColumnLayouts) {
2,512✔
7287
            const visibleLayoutColumns = this.visibleColumns
2✔
7288
                .filter(col => col.columnLayout)
20✔
7289
                .sort((a, b) => a.visibleIndex - b.visibleIndex);
2✔
7290
            const colLayout = visibleLayoutColumns[index];
2✔
7291
            return colLayout ? colLayout.children.toArray() : [];
2!
7292
        } else {
7293
            const visibleColumns = this.visibleColumns
2,510✔
7294
                .filter(col => !col.columnGroup)
14,120✔
7295
                .sort((a, b) => a.visibleIndex - b.visibleIndex);
11,790✔
7296
            return [visibleColumns[index]];
2,510✔
7297
        }
7298
    }
7299

7300
    protected autoSizeColumnsInView() {
7301
        if (!this.hasColumnsToAutosize) return;
359✔
7302
        const vState = this.headerContainer.state;
26✔
7303
        let colResized = false;
26✔
7304
        const unpinnedInView = this.headerContainer.igxGridForOf.slice(vState.startIndex, vState.startIndex + vState.chunkSize).flatMap(x => x.columnGroup ? x.allChildren : x);
108✔
7305
        const columnsInView = this.pinnedColumns.concat(unpinnedInView as IgxColumnComponent[]);
26✔
7306
        for (const col of columnsInView) {
26✔
7307
            if (!col.autoSize && col.headerCell) {
111✔
7308
                const cellsContentWidths = [];
76✔
7309
                if (col._cells.length !== this.rowList.length) {
76!
7310
                    this.rowList.forEach(x => x.cdr.detectChanges());
×
7311
                }
7312
                const cells = this._dataRowList.map(x => x.cells.find(c => c.column === col));
2,297✔
7313
                cells.forEach((cell) => cellsContentWidths.push(cell?.nativeElement?.offsetWidth || 0));
566!
7314
                let maxForCells = Math.max(...cellsContentWidths);
76✔
7315
                const header = this.headerCellList.find(x => x.column === col);
296✔
7316
                cellsContentWidths.push(header.nativeElement.offsetWidth);
76✔
7317
                const max = Math.max(...cellsContentWidths);
76✔
7318
                // in cases with template contains something, like a webcomponent,
7319
                // that renders fully only after it is already injected in the DOM,
7320
                // and initially renders as empty, skip measuring it.
7321
                let emptyCellWithPaddingOnly = 0;
76✔
7322
                if (cells.length > 0 && !!col.bodyTemplate) {
76✔
7323
                    const cellStyle = this.document.defaultView.getComputedStyle(cells[0].nativeElement);
1✔
7324
                    emptyCellWithPaddingOnly = parseFloat(cellStyle.paddingLeft) + parseFloat(cellStyle.paddingRight);
1✔
7325
                } else {
7326
                    maxForCells = max;
75✔
7327
                }
7328

7329
                if (max === 0 || (maxForCells <= emptyCellWithPaddingOnly && this._firstAutoResize)) {
76!
7330
                    // cells not in DOM yet or content not fully initialized.
7331
                    continue;
6✔
7332
                }
7333
                let maxSize = Math.ceil(Math.max(...cellsContentWidths)) + 1;
70✔
7334
                if (col.maxWidth && maxSize > col.maxWidthPx) {
70✔
7335
                    maxSize = col.maxWidthPx;
3✔
7336
                } else if (maxSize < col.minWidthPx) {
67✔
7337
                    maxSize = col.minWidthPx;
6✔
7338
                }
7339
                col.autoSize = maxSize;
70✔
7340
                col.resetCaches();
70✔
7341
                colResized = true;
70✔
7342
            }
7343
        }
7344
        if (colResized) {
26✔
7345
            this.resetCachedWidths();
18✔
7346
            this.cdr.detectChanges();
18✔
7347
        }
7348

7349
        if (this.isColumnWidthSum) {
26✔
7350
            this.calcWidth = this.getColumnWidthSum();
2✔
7351
        }
7352
    }
7353

7354
    protected extractDataFromColumnsSelection(source: any[], formatters = false, headers = false): any[] {
×
7355
        let record = {};
20✔
7356
        const selectedData = [];
20✔
7357
        const selectedColumns = this.selectedColumns();
20✔
7358
        if (selectedColumns.length === 0) {
20✔
7359
            return [];
12✔
7360
        }
7361

7362
        for (const data of source) {
8✔
7363
            selectedColumns.forEach((col) => {
71✔
7364
                const key = headers ? col.header || col.field : col.field;
142!
7365
                record[key] = formatters && col.formatter ? col.formatter(data[col.field], data)
142!
7366
                    : data[col.field];
7367
            });
7368

7369
            if (Object.keys(record).length) {
71✔
7370
                selectedData.push(record);
71✔
7371
            }
7372
            record = {};
71✔
7373
        }
7374
        return selectedData;
8✔
7375
    }
7376

7377
    /**
7378
     * @hidden
7379
     */
7380
    protected initPinning() {
7381
        this.calculateGridWidth();
3,911✔
7382
        this.resetCaches();
3,911✔
7383
        this.handleColumnPinningForGroups();
3,911✔
7384
        this.notifyChanges();
3,911✔
7385
    }
7386

7387
    /**
7388
     * @hidden
7389
     */
7390
    protected scrollTo(row: any | number, column: any | number, inCollection = this._filteredSortedUnpinnedData): void {
3✔
7391
        let delayScrolling = false;
106✔
7392

7393
        if (this.paginator && typeof (row) !== 'number') {
106✔
7394
            const rowIndex = inCollection.indexOf(row);
16✔
7395
            const page = Math.floor(rowIndex / this.perPage);
16✔
7396

7397
            if (this.page !== page) {
16✔
7398
                delayScrolling = true;
5✔
7399
                this.page = page;
5✔
7400
            }
7401
        }
7402

7403
        if (delayScrolling) {
106✔
7404
            this.verticalScrollContainer.dataChanged.pipe(first()).subscribe(() => {
5✔
7405
                this.scrollDirective(this.verticalScrollContainer,
5✔
7406
                    typeof (row) === 'number' ? row : this.unpinnedDataView.indexOf(row));
5!
7407
            });
7408
        } else {
7409
            this.scrollDirective(this.verticalScrollContainer,
101✔
7410
                typeof (row) === 'number' ? row : this.unpinnedDataView.indexOf(row));
101✔
7411
        }
7412

7413
        this.scrollToHorizontally(column);
106✔
7414
    }
7415

7416
    /**
7417
     * @hidden
7418
     */
7419
    protected scrollToHorizontally(column: any | number) {
7420
        let columnIndex = typeof column === 'number' ? column : this.getColumnByName(column).visibleIndex;
191✔
7421
        const scrollRow = this.rowList.find(r => !!r.virtDirRow);
233✔
7422
        const virtDir = scrollRow ? scrollRow.virtDirRow : null;
191✔
7423
        if (this.isPinningToStart && this.pinnedColumns.length) {
191✔
7424
            if (columnIndex >= this.pinnedColumns.length) {
1!
7425
                columnIndex -= this.pinnedColumns.length;
×
7426
                this.scrollDirective(virtDir, columnIndex);
×
7427
            }
7428
        } else {
7429
            this.scrollDirective(virtDir, columnIndex);
190✔
7430
        }
7431
    }
7432

7433
    /**
7434
     * @hidden
7435
     */
7436
    protected scrollDirective(directive: IgxGridForOfDirective<any, any[]>, goal: number): void {
7437
        if (!directive) {
381✔
7438
            return;
1✔
7439
        }
7440
        directive.scrollTo(goal);
380✔
7441
    }
7442

7443

7444
    /**
7445
     * @hidden
7446
     */
7447
    protected getColumnWidthSum(): number {
7448
        let colSum = 0;
43✔
7449
        const cols = this.hasColumnLayouts ?
43!
7450
            this.visibleColumns.filter(x => x.columnLayout) : this.visibleColumns.filter(x => !x.columnGroup);
271✔
7451
        cols.forEach((item) => {
43✔
7452
            colSum += parseInt((item.calcWidth || item.defaultWidth), 10) || this.minColumnWidth;
238!
7453
        });
7454
        if (!colSum) {
43!
7455
            return null;
×
7456
        }
7457
        this.cdr.detectChanges();
43✔
7458
        colSum += this.featureColumnsWidth();
43✔
7459
        return colSum;
43✔
7460
    }
7461

7462
    /**
7463
     * Notify changes, reset cache and populateVisibleIndexes.
7464
     *
7465
     * @hidden
7466
     */
7467
    private _columnsReordered(column: IgxColumnComponent) {
7468
        this.notifyChanges();
128✔
7469
        // after reordering is done reset cached column collections.
7470
        this.resetColumnCollections();
128✔
7471
        column.resetCaches();
128✔
7472
    }
7473

7474
    protected buildDataView(_data: any[]) {
7475
        this._dataView = this.isRowPinningToTop ?
13,593✔
7476
            [...this.pinnedDataView, ...this.unpinnedDataView] :
7477
            [...this.unpinnedDataView, ...this.pinnedDataView];
7478
    }
7479

7480
    private _applyWidthHostBinding() {
7481
        let width = this._width;
7,829✔
7482
        if (width === null) {
7,829✔
7483
            let currentWidth = this.calcWidth;
2✔
7484
            if (this.hasVerticalScroll()) {
2!
7485
                currentWidth += this.scrollSize;
×
7486
            }
7487
            width = currentWidth + 'px';
2✔
7488
            this.resetCaches();
2✔
7489
        }
7490
        this._hostWidth = width;
7,829✔
7491
        this.cdr.markForCheck();
7,829✔
7492
    }
7493

7494
    protected verticalScrollHandler(event) {
7495
        this.verticalScrollContainer.onScroll(event);
326✔
7496
        this.disableTransitions = true;
326✔
7497

7498
        this.zone.run(() => {
326✔
7499
            this.zone.onStable.pipe(first()).subscribe(() => {
326✔
7500
                this.verticalScrollContainer.chunkLoad.emit(this.verticalScrollContainer.state);
326✔
7501
                if (this.rowEditable) {
326✔
7502
                    this.changeRowEditingOverlayStateOnScroll(this.crudService.rowInEditMode);
16✔
7503
                }
7504
            });
7505
        });
7506
        this.disableTransitions = false;
326✔
7507

7508
        this.hideOverlays();
326✔
7509
        this.actionStrip?.hide();
326✔
7510
        if (this.actionStrip) {
326✔
7511
            this.actionStrip.context = null;
11✔
7512
        }
7513
        const args: IGridScrollEventArgs = {
326✔
7514
            direction: 'vertical',
7515
            event,
7516
            scrollPosition: this.verticalScrollContainer.scrollPosition
7517
        };
7518
        this.gridScroll.emit(args);
326✔
7519
    }
7520

7521
    protected horizontalScrollHandler(event) {
7522
        const scrollLeft = event.target.scrollLeft;
313✔
7523
        this.headerContainer.onHScroll(scrollLeft);
313✔
7524
        this._horizontalForOfs.forEach(vfor => vfor.onHScroll(scrollLeft));
1,976✔
7525
        this.cdr.markForCheck();
313✔
7526

7527
        this.zone.run(() => {
313✔
7528
            this.zone.onStable.pipe(first()).subscribe(() => {
313✔
7529
                this.parentVirtDir.chunkLoad.emit(this.headerContainer.state);
311✔
7530
                requestAnimationFrame(() => {
311✔
7531
                    this.autoSizeColumnsInView();
311✔
7532
                });
7533
            });
7534
        });
7535
        if (!this.navigation.isColumnFullyVisible(this.navigation.lastColumnIndex)) {
313✔
7536
            this.hideOverlays();
206✔
7537
        }
7538
        const args: IGridScrollEventArgs = { direction: 'horizontal', event, scrollPosition: this.headerContainer.scrollPosition };
313✔
7539
        this.gridScroll.emit(args);
313✔
7540
    }
7541

7542
    protected get renderedActualRowHeight() {
7543
        let border = 1;
502✔
7544
        if (this.rowList.toArray().length > 0) {
502✔
7545
            const rowStyles = this.document.defaultView.getComputedStyle(this.rowList.first.nativeElement);
352✔
7546
            border = rowStyles.borderBottomWidth ? Math.ceil(parseFloat(rowStyles.borderBottomWidth)) : border;
352✔
7547
        }
7548
        return this.rowHeight + border;
502✔
7549
    }
7550

7551
    private executeCallback(rowIndex, visibleColIndex = -1, cb: (args: any) => void = null) {
×
7552
        if (!cb) {
832✔
7553
            return;
281✔
7554
        }
7555
        let row = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).find(r => r.index === rowIndex);
1,622✔
7556
        if (!row) {
551✔
7557
            if ((this as any).totalItemCount) {
8!
7558
                this.verticalScrollContainer.dataChanged.pipe(first()).subscribe(() => {
×
7559
                    this.cdr.detectChanges();
×
7560
                    row = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).find(r => r.index === rowIndex);
×
7561
                    const cbArgs = this.getNavigationArguments(row, visibleColIndex);
×
7562
                    cb(cbArgs);
×
7563
                });
7564
            }
7565
            const dataViewIndex = this._getDataViewIndex(rowIndex);
8✔
7566
            if (this.dataView[dataViewIndex].detailsData) {
8✔
7567
                this.navigation.setActiveNode({ row: rowIndex });
8✔
7568
                this.cdr.detectChanges();
8✔
7569
            }
7570

7571
            return;
8✔
7572
        }
7573
        const args = this.getNavigationArguments(row, visibleColIndex);
543✔
7574
        cb(args);
543✔
7575
    }
7576

7577
    private getNavigationArguments(row, visibleColIndex) {
7578
        let targetType: GridKeydownTargetType; let target;
7579
        switch (row.nativeElement.tagName.toLowerCase()) {
543!
7580
            case 'igx-grid-groupby-row':
7581
                targetType = 'groupRow';
30✔
7582
                target = row;
30✔
7583
                break;
30✔
7584
            case 'igx-grid-summary-row':
7585
                targetType = 'summaryCell';
50✔
7586
                target = visibleColIndex !== -1 ?
50!
7587
                    row.summaryCells.find(c => c.visibleColumnIndex === visibleColIndex) : row.summaryCells.first;
139✔
7588
                break;
50✔
7589
            case 'igx-child-grid-row':
7590
                targetType = 'hierarchicalRow';
×
7591
                target = row;
×
7592
                break;
×
7593
            default:
7594
                targetType = 'dataCell';
463✔
7595
                target = visibleColIndex !== -1 ? row.cells.find(c => c.visibleColumnIndex === visibleColIndex) : row.cells.first;
1,286!
7596
                break;
463✔
7597
        }
7598
        return { targetType, target };
543✔
7599
    }
7600

7601
    private getNextDataRowIndex(currentRowIndex, previous = false): number {
15✔
7602
        const resolvedIndex = this._getDataViewIndex(currentRowIndex);
30✔
7603
        if (currentRowIndex < 0 || (currentRowIndex === 0 && previous) || (resolvedIndex >= this.dataView.length - 1 && !previous)) {
30!
7604
            return currentRowIndex;
7✔
7605
        }
7606
        // find next/prev record that is editable.
7607
        const nextRowIndex = previous ? this.findPrevEditableDataRowIndex(currentRowIndex) :
23✔
7608
            this.dataView.findIndex((rec, index) =>
7609
                index > resolvedIndex && this.isEditableDataRecordAtIndex(index));
56✔
7610
        const nextDataIndex = this.getDataIndex(nextRowIndex);
23✔
7611
        return nextDataIndex !== -1 ? nextDataIndex : currentRowIndex;
23!
7612
    }
7613

7614
    /**
7615
     * Returns the previous editable row index or -1 if no such row is found.
7616
     *
7617
     * @param currentIndex The index of the current editable record.
7618
     */
7619
    private findPrevEditableDataRowIndex(currentIndex): number {
7620
        let i = this.dataView.length;
8✔
7621
        const resolvedIndex = this._getDataViewIndex(currentIndex);
8✔
7622
        while (i--) {
8✔
7623
            if (i < resolvedIndex && this.isEditableDataRecordAtIndex(i)) {
178✔
7624
                return i;
8✔
7625
            }
7626
        }
7627
        return -1;
×
7628
    }
7629

7630

7631
    /**
7632
     * Returns if the record at the specified data view index is a an editable data record.
7633
     * If record is group rec, summary rec, child rec, ghost rec. etc. it is not editable.
7634
     *
7635
     * @param dataViewIndex The index of that record in the data view.
7636
     *
7637
     */
7638
    // TODO: Consider moving it into CRUD
7639
    private isEditableDataRecordAtIndex(dataViewIndex) {
7640
        const rec = this.dataView[dataViewIndex];
29✔
7641
        return !rec.expression && !rec.summaries && !rec.childGridsData && !rec.detailsData &&
29✔
7642
            !this.isGhostRecordAtIndex(dataViewIndex);
7643
    }
7644

7645
    /**
7646
     * Returns if the record at the specified data view index is a ghost.
7647
     * If record is pinned but is not in pinned area then it is a ghost record.
7648
     *
7649
     * @param dataViewIndex The index of that record in the data view.
7650
     */
7651
    private isGhostRecordAtIndex(dataViewIndex) {
7652
        const isPinned = this.isRecordPinned(this.dataView[dataViewIndex]);
27✔
7653
        const isInPinnedArea = this.isRecordPinnedByViewIndex(dataViewIndex);
27✔
7654
        return isPinned && !isInPinnedArea;
27✔
7655
    }
7656

7657
    private isValidPosition(rowIndex, colIndex): boolean {
7658
        const rows = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).length;
97✔
7659
        const cols = this._columns.filter(col => !col.columnGroup && col.visibleIndex >= 0 && !col.hidden).length;
777✔
7660
        if (rows < 1 || cols < 1) {
97✔
7661
            return false;
2✔
7662
        }
7663
        if (rowIndex > -1 && rowIndex < this.dataView.length &&
95✔
7664
            colIndex > - 1 && colIndex <= Math.max(...this.visibleColumns.map(c => c.visibleIndex))) {
694✔
7665
            return true;
93✔
7666
        }
7667
        return false;
2✔
7668
    }
7669

7670
    private find(text: string, increment: number, caseSensitive?: boolean, exactMatch?: boolean, scroll?: boolean, endEdit = true) {
195✔
7671
        if (!this.rowList) {
322!
7672
            return 0;
×
7673
        }
7674

7675
        if (endEdit) {
322✔
7676
            this.crudService.endEdit(false);
283✔
7677
        }
7678

7679
        if (!text) {
322!
7680
            this.clearSearch();
×
7681
            return 0;
×
7682
        }
7683

7684
        const caseSensitiveResolved = caseSensitive ? true : false;
322✔
7685
        const exactMatchResolved = exactMatch ? true : false;
322✔
7686
        let rebuildCache = false;
322✔
7687

7688
        if (this._lastSearchInfo.searchText !== text ||
322✔
7689
            this._lastSearchInfo.caseSensitive !== caseSensitiveResolved ||
7690
            this._lastSearchInfo.exactMatch !== exactMatchResolved) {
7691
            this._lastSearchInfo = {
84✔
7692
                searchText: text,
7693
                activeMatchIndex: 0,
7694
                caseSensitive: caseSensitiveResolved,
7695
                exactMatch: exactMatchResolved,
7696
                matchInfoCache: [],
7697
                matchCount: 0,
7698
                content: ''
7699
            };
7700

7701
            rebuildCache = true;
84✔
7702
        } else {
7703
            this._lastSearchInfo.activeMatchIndex += increment;
238✔
7704
        }
7705

7706
        if (rebuildCache) {
322✔
7707
            this.rowList.forEach((row) => {
84✔
7708
                if (row.cells) {
784✔
7709
                    row.cells.forEach((c: IgxGridCellComponent) => {
740✔
7710
                        c.highlightText(text, caseSensitiveResolved, exactMatchResolved);
3,097✔
7711
                    });
7712
                }
7713
            });
7714

7715
            this.rebuildMatchCache();
84✔
7716
        }
7717

7718
        if (this._lastSearchInfo.activeMatchIndex >= this._lastSearchInfo.matchCount) {
322✔
7719
            this._lastSearchInfo.activeMatchIndex = 0;
28✔
7720
        } else if (this._lastSearchInfo.activeMatchIndex < 0) {
294✔
7721
            this._lastSearchInfo.activeMatchIndex = this._lastSearchInfo.matchCount - 1;
8✔
7722
        }
7723

7724
        if (this._lastSearchInfo.matchCount > 0) {
322✔
7725
            const matchInfo = this._lastSearchInfo.matchInfoCache[this._lastSearchInfo.activeMatchIndex];
304✔
7726
            this._lastSearchInfo = { ...this._lastSearchInfo };
304✔
7727

7728
            if (scroll !== false) {
304✔
7729
                this.scrollTo(matchInfo.row, matchInfo.column);
182✔
7730
            }
7731

7732
            this.textHighlightService.setActiveHighlight(this.id, {
304✔
7733
                column: matchInfo.column,
7734
                row: matchInfo.row,
7735
                index: matchInfo.index,
7736
                metadata: matchInfo.metadata,
7737
            });
7738

7739
        } else {
7740
            this.textHighlightService.clearActiveHighlight(this.id);
18✔
7741
        }
7742

7743
        return this._lastSearchInfo.matchCount;
322✔
7744
    }
7745

7746
    private rebuildMatchCache() {
7747
        this._lastSearchInfo.matchInfoCache = [];
211✔
7748

7749
        const caseSensitive = this._lastSearchInfo.caseSensitive;
211✔
7750
        const exactMatch = this._lastSearchInfo.exactMatch;
211✔
7751
        const searchText = caseSensitive ? this._lastSearchInfo.searchText : this._lastSearchInfo.searchText.toLowerCase();
211✔
7752
        const data = this.filteredSortedData;
211✔
7753
        const columnItems = this.visibleColumns.filter((c) => !c.columnGroup).sort((c1, c2) => c1.visibleIndex - c2.visibleIndex);
908✔
7754

7755
        data.forEach((dataRow, rowIndex) => {
211✔
7756
            columnItems.forEach((c) => {
3,474✔
7757
                const pipeArgs = this.getColumnByName(c.field).pipeArgs;
14,791✔
7758
                const value = c.formatter ? c.formatter(resolveNestedPath(dataRow, c.field), dataRow) :
14,791!
7759
                    c.dataType === 'number' ? formatNumber(resolveNestedPath(dataRow, c.field), this.locale, pipeArgs.digitsInfo) :
14,791✔
7760
                        c.dataType === 'date'
11,115✔
7761
                            ? formatDate(resolveNestedPath(dataRow, c.field), pipeArgs.format, this.locale, pipeArgs.timezone)
7762
                            : resolveNestedPath(dataRow, c.field);
7763
                if (value !== undefined && value !== null && c.searchable) {
14,791✔
7764
                    let searchValue = caseSensitive ? String(value) : String(value).toLowerCase();
14,576✔
7765

7766
                    if (exactMatch) {
14,576✔
7767
                        if (searchValue === searchText) {
536✔
7768
                            const mic: IMatchInfoCache = {
9✔
7769
                                row: dataRow,
7770
                                column: c.field,
7771
                                index: 0,
7772
                                metadata: new Map<string, boolean>([['pinned', this.isRecordPinnedByIndex(rowIndex)]])
7773
                            };
7774

7775
                            this._lastSearchInfo.matchInfoCache.push(mic);
9✔
7776
                        }
7777
                    } else {
7778
                        let occurrenceIndex = 0;
14,040✔
7779
                        let searchIndex = searchValue.indexOf(searchText);
14,040✔
7780

7781
                        while (searchIndex !== -1) {
14,040✔
7782
                            const mic: IMatchInfoCache = {
2,410✔
7783
                                row: dataRow,
7784
                                column: c.field,
7785
                                index: occurrenceIndex++,
7786
                                metadata: new Map<string, boolean>([['pinned', this.isRecordPinnedByIndex(rowIndex)]])
7787
                            };
7788

7789
                            this._lastSearchInfo.matchInfoCache.push(mic);
2,410✔
7790

7791
                            searchValue = searchValue.substring(searchIndex + searchText.length);
2,410✔
7792
                            searchIndex = searchValue.indexOf(searchText);
2,410✔
7793
                        }
7794
                    }
7795
                }
7796
            });
7797
        });
7798

7799
        this._lastSearchInfo.matchCount = this._lastSearchInfo.matchInfoCache.length;
211✔
7800
    }
7801

7802
    protected updateDefaultRowHeight() {
7803
        if (this.dataRowList.length > 0 && this.dataRowList.first.cells && this.dataRowList.first.cells.length > 0) {
3,641✔
7804
            const height = parseFloat(this.document.defaultView.getComputedStyle(this.dataRowList.first.cells.first.nativeElement)?.getPropertyValue('height'));
112✔
7805
            if (height) {
112!
7806
                this._defaultRowHeight = height;
112✔
7807
            } else {
7808
                this._shouldRecalcRowHeight = true;
×
7809
            }
7810
        }
7811
    }
7812

7813
    // TODO: About to Move to CRUD
7814
    private configureRowEditingOverlay(rowID: any, useOuter = false) {
72✔
7815
        let settings = this.rowEditSettings;
280✔
7816
        const overlay = this.overlayService.getOverlayById(this.rowEditingOverlay.overlayId);
280✔
7817
        if (overlay) {
280✔
7818
            settings = overlay.settings;
73✔
7819
        }
7820
        settings.outlet = useOuter ? this.parentRowOutletDirective : this.rowOutletDirective;
280✔
7821
        this.rowEditPositioningStrategy.settings.container = this.tbody.nativeElement;
280✔
7822
        const pinned = this._pinnedRecordIDs.indexOf(rowID) !== -1;
280✔
7823
        const targetRow = !pinned ?
280✔
7824
            this.gridAPI.get_row_by_key(rowID) as IgxRowDirective
7825
            : this.pinnedRows.find(x => x.key === rowID) as IgxRowDirective;
8✔
7826
        if (!targetRow) {
280!
7827
            return;
×
7828
        }
7829
        settings.target = targetRow.element.nativeElement;
280✔
7830
        this.toggleRowEditingOverlay(true);
280✔
7831
    }
7832

7833
    private handleColumnPinningForGroups(): void {
7834
        // When a column is a group or is inside a group, pin all related.
7835
        const pinnedColumns = [];
3,911✔
7836
        const unpinnedColumns = [];
3,911✔
7837

7838
        this._pinnedColumns.forEach(col => {
3,911✔
7839
            if (col.parent) {
492✔
7840
                col.parent.pinned = true;
113✔
7841
            }
7842
            if (col.columnGroup) {
492✔
7843
                col.children.forEach(child => child.pinned = true);
112✔
7844
            }
7845
        });
7846

7847
        // Make sure we don't exceed unpinned area min width and get pinned and unpinned col collections.
7848
        // We take into account top level columns (top level groups and non groups).
7849
        // If top level is unpinned the pinning handles all children to be unpinned as well.
7850
        for (const column of this._columns) {
3,911✔
7851
            if (column.pinned && !column.parent) {
23,207✔
7852
                pinnedColumns.push(column);
380✔
7853
            } else if (column.pinned && column.parent) {
22,827✔
7854
                if (column.topLevelParent.pinned) {
115!
7855
                    pinnedColumns.push(column);
115✔
7856
                } else {
7857
                    column.pinned = false;
×
7858
                    unpinnedColumns.push(column);
×
7859
                }
7860
            } else {
7861
                unpinnedColumns.push(column);
22,712✔
7862
            }
7863
        }
7864
        // Assign the applicable collections.
7865
        this._pinnedColumns = pinnedColumns;
3,911✔
7866
        this._unpinnedColumns = unpinnedColumns;
3,911✔
7867
    }
7868

7869
    protected shouldRecreateColumns(oldData: any[] | null | undefined, newData: any[] | null | undefined): boolean {
7870
        if (!oldData || !oldData.length) return true;
73✔
7871
        if (!newData || !newData.length) return false;
46!
7872
        return Object.keys(oldData[0]).join() !== Object.keys(newData[0]).join();
46✔
7873
    }
7874

7875
    /**
7876
     * Clears the current navigation service active node
7877
     */
7878
    private clearActiveNode() {
7879
        this.navigation.lastActiveNode = this.navigation.activeNode;
76✔
7880
        this.navigation.activeNode = {} as IActiveNode;
76✔
7881
        this.notifyChanges();
76✔
7882
    }
7883
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc